Merge tag 'v5.16-rc5' into locking/core, to pick up fixes
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
+2
-9
@@ -6346,11 +6346,6 @@ BTF_ID_LIST_GLOBAL_SINGLE(btf_task_struct_ids, struct, task_struct)
|
||||
|
||||
/* BTF ID set registration API for modules */
|
||||
|
||||
struct kfunc_btf_id_list {
|
||||
struct list_head list;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
|
||||
|
||||
void register_kfunc_btf_id_set(struct kfunc_btf_id_list *l,
|
||||
@@ -6376,8 +6371,6 @@ bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id,
|
||||
{
|
||||
struct kfunc_btf_id_set *s;
|
||||
|
||||
if (!owner)
|
||||
return false;
|
||||
mutex_lock(&klist->mutex);
|
||||
list_for_each_entry(s, &klist->list, list) {
|
||||
if (s->owner == owner && btf_id_set_contains(s->set, kfunc_id)) {
|
||||
@@ -6389,8 +6382,6 @@ bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id,
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define DEFINE_KFUNC_BTF_ID_LIST(name) \
|
||||
struct kfunc_btf_id_list name = { LIST_HEAD_INIT(name.list), \
|
||||
__MUTEX_INITIALIZER(name.mutex) }; \
|
||||
@@ -6398,3 +6389,5 @@ bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id,
|
||||
|
||||
DEFINE_KFUNC_BTF_ID_LIST(bpf_tcp_ca_kfunc_list);
|
||||
DEFINE_KFUNC_BTF_ID_LIST(prog_test_kfunc_list);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1809,6 +1809,8 @@ sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_sysctl_get_new_value_proto;
|
||||
case BPF_FUNC_sysctl_set_new_value:
|
||||
return &bpf_sysctl_set_new_value_proto;
|
||||
case BPF_FUNC_ktime_get_coarse_ns:
|
||||
return &bpf_ktime_get_coarse_ns_proto;
|
||||
default:
|
||||
return cgroup_base_func_proto(func_id, prog);
|
||||
}
|
||||
|
||||
@@ -1364,8 +1364,6 @@ bpf_base_func_proto(enum bpf_func_id func_id)
|
||||
return &bpf_ktime_get_ns_proto;
|
||||
case BPF_FUNC_ktime_get_boot_ns:
|
||||
return &bpf_ktime_get_boot_ns_proto;
|
||||
case BPF_FUNC_ktime_get_coarse_ns:
|
||||
return &bpf_ktime_get_coarse_ns_proto;
|
||||
case BPF_FUNC_ringbuf_output:
|
||||
return &bpf_ringbuf_output_proto;
|
||||
case BPF_FUNC_ringbuf_reserve:
|
||||
|
||||
+36
-21
@@ -132,6 +132,21 @@ static struct bpf_map *find_and_alloc_map(union bpf_attr *attr)
|
||||
return map;
|
||||
}
|
||||
|
||||
static void bpf_map_write_active_inc(struct bpf_map *map)
|
||||
{
|
||||
atomic64_inc(&map->writecnt);
|
||||
}
|
||||
|
||||
static void bpf_map_write_active_dec(struct bpf_map *map)
|
||||
{
|
||||
atomic64_dec(&map->writecnt);
|
||||
}
|
||||
|
||||
bool bpf_map_write_active(const struct bpf_map *map)
|
||||
{
|
||||
return atomic64_read(&map->writecnt) != 0;
|
||||
}
|
||||
|
||||
static u32 bpf_map_value_size(const struct bpf_map *map)
|
||||
{
|
||||
if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
|
||||
@@ -601,11 +616,8 @@ static void bpf_map_mmap_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct bpf_map *map = vma->vm_file->private_data;
|
||||
|
||||
if (vma->vm_flags & VM_MAYWRITE) {
|
||||
mutex_lock(&map->freeze_mutex);
|
||||
map->writecnt++;
|
||||
mutex_unlock(&map->freeze_mutex);
|
||||
}
|
||||
if (vma->vm_flags & VM_MAYWRITE)
|
||||
bpf_map_write_active_inc(map);
|
||||
}
|
||||
|
||||
/* called for all unmapped memory region (including initial) */
|
||||
@@ -613,11 +625,8 @@ static void bpf_map_mmap_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct bpf_map *map = vma->vm_file->private_data;
|
||||
|
||||
if (vma->vm_flags & VM_MAYWRITE) {
|
||||
mutex_lock(&map->freeze_mutex);
|
||||
map->writecnt--;
|
||||
mutex_unlock(&map->freeze_mutex);
|
||||
}
|
||||
if (vma->vm_flags & VM_MAYWRITE)
|
||||
bpf_map_write_active_dec(map);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct bpf_map_default_vmops = {
|
||||
@@ -668,7 +677,7 @@ static int bpf_map_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
goto out;
|
||||
|
||||
if (vma->vm_flags & VM_MAYWRITE)
|
||||
map->writecnt++;
|
||||
bpf_map_write_active_inc(map);
|
||||
out:
|
||||
mutex_unlock(&map->freeze_mutex);
|
||||
return err;
|
||||
@@ -1139,6 +1148,7 @@ static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr)
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
bpf_map_write_active_inc(map);
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_WRITE)) {
|
||||
err = -EPERM;
|
||||
goto err_put;
|
||||
@@ -1174,6 +1184,7 @@ free_value:
|
||||
free_key:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
bpf_map_write_active_dec(map);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
@@ -1196,6 +1207,7 @@ static int map_delete_elem(union bpf_attr *attr)
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
bpf_map_write_active_inc(map);
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_WRITE)) {
|
||||
err = -EPERM;
|
||||
goto err_put;
|
||||
@@ -1226,6 +1238,7 @@ static int map_delete_elem(union bpf_attr *attr)
|
||||
out:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
bpf_map_write_active_dec(map);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
@@ -1533,6 +1546,7 @@ static int map_lookup_and_delete_elem(union bpf_attr *attr)
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
bpf_map_write_active_inc(map);
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_READ) ||
|
||||
!(map_get_sys_perms(map, f) & FMODE_CAN_WRITE)) {
|
||||
err = -EPERM;
|
||||
@@ -1597,6 +1611,7 @@ free_value:
|
||||
free_key:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
bpf_map_write_active_dec(map);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
@@ -1624,8 +1639,7 @@ static int map_freeze(const union bpf_attr *attr)
|
||||
}
|
||||
|
||||
mutex_lock(&map->freeze_mutex);
|
||||
|
||||
if (map->writecnt) {
|
||||
if (bpf_map_write_active(map)) {
|
||||
err = -EBUSY;
|
||||
goto err_put;
|
||||
}
|
||||
@@ -4171,6 +4185,9 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr,
|
||||
int cmd)
|
||||
{
|
||||
bool has_read = cmd == BPF_MAP_LOOKUP_BATCH ||
|
||||
cmd == BPF_MAP_LOOKUP_AND_DELETE_BATCH;
|
||||
bool has_write = cmd != BPF_MAP_LOOKUP_BATCH;
|
||||
struct bpf_map *map;
|
||||
int err, ufd;
|
||||
struct fd f;
|
||||
@@ -4183,16 +4200,13 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
|
||||
if ((cmd == BPF_MAP_LOOKUP_BATCH ||
|
||||
cmd == BPF_MAP_LOOKUP_AND_DELETE_BATCH) &&
|
||||
!(map_get_sys_perms(map, f) & FMODE_CAN_READ)) {
|
||||
if (has_write)
|
||||
bpf_map_write_active_inc(map);
|
||||
if (has_read && !(map_get_sys_perms(map, f) & FMODE_CAN_READ)) {
|
||||
err = -EPERM;
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
if (cmd != BPF_MAP_LOOKUP_BATCH &&
|
||||
!(map_get_sys_perms(map, f) & FMODE_CAN_WRITE)) {
|
||||
if (has_write && !(map_get_sys_perms(map, f) & FMODE_CAN_WRITE)) {
|
||||
err = -EPERM;
|
||||
goto err_put;
|
||||
}
|
||||
@@ -4205,8 +4219,9 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
|
||||
BPF_DO_BATCH(map->ops->map_update_batch);
|
||||
else
|
||||
BPF_DO_BATCH(map->ops->map_delete_batch);
|
||||
|
||||
err_put:
|
||||
if (has_write)
|
||||
bpf_map_write_active_dec(map);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
+26
-3
@@ -1151,7 +1151,8 @@ static void mark_ptr_not_null_reg(struct bpf_reg_state *reg)
|
||||
/* transfer reg's id which is unique for every map_lookup_elem
|
||||
* as UID of the inner map.
|
||||
*/
|
||||
reg->map_uid = reg->id;
|
||||
if (map_value_has_timer(map->inner_map_meta))
|
||||
reg->map_uid = reg->id;
|
||||
} else if (map->map_type == BPF_MAP_TYPE_XSKMAP) {
|
||||
reg->type = PTR_TO_XDP_SOCK;
|
||||
} else if (map->map_type == BPF_MAP_TYPE_SOCKMAP ||
|
||||
@@ -4055,7 +4056,22 @@ static void coerce_reg_to_size(struct bpf_reg_state *reg, int size)
|
||||
|
||||
static bool bpf_map_is_rdonly(const struct bpf_map *map)
|
||||
{
|
||||
return (map->map_flags & BPF_F_RDONLY_PROG) && map->frozen;
|
||||
/* A map is considered read-only if the following condition are true:
|
||||
*
|
||||
* 1) BPF program side cannot change any of the map content. The
|
||||
* BPF_F_RDONLY_PROG flag is throughout the lifetime of a map
|
||||
* and was set at map creation time.
|
||||
* 2) The map value(s) have been initialized from user space by a
|
||||
* loader and then "frozen", such that no new map update/delete
|
||||
* operations from syscall side are possible for the rest of
|
||||
* the map's lifetime from that point onwards.
|
||||
* 3) Any parallel/pending map update/delete operations from syscall
|
||||
* side have been completed. Only after that point, it's safe to
|
||||
* assume that map value(s) are immutable.
|
||||
*/
|
||||
return (map->map_flags & BPF_F_RDONLY_PROG) &&
|
||||
READ_ONCE(map->frozen) &&
|
||||
!bpf_map_write_active(map);
|
||||
}
|
||||
|
||||
static int bpf_map_direct_read(struct bpf_map *map, int off, int size, u64 *val)
|
||||
@@ -8406,7 +8422,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *vstate,
|
||||
|
||||
new_range = dst_reg->off;
|
||||
if (range_right_open)
|
||||
new_range--;
|
||||
new_range++;
|
||||
|
||||
/* Examples for register markings:
|
||||
*
|
||||
@@ -11631,6 +11647,13 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
|
||||
}
|
||||
}
|
||||
|
||||
if (map_value_has_timer(map)) {
|
||||
if (is_tracing_prog_type(prog_type)) {
|
||||
verbose(env, "tracing progs cannot use bpf_timer yet\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((bpf_prog_is_dev_bound(prog->aux) || bpf_map_is_dev_bound(map)) &&
|
||||
!bpf_offload_prog_map_match(prog, map)) {
|
||||
verbose(env, "offload device mismatch between prog and map\n");
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <linux/smpboot.h>
|
||||
#include <linux/relay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/scs.h>
|
||||
#include <linux/percpu-rwsem.h>
|
||||
#include <linux/cpuset.h>
|
||||
|
||||
@@ -587,6 +588,12 @@ static int bringup_cpu(unsigned int cpu)
|
||||
struct task_struct *idle = idle_thread_get(cpu);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Reset stale stack state from the last time this CPU was online.
|
||||
*/
|
||||
scs_task_reset(idle);
|
||||
kasan_unpoison_task_stack(idle);
|
||||
|
||||
/*
|
||||
* Some architectures have to walk the irq descriptors to
|
||||
* setup the vector space for the cpu which comes online.
|
||||
|
||||
@@ -48,7 +48,7 @@ bool syscall_user_dispatch(struct pt_regs *regs)
|
||||
* the selector is loaded by userspace.
|
||||
*/
|
||||
if (unlikely(__get_user(state, sd->selector))) {
|
||||
force_fatal_sig(SIGSEGV);
|
||||
force_exit_sig(SIGSEGV);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ bool syscall_user_dispatch(struct pt_regs *regs)
|
||||
return false;
|
||||
|
||||
if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
|
||||
force_fatal_sig(SIGSYS);
|
||||
force_exit_sig(SIGSYS);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9759,6 +9759,9 @@ void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size,
|
||||
continue;
|
||||
if (event->attr.config != entry->type)
|
||||
continue;
|
||||
/* Cannot deliver synchronous signal to other task. */
|
||||
if (event->attr.sigtrap)
|
||||
continue;
|
||||
if (perf_tp_event_match(event, &data, regs))
|
||||
perf_swevent_event(event, count, &data, regs);
|
||||
}
|
||||
|
||||
@@ -2086,6 +2086,9 @@ int register_kretprobe(struct kretprobe *rp)
|
||||
}
|
||||
}
|
||||
|
||||
if (rp->data_size > KRETPROBE_MAX_DATA_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
rp->kp.pre_handler = pre_handler_kretprobe;
|
||||
rp->kp.post_handler = NULL;
|
||||
|
||||
|
||||
+91
-95
@@ -105,9 +105,9 @@
|
||||
* atomic_long_cmpxchg() will be used to obtain writer lock.
|
||||
*
|
||||
* There are three places where the lock handoff bit may be set or cleared.
|
||||
* 1) rwsem_mark_wake() for readers.
|
||||
* 2) rwsem_try_write_lock() for writers.
|
||||
* 3) Error path of rwsem_down_write_slowpath().
|
||||
* 1) rwsem_mark_wake() for readers -- set, clear
|
||||
* 2) rwsem_try_write_lock() for writers -- set, clear
|
||||
* 3) rwsem_del_waiter() -- clear
|
||||
*
|
||||
* For all the above cases, wait_lock will be held. A writer must also
|
||||
* be the first one in the wait_list to be eligible for setting the handoff
|
||||
@@ -334,6 +334,9 @@ struct rwsem_waiter {
|
||||
struct task_struct *task;
|
||||
enum rwsem_waiter_type type;
|
||||
unsigned long timeout;
|
||||
|
||||
/* Writer only, not initialized in reader */
|
||||
bool handoff_set;
|
||||
};
|
||||
#define rwsem_first_waiter(sem) \
|
||||
list_first_entry(&sem->wait_list, struct rwsem_waiter, list)
|
||||
@@ -344,12 +347,6 @@ enum rwsem_wake_type {
|
||||
RWSEM_WAKE_READ_OWNED /* Waker thread holds the read lock */
|
||||
};
|
||||
|
||||
enum writer_wait_state {
|
||||
WRITER_NOT_FIRST, /* Writer is not first in wait list */
|
||||
WRITER_FIRST, /* Writer is first in wait list */
|
||||
WRITER_HANDOFF /* Writer is first & handoff needed */
|
||||
};
|
||||
|
||||
/*
|
||||
* The typical HZ value is either 250 or 1000. So set the minimum waiting
|
||||
* time to at least 4ms or 1 jiffy (if it is higher than 4ms) in the wait
|
||||
@@ -365,6 +362,31 @@ enum writer_wait_state {
|
||||
*/
|
||||
#define MAX_READERS_WAKEUP 0x100
|
||||
|
||||
static inline void
|
||||
rwsem_add_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter)
|
||||
{
|
||||
lockdep_assert_held(&sem->wait_lock);
|
||||
list_add_tail(&waiter->list, &sem->wait_list);
|
||||
/* caller will set RWSEM_FLAG_WAITERS */
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a waiter from the wait_list and clear flags.
|
||||
*
|
||||
* Both rwsem_mark_wake() and rwsem_try_write_lock() contain a full 'copy' of
|
||||
* this function. Modify with care.
|
||||
*/
|
||||
static inline void
|
||||
rwsem_del_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter)
|
||||
{
|
||||
lockdep_assert_held(&sem->wait_lock);
|
||||
list_del(&waiter->list);
|
||||
if (likely(!list_empty(&sem->wait_list)))
|
||||
return;
|
||||
|
||||
atomic_long_andnot(RWSEM_FLAG_HANDOFF | RWSEM_FLAG_WAITERS, &sem->count);
|
||||
}
|
||||
|
||||
/*
|
||||
* handle the lock release when processes blocked on it that can now run
|
||||
* - if we come here from up_xxxx(), then the RWSEM_FLAG_WAITERS bit must
|
||||
@@ -376,6 +398,8 @@ enum writer_wait_state {
|
||||
* preferably when the wait_lock is released
|
||||
* - woken process blocks are discarded from the list after having task zeroed
|
||||
* - writers are only marked woken if downgrading is false
|
||||
*
|
||||
* Implies rwsem_del_waiter() for all woken readers.
|
||||
*/
|
||||
static void rwsem_mark_wake(struct rw_semaphore *sem,
|
||||
enum rwsem_wake_type wake_type,
|
||||
@@ -490,17 +514,24 @@ static void rwsem_mark_wake(struct rw_semaphore *sem,
|
||||
|
||||
adjustment = woken * RWSEM_READER_BIAS - adjustment;
|
||||
lockevent_cond_inc(rwsem_wake_reader, woken);
|
||||
if (list_empty(&sem->wait_list)) {
|
||||
/* hit end of list above */
|
||||
adjustment -= RWSEM_FLAG_WAITERS;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we've woken a reader, we no longer need to force writers
|
||||
* to give up the lock and we can clear HANDOFF.
|
||||
*/
|
||||
if (woken && (atomic_long_read(&sem->count) & RWSEM_FLAG_HANDOFF))
|
||||
adjustment -= RWSEM_FLAG_HANDOFF;
|
||||
oldcount = atomic_long_read(&sem->count);
|
||||
if (list_empty(&sem->wait_list)) {
|
||||
/*
|
||||
* Combined with list_move_tail() above, this implies
|
||||
* rwsem_del_waiter().
|
||||
*/
|
||||
adjustment -= RWSEM_FLAG_WAITERS;
|
||||
if (oldcount & RWSEM_FLAG_HANDOFF)
|
||||
adjustment -= RWSEM_FLAG_HANDOFF;
|
||||
} else if (woken) {
|
||||
/*
|
||||
* When we've woken a reader, we no longer need to force
|
||||
* writers to give up the lock and we can clear HANDOFF.
|
||||
*/
|
||||
if (oldcount & RWSEM_FLAG_HANDOFF)
|
||||
adjustment -= RWSEM_FLAG_HANDOFF;
|
||||
}
|
||||
|
||||
if (adjustment)
|
||||
atomic_long_add(adjustment, &sem->count);
|
||||
@@ -532,12 +563,12 @@ static void rwsem_mark_wake(struct rw_semaphore *sem,
|
||||
* race conditions between checking the rwsem wait list and setting the
|
||||
* sem->count accordingly.
|
||||
*
|
||||
* If wstate is WRITER_HANDOFF, it will make sure that either the handoff
|
||||
* bit is set or the lock is acquired with handoff bit cleared.
|
||||
* Implies rwsem_del_waiter() on success.
|
||||
*/
|
||||
static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,
|
||||
enum writer_wait_state wstate)
|
||||
struct rwsem_waiter *waiter)
|
||||
{
|
||||
bool first = rwsem_first_waiter(sem) == waiter;
|
||||
long count, new;
|
||||
|
||||
lockdep_assert_held(&sem->wait_lock);
|
||||
@@ -546,13 +577,19 @@ static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,
|
||||
do {
|
||||
bool has_handoff = !!(count & RWSEM_FLAG_HANDOFF);
|
||||
|
||||
if (has_handoff && wstate == WRITER_NOT_FIRST)
|
||||
return false;
|
||||
if (has_handoff) {
|
||||
if (!first)
|
||||
return false;
|
||||
|
||||
/* First waiter inherits a previously set handoff bit */
|
||||
waiter->handoff_set = true;
|
||||
}
|
||||
|
||||
new = count;
|
||||
|
||||
if (count & RWSEM_LOCK_MASK) {
|
||||
if (has_handoff || (wstate != WRITER_HANDOFF))
|
||||
if (has_handoff || (!rt_task(waiter->task) &&
|
||||
!time_after(jiffies, waiter->timeout)))
|
||||
return false;
|
||||
|
||||
new |= RWSEM_FLAG_HANDOFF;
|
||||
@@ -569,9 +606,17 @@ static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,
|
||||
* We have either acquired the lock with handoff bit cleared or
|
||||
* set the handoff bit.
|
||||
*/
|
||||
if (new & RWSEM_FLAG_HANDOFF)
|
||||
if (new & RWSEM_FLAG_HANDOFF) {
|
||||
waiter->handoff_set = true;
|
||||
lockevent_inc(rwsem_wlock_handoff);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Have rwsem_try_write_lock() fully imply rwsem_del_waiter() on
|
||||
* success.
|
||||
*/
|
||||
list_del(&waiter->list);
|
||||
rwsem_set_owner(sem);
|
||||
return true;
|
||||
}
|
||||
@@ -947,7 +992,7 @@ queue:
|
||||
}
|
||||
adjustment += RWSEM_FLAG_WAITERS;
|
||||
}
|
||||
list_add_tail(&waiter.list, &sem->wait_list);
|
||||
rwsem_add_waiter(sem, &waiter);
|
||||
|
||||
/* we're now waiting on the lock, but no longer actively locking */
|
||||
count = atomic_long_add_return(adjustment, &sem->count);
|
||||
@@ -993,11 +1038,7 @@ queue:
|
||||
return sem;
|
||||
|
||||
out_nolock:
|
||||
list_del(&waiter.list);
|
||||
if (list_empty(&sem->wait_list)) {
|
||||
atomic_long_andnot(RWSEM_FLAG_WAITERS|RWSEM_FLAG_HANDOFF,
|
||||
&sem->count);
|
||||
}
|
||||
rwsem_del_waiter(sem, &waiter);
|
||||
raw_spin_unlock_irq(&sem->wait_lock);
|
||||
__set_current_state(TASK_RUNNING);
|
||||
lockevent_inc(rwsem_rlock_fail);
|
||||
@@ -1011,9 +1052,7 @@ static struct rw_semaphore *
|
||||
rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
|
||||
{
|
||||
long count;
|
||||
enum writer_wait_state wstate;
|
||||
struct rwsem_waiter waiter;
|
||||
struct rw_semaphore *ret = sem;
|
||||
DEFINE_WAKE_Q(wake_q);
|
||||
|
||||
/* do optimistic spinning and steal lock if possible */
|
||||
@@ -1029,16 +1068,13 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
|
||||
waiter.task = current;
|
||||
waiter.type = RWSEM_WAITING_FOR_WRITE;
|
||||
waiter.timeout = jiffies + RWSEM_WAIT_TIMEOUT;
|
||||
waiter.handoff_set = false;
|
||||
|
||||
raw_spin_lock_irq(&sem->wait_lock);
|
||||
|
||||
/* account for this before adding a new element to the list */
|
||||
wstate = list_empty(&sem->wait_list) ? WRITER_FIRST : WRITER_NOT_FIRST;
|
||||
|
||||
list_add_tail(&waiter.list, &sem->wait_list);
|
||||
rwsem_add_waiter(sem, &waiter);
|
||||
|
||||
/* we're now waiting on the lock */
|
||||
if (wstate == WRITER_NOT_FIRST) {
|
||||
if (rwsem_first_waiter(sem) != &waiter) {
|
||||
count = atomic_long_read(&sem->count);
|
||||
|
||||
/*
|
||||
@@ -1074,13 +1110,16 @@ wait:
|
||||
/* wait until we successfully acquire the lock */
|
||||
set_current_state(state);
|
||||
for (;;) {
|
||||
if (rwsem_try_write_lock(sem, wstate)) {
|
||||
if (rwsem_try_write_lock(sem, &waiter)) {
|
||||
/* rwsem_try_write_lock() implies ACQUIRE on success */
|
||||
break;
|
||||
}
|
||||
|
||||
raw_spin_unlock_irq(&sem->wait_lock);
|
||||
|
||||
if (signal_pending_state(state, current))
|
||||
goto out_nolock;
|
||||
|
||||
/*
|
||||
* After setting the handoff bit and failing to acquire
|
||||
* the lock, attempt to spin on owner to accelerate lock
|
||||
@@ -1089,7 +1128,7 @@ wait:
|
||||
* In this case, we attempt to acquire the lock again
|
||||
* without sleeping.
|
||||
*/
|
||||
if (wstate == WRITER_HANDOFF) {
|
||||
if (waiter.handoff_set) {
|
||||
enum owner_state owner_state;
|
||||
|
||||
preempt_disable();
|
||||
@@ -1100,66 +1139,26 @@ wait:
|
||||
goto trylock_again;
|
||||
}
|
||||
|
||||
/* Block until there are no active lockers. */
|
||||
for (;;) {
|
||||
if (signal_pending_state(state, current))
|
||||
goto out_nolock;
|
||||
|
||||
schedule();
|
||||
lockevent_inc(rwsem_sleep_writer);
|
||||
set_current_state(state);
|
||||
/*
|
||||
* If HANDOFF bit is set, unconditionally do
|
||||
* a trylock.
|
||||
*/
|
||||
if (wstate == WRITER_HANDOFF)
|
||||
break;
|
||||
|
||||
if ((wstate == WRITER_NOT_FIRST) &&
|
||||
(rwsem_first_waiter(sem) == &waiter))
|
||||
wstate = WRITER_FIRST;
|
||||
|
||||
count = atomic_long_read(&sem->count);
|
||||
if (!(count & RWSEM_LOCK_MASK))
|
||||
break;
|
||||
|
||||
/*
|
||||
* The setting of the handoff bit is deferred
|
||||
* until rwsem_try_write_lock() is called.
|
||||
*/
|
||||
if ((wstate == WRITER_FIRST) && (rt_task(current) ||
|
||||
time_after(jiffies, waiter.timeout))) {
|
||||
wstate = WRITER_HANDOFF;
|
||||
lockevent_inc(rwsem_wlock_handoff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
schedule();
|
||||
lockevent_inc(rwsem_sleep_writer);
|
||||
set_current_state(state);
|
||||
trylock_again:
|
||||
raw_spin_lock_irq(&sem->wait_lock);
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
list_del(&waiter.list);
|
||||
raw_spin_unlock_irq(&sem->wait_lock);
|
||||
lockevent_inc(rwsem_wlock);
|
||||
|
||||
return ret;
|
||||
return sem;
|
||||
|
||||
out_nolock:
|
||||
__set_current_state(TASK_RUNNING);
|
||||
raw_spin_lock_irq(&sem->wait_lock);
|
||||
list_del(&waiter.list);
|
||||
|
||||
if (unlikely(wstate == WRITER_HANDOFF))
|
||||
atomic_long_add(-RWSEM_FLAG_HANDOFF, &sem->count);
|
||||
|
||||
if (list_empty(&sem->wait_list))
|
||||
atomic_long_andnot(RWSEM_FLAG_WAITERS, &sem->count);
|
||||
else
|
||||
rwsem_del_waiter(sem, &waiter);
|
||||
if (!list_empty(&sem->wait_list))
|
||||
rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q);
|
||||
raw_spin_unlock_irq(&sem->wait_lock);
|
||||
wake_up_q(&wake_q);
|
||||
lockevent_inc(rwsem_wlock_fail);
|
||||
|
||||
return ERR_PTR(-EINTR);
|
||||
}
|
||||
|
||||
@@ -1240,17 +1239,14 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
|
||||
|
||||
DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);
|
||||
|
||||
/*
|
||||
* Optimize for the case when the rwsem is not locked at all.
|
||||
*/
|
||||
tmp = RWSEM_UNLOCKED_VALUE;
|
||||
do {
|
||||
tmp = atomic_long_read(&sem->count);
|
||||
while (!(tmp & RWSEM_READ_FAILED_MASK)) {
|
||||
if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp,
|
||||
tmp + RWSEM_READER_BIAS)) {
|
||||
tmp + RWSEM_READER_BIAS)) {
|
||||
rwsem_set_reader_owned(sem);
|
||||
return 1;
|
||||
}
|
||||
} while (!(tmp & RWSEM_READ_FAILED_MASK));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -693,7 +693,7 @@ static int load_image_and_restore(void)
|
||||
goto Unlock;
|
||||
|
||||
error = swsusp_read(&flags);
|
||||
swsusp_close(FMODE_READ);
|
||||
swsusp_close(FMODE_READ | FMODE_EXCL);
|
||||
if (!error)
|
||||
error = hibernation_restore(flags & SF_PLATFORM_MODE);
|
||||
|
||||
@@ -983,7 +983,7 @@ static int software_resume(void)
|
||||
/* The snapshot device should not be opened while we're running */
|
||||
if (!hibernate_acquire()) {
|
||||
error = -EBUSY;
|
||||
swsusp_close(FMODE_READ);
|
||||
swsusp_close(FMODE_READ | FMODE_EXCL);
|
||||
goto Unlock;
|
||||
}
|
||||
|
||||
@@ -1018,7 +1018,7 @@ static int software_resume(void)
|
||||
pm_pr_dbg("Hibernation image not present or could not be loaded.\n");
|
||||
return error;
|
||||
Close_Finish:
|
||||
swsusp_close(FMODE_READ);
|
||||
swsusp_close(FMODE_READ | FMODE_EXCL);
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -177,7 +177,7 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf,
|
||||
if (res <= 0)
|
||||
goto unlock;
|
||||
} else {
|
||||
res = PAGE_SIZE - pg_offp;
|
||||
res = PAGE_SIZE;
|
||||
}
|
||||
|
||||
if (!data_of(data->handle)) {
|
||||
|
||||
@@ -3253,6 +3253,11 @@ void defer_console_output(void)
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
void printk_trigger_flush(void)
|
||||
{
|
||||
defer_console_output();
|
||||
}
|
||||
|
||||
int vprintk_deferred(const char *fmt, va_list args)
|
||||
{
|
||||
int r;
|
||||
|
||||
+3
-7
@@ -1918,7 +1918,7 @@ static void __init init_uclamp_rq(struct rq *rq)
|
||||
};
|
||||
}
|
||||
|
||||
rq->uclamp_flags = 0;
|
||||
rq->uclamp_flags = UCLAMP_FLAG_IDLE;
|
||||
}
|
||||
|
||||
static void __init init_uclamp(void)
|
||||
@@ -6620,11 +6620,11 @@ static int __init setup_preempt_mode(char *str)
|
||||
int mode = sched_dynamic_mode(str);
|
||||
if (mode < 0) {
|
||||
pr_warn("Dynamic Preempt: unsupported mode: %s\n", str);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sched_dynamic_update(mode);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("preempt=", setup_preempt_mode);
|
||||
|
||||
@@ -8622,9 +8622,6 @@ void __init init_idle(struct task_struct *idle, int cpu)
|
||||
idle->flags |= PF_IDLE | PF_KTHREAD | PF_NO_SETAFFINITY;
|
||||
kthread_set_per_cpu(idle, cpu);
|
||||
|
||||
scs_task_reset(idle);
|
||||
kasan_unpoison_task_stack(idle);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* It's possible that init_idle() gets called multiple times on a task,
|
||||
@@ -8780,7 +8777,6 @@ void idle_task_exit(void)
|
||||
finish_arch_post_lock_switch();
|
||||
}
|
||||
|
||||
scs_task_reset(current);
|
||||
/* finish_cpu(), as ran on the BP, will clean up the active_mm state */
|
||||
}
|
||||
|
||||
|
||||
@@ -615,7 +615,8 @@ void task_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st)
|
||||
.sum_exec_runtime = p->se.sum_exec_runtime,
|
||||
};
|
||||
|
||||
task_cputime(p, &cputime.utime, &cputime.stime);
|
||||
if (task_cputime(p, &cputime.utime, &cputime.stime))
|
||||
cputime.sum_exec_runtime = task_sched_runtime(p);
|
||||
cputime_adjust(&cputime, &p->prev_cputime, ut, st);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(task_cputime_adjusted);
|
||||
@@ -828,19 +829,21 @@ u64 task_gtime(struct task_struct *t)
|
||||
* add up the pending nohz execution time since the last
|
||||
* cputime snapshot.
|
||||
*/
|
||||
void task_cputime(struct task_struct *t, u64 *utime, u64 *stime)
|
||||
bool task_cputime(struct task_struct *t, u64 *utime, u64 *stime)
|
||||
{
|
||||
struct vtime *vtime = &t->vtime;
|
||||
unsigned int seq;
|
||||
u64 delta;
|
||||
int ret;
|
||||
|
||||
if (!vtime_accounting_enabled()) {
|
||||
*utime = t->utime;
|
||||
*stime = t->stime;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = false;
|
||||
seq = read_seqcount_begin(&vtime->seqcount);
|
||||
|
||||
*utime = t->utime;
|
||||
@@ -850,6 +853,7 @@ void task_cputime(struct task_struct *t, u64 *utime, u64 *stime)
|
||||
if (vtime->state < VTIME_SYS)
|
||||
continue;
|
||||
|
||||
ret = true;
|
||||
delta = vtime_delta(vtime);
|
||||
|
||||
/*
|
||||
@@ -861,6 +865,8 @@ void task_cputime(struct task_struct *t, u64 *utime, u64 *stime)
|
||||
else
|
||||
*utime += vtime->utime + delta;
|
||||
} while (read_seqcount_retry(&vtime->seqcount, seq));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vtime_state_fetch(struct vtime *vtime, int cpu)
|
||||
|
||||
@@ -238,6 +238,13 @@ void __wake_up_sync(struct wait_queue_head *wq_head, unsigned int mode)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal use only */
|
||||
|
||||
void __wake_up_pollfree(struct wait_queue_head *wq_head)
|
||||
{
|
||||
__wake_up(wq_head, TASK_NORMAL, 0, poll_to_key(EPOLLHUP | POLLFREE));
|
||||
/* POLLFREE must have cleared the queue. */
|
||||
WARN_ON_ONCE(waitqueue_active(wq_head));
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: we use "set_current_state()" _after_ the wait-queue add,
|
||||
* because we need a memory barrier there on SMP, so that any
|
||||
|
||||
+29
-7
@@ -1298,6 +1298,12 @@ int do_send_sig_info(int sig, struct kernel_siginfo *info, struct task_struct *p
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum sig_handler {
|
||||
HANDLER_CURRENT, /* If reachable use the current handler */
|
||||
HANDLER_SIG_DFL, /* Always use SIG_DFL handler semantics */
|
||||
HANDLER_EXIT, /* Only visible as the process exit code */
|
||||
};
|
||||
|
||||
/*
|
||||
* Force a signal that the process can't ignore: if necessary
|
||||
* we unblock the signal and change any SIG_IGN to SIG_DFL.
|
||||
@@ -1310,7 +1316,8 @@ int do_send_sig_info(int sig, struct kernel_siginfo *info, struct task_struct *p
|
||||
* that is why we also clear SIGNAL_UNKILLABLE.
|
||||
*/
|
||||
static int
|
||||
force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t, bool sigdfl)
|
||||
force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t,
|
||||
enum sig_handler handler)
|
||||
{
|
||||
unsigned long int flags;
|
||||
int ret, blocked, ignored;
|
||||
@@ -1321,9 +1328,10 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t, bool
|
||||
action = &t->sighand->action[sig-1];
|
||||
ignored = action->sa.sa_handler == SIG_IGN;
|
||||
blocked = sigismember(&t->blocked, sig);
|
||||
if (blocked || ignored || sigdfl) {
|
||||
if (blocked || ignored || (handler != HANDLER_CURRENT)) {
|
||||
action->sa.sa_handler = SIG_DFL;
|
||||
action->sa.sa_flags |= SA_IMMUTABLE;
|
||||
if (handler == HANDLER_EXIT)
|
||||
action->sa.sa_flags |= SA_IMMUTABLE;
|
||||
if (blocked) {
|
||||
sigdelset(&t->blocked, sig);
|
||||
recalc_sigpending_and_wake(t);
|
||||
@@ -1343,7 +1351,7 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t, bool
|
||||
|
||||
int force_sig_info(struct kernel_siginfo *info)
|
||||
{
|
||||
return force_sig_info_to_task(info, current, false);
|
||||
return force_sig_info_to_task(info, current, HANDLER_CURRENT);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1660,7 +1668,20 @@ void force_fatal_sig(int sig)
|
||||
info.si_code = SI_KERNEL;
|
||||
info.si_pid = 0;
|
||||
info.si_uid = 0;
|
||||
force_sig_info_to_task(&info, current, true);
|
||||
force_sig_info_to_task(&info, current, HANDLER_SIG_DFL);
|
||||
}
|
||||
|
||||
void force_exit_sig(int sig)
|
||||
{
|
||||
struct kernel_siginfo info;
|
||||
|
||||
clear_siginfo(&info);
|
||||
info.si_signo = sig;
|
||||
info.si_errno = 0;
|
||||
info.si_code = SI_KERNEL;
|
||||
info.si_pid = 0;
|
||||
info.si_uid = 0;
|
||||
force_sig_info_to_task(&info, current, HANDLER_EXIT);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1693,7 +1714,7 @@ int force_sig_fault_to_task(int sig, int code, void __user *addr
|
||||
info.si_flags = flags;
|
||||
info.si_isr = isr;
|
||||
#endif
|
||||
return force_sig_info_to_task(&info, t, false);
|
||||
return force_sig_info_to_task(&info, t, HANDLER_CURRENT);
|
||||
}
|
||||
|
||||
int force_sig_fault(int sig, int code, void __user *addr
|
||||
@@ -1813,7 +1834,8 @@ int force_sig_seccomp(int syscall, int reason, bool force_coredump)
|
||||
info.si_errno = reason;
|
||||
info.si_arch = syscall_get_arch(current);
|
||||
info.si_syscall = syscall;
|
||||
return force_sig_info_to_task(&info, current, force_coredump);
|
||||
return force_sig_info_to_task(&info, current,
|
||||
force_coredump ? HANDLER_EXIT : HANDLER_CURRENT);
|
||||
}
|
||||
|
||||
/* For the crazy architectures that include trap information in
|
||||
|
||||
+2
-1
@@ -595,7 +595,8 @@ void irq_enter_rcu(void)
|
||||
{
|
||||
__irq_enter_raw();
|
||||
|
||||
if (is_idle_task(current) && (irq_count() == HARDIRQ_OFFSET))
|
||||
if (tick_nohz_full_cpu(smp_processor_id()) ||
|
||||
(is_idle_task(current) && (irq_count() == HARDIRQ_OFFSET)))
|
||||
tick_irq_enter();
|
||||
|
||||
account_hardirq_enter(current);
|
||||
|
||||
@@ -1375,6 +1375,13 @@ static inline void tick_nohz_irq_enter(void)
|
||||
now = ktime_get();
|
||||
if (ts->idle_active)
|
||||
tick_nohz_stop_idle(ts, now);
|
||||
/*
|
||||
* If all CPUs are idle. We may need to update a stale jiffies value.
|
||||
* Note nohz_full is a special case: a timekeeper is guaranteed to stay
|
||||
* alive but it might be busy looping with interrupts disabled in some
|
||||
* rare case (typically stop machine). So we must make sure we have a
|
||||
* last resort.
|
||||
*/
|
||||
if (ts->tick_stopped)
|
||||
tick_nohz_update_jiffies(now);
|
||||
}
|
||||
|
||||
+9
-7
@@ -2054,26 +2054,28 @@ unsigned long msleep_interruptible(unsigned int msecs)
|
||||
EXPORT_SYMBOL(msleep_interruptible);
|
||||
|
||||
/**
|
||||
* usleep_range - Sleep for an approximate time
|
||||
* @min: Minimum time in usecs to sleep
|
||||
* @max: Maximum time in usecs to sleep
|
||||
* usleep_range_state - Sleep for an approximate time in a given state
|
||||
* @min: Minimum time in usecs to sleep
|
||||
* @max: Maximum time in usecs to sleep
|
||||
* @state: State of the current task that will be while sleeping
|
||||
*
|
||||
* In non-atomic context where the exact wakeup time is flexible, use
|
||||
* usleep_range() instead of udelay(). The sleep improves responsiveness
|
||||
* usleep_range_state() instead of udelay(). The sleep improves responsiveness
|
||||
* by avoiding the CPU-hogging busy-wait of udelay(), and the range reduces
|
||||
* power usage by allowing hrtimers to take advantage of an already-
|
||||
* scheduled interrupt instead of scheduling a new one just for this sleep.
|
||||
*/
|
||||
void __sched usleep_range(unsigned long min, unsigned long max)
|
||||
void __sched usleep_range_state(unsigned long min, unsigned long max,
|
||||
unsigned int state)
|
||||
{
|
||||
ktime_t exp = ktime_add_us(ktime_get(), min);
|
||||
u64 delta = (u64)(max - min) * NSEC_PER_USEC;
|
||||
|
||||
for (;;) {
|
||||
__set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
__set_current_state(state);
|
||||
/* Do not return before the requested sleep time has elapsed */
|
||||
if (!schedule_hrtimeout_range(&exp, delta, HRTIMER_MODE_ABS))
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(usleep_range);
|
||||
EXPORT_SYMBOL(usleep_range_state);
|
||||
|
||||
@@ -1111,8 +1111,6 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_ktime_get_ns_proto;
|
||||
case BPF_FUNC_ktime_get_boot_ns:
|
||||
return &bpf_ktime_get_boot_ns_proto;
|
||||
case BPF_FUNC_ktime_get_coarse_ns:
|
||||
return &bpf_ktime_get_coarse_ns_proto;
|
||||
case BPF_FUNC_tail_call:
|
||||
return &bpf_tail_call_proto;
|
||||
case BPF_FUNC_get_current_pid_tgid:
|
||||
|
||||
@@ -5217,6 +5217,7 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
|
||||
{
|
||||
struct ftrace_direct_func *direct;
|
||||
struct ftrace_func_entry *entry;
|
||||
struct ftrace_hash *hash;
|
||||
int ret = -ENODEV;
|
||||
|
||||
mutex_lock(&direct_mutex);
|
||||
@@ -5225,7 +5226,8 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
|
||||
if (!entry)
|
||||
goto out_unlock;
|
||||
|
||||
if (direct_functions->count == 1)
|
||||
hash = direct_ops.func_hash->filter_hash;
|
||||
if (hash->count == 1)
|
||||
unregister_ftrace_function(&direct_ops);
|
||||
|
||||
ret = ftrace_set_filter_ip(&direct_ops, ip, 1, 0);
|
||||
@@ -5540,6 +5542,10 @@ int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
|
||||
err = unregister_ftrace_function(ops);
|
||||
remove_direct_functions_hash(hash, addr);
|
||||
mutex_unlock(&direct_mutex);
|
||||
|
||||
/* cleanup for possible another register call */
|
||||
ops->func = NULL;
|
||||
ops->trampoline = 0;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
|
||||
|
||||
+13
-3
@@ -3812,6 +3812,18 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
iter->fmt[i] = '\0';
|
||||
trace_seq_vprintf(&iter->seq, iter->fmt, ap);
|
||||
|
||||
/*
|
||||
* If iter->seq is full, the above call no longer guarantees
|
||||
* that ap is in sync with fmt processing, and further calls
|
||||
* to va_arg() can return wrong positional arguments.
|
||||
*
|
||||
* Ensure that ap is no longer used in this case.
|
||||
*/
|
||||
if (iter->seq.full) {
|
||||
p = "";
|
||||
break;
|
||||
}
|
||||
|
||||
if (star)
|
||||
len = va_arg(ap, int);
|
||||
|
||||
@@ -6706,9 +6718,7 @@ waitagain:
|
||||
cnt = PAGE_SIZE - 1;
|
||||
|
||||
/* reset all but tr, trace, and overruns */
|
||||
memset(&iter->seq, 0,
|
||||
sizeof(struct trace_iterator) -
|
||||
offsetof(struct trace_iterator, seq));
|
||||
memset_startat(iter, 0, seq);
|
||||
cpumask_clear(iter->started);
|
||||
trace_seq_init(&iter->seq);
|
||||
iter->pos = -1;
|
||||
|
||||
+18
-6
@@ -1366,14 +1366,26 @@ __event_trigger_test_discard(struct trace_event_file *file,
|
||||
if (eflags & EVENT_FILE_FL_TRIGGER_COND)
|
||||
*tt = event_triggers_call(file, buffer, entry, event);
|
||||
|
||||
if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags) ||
|
||||
(unlikely(file->flags & EVENT_FILE_FL_FILTERED) &&
|
||||
!filter_match_preds(file->filter, entry))) {
|
||||
__trace_event_discard_commit(buffer, event);
|
||||
return true;
|
||||
}
|
||||
if (likely(!(file->flags & (EVENT_FILE_FL_SOFT_DISABLED |
|
||||
EVENT_FILE_FL_FILTERED |
|
||||
EVENT_FILE_FL_PID_FILTER))))
|
||||
return false;
|
||||
|
||||
if (file->flags & EVENT_FILE_FL_SOFT_DISABLED)
|
||||
goto discard;
|
||||
|
||||
if (file->flags & EVENT_FILE_FL_FILTERED &&
|
||||
!filter_match_preds(file->filter, entry))
|
||||
goto discard;
|
||||
|
||||
if ((file->flags & EVENT_FILE_FL_PID_FILTER) &&
|
||||
trace_event_ignore_this_pid(file))
|
||||
goto discard;
|
||||
|
||||
return false;
|
||||
discard:
|
||||
__trace_event_discard_commit(buffer, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2678,12 +2678,24 @@ static struct trace_event_file *
|
||||
trace_create_new_event(struct trace_event_call *call,
|
||||
struct trace_array *tr)
|
||||
{
|
||||
struct trace_pid_list *no_pid_list;
|
||||
struct trace_pid_list *pid_list;
|
||||
struct trace_event_file *file;
|
||||
unsigned int first;
|
||||
|
||||
file = kmem_cache_alloc(file_cachep, GFP_TRACE);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
pid_list = rcu_dereference_protected(tr->filtered_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
no_pid_list = rcu_dereference_protected(tr->filtered_no_pids,
|
||||
lockdep_is_held(&event_mutex));
|
||||
|
||||
if (!trace_pid_list_first(pid_list, &first) ||
|
||||
!trace_pid_list_first(no_pid_list, &first))
|
||||
file->flags |= EVENT_FILE_FL_PID_FILTER;
|
||||
|
||||
file->event_call = call;
|
||||
file->tr = tr;
|
||||
atomic_set(&file->sm_ref, 0);
|
||||
|
||||
@@ -2576,28 +2576,27 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
|
||||
/* Split the expression string at the root operator */
|
||||
if (!sep)
|
||||
goto free;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
*sep = '\0';
|
||||
operand1_str = str;
|
||||
str = sep+1;
|
||||
|
||||
/* Binary operator requires both operands */
|
||||
if (*operand1_str == '\0' || *str == '\0')
|
||||
goto free;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
operand_flags = 0;
|
||||
|
||||
/* LHS of string is an expression e.g. a+b in a+b+c */
|
||||
operand1 = parse_expr(hist_data, file, operand1_str, operand_flags, NULL, n_subexprs);
|
||||
if (IS_ERR(operand1)) {
|
||||
ret = PTR_ERR(operand1);
|
||||
operand1 = NULL;
|
||||
goto free;
|
||||
}
|
||||
if (IS_ERR(operand1))
|
||||
return ERR_CAST(operand1);
|
||||
|
||||
if (operand1->flags & HIST_FIELD_FL_STRING) {
|
||||
hist_err(file->tr, HIST_ERR_INVALID_STR_OPERAND, errpos(operand1_str));
|
||||
ret = -EINVAL;
|
||||
goto free;
|
||||
goto free_op1;
|
||||
}
|
||||
|
||||
/* RHS of string is another expression e.g. c in a+b+c */
|
||||
@@ -2605,13 +2604,12 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
operand2 = parse_expr(hist_data, file, str, operand_flags, NULL, n_subexprs);
|
||||
if (IS_ERR(operand2)) {
|
||||
ret = PTR_ERR(operand2);
|
||||
operand2 = NULL;
|
||||
goto free;
|
||||
goto free_op1;
|
||||
}
|
||||
if (operand2->flags & HIST_FIELD_FL_STRING) {
|
||||
hist_err(file->tr, HIST_ERR_INVALID_STR_OPERAND, errpos(str));
|
||||
ret = -EINVAL;
|
||||
goto free;
|
||||
goto free_operands;
|
||||
}
|
||||
|
||||
switch (field_op) {
|
||||
@@ -2629,12 +2627,12 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto free;
|
||||
goto free_operands;
|
||||
}
|
||||
|
||||
ret = check_expr_operands(file->tr, operand1, operand2, &var1, &var2);
|
||||
if (ret)
|
||||
goto free;
|
||||
goto free_operands;
|
||||
|
||||
operand_flags = var1 ? var1->flags : operand1->flags;
|
||||
operand2_flags = var2 ? var2->flags : operand2->flags;
|
||||
@@ -2653,12 +2651,13 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
expr = create_hist_field(hist_data, NULL, flags, var_name);
|
||||
if (!expr) {
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
goto free_operands;
|
||||
}
|
||||
|
||||
operand1->read_once = true;
|
||||
operand2->read_once = true;
|
||||
|
||||
/* The operands are now owned and free'd by 'expr' */
|
||||
expr->operands[0] = operand1;
|
||||
expr->operands[1] = operand2;
|
||||
|
||||
@@ -2669,7 +2668,7 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
if (!divisor) {
|
||||
hist_err(file->tr, HIST_ERR_DIVISION_BY_ZERO, errpos(str));
|
||||
ret = -EDOM;
|
||||
goto free;
|
||||
goto free_expr;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2709,18 +2708,22 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
|
||||
if (!expr->type) {
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
goto free_expr;
|
||||
}
|
||||
|
||||
expr->name = expr_str(expr, 0);
|
||||
}
|
||||
|
||||
return expr;
|
||||
free:
|
||||
destroy_hist_field(operand1, 0);
|
||||
destroy_hist_field(operand2, 0);
|
||||
destroy_hist_field(expr, 0);
|
||||
|
||||
free_operands:
|
||||
destroy_hist_field(operand2, 0);
|
||||
free_op1:
|
||||
destroy_hist_field(operand1, 0);
|
||||
return ERR_PTR(ret);
|
||||
|
||||
free_expr:
|
||||
destroy_hist_field(expr, 0);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
@@ -3026,8 +3029,10 @@ static inline void __update_field_vars(struct tracing_map_elt *elt,
|
||||
if (val->flags & HIST_FIELD_FL_STRING) {
|
||||
char *str = elt_data->field_var_str[j++];
|
||||
char *val_str = (char *)(uintptr_t)var_val;
|
||||
unsigned int size;
|
||||
|
||||
strscpy(str, val_str, val->size);
|
||||
size = min(val->size, STR_VAR_LEN_MAX);
|
||||
strscpy(str, val_str, size);
|
||||
var_val = (u64)(uintptr_t)str;
|
||||
}
|
||||
tracing_map_set_var(elt, var_idx, var_val);
|
||||
@@ -3752,7 +3757,7 @@ static int check_synth_field(struct synth_event *event,
|
||||
|
||||
if (strcmp(field->type, hist_field->type) != 0) {
|
||||
if (field->size != hist_field->size ||
|
||||
field->is_signed != hist_field->is_signed)
|
||||
(!field->is_string && field->is_signed != hist_field->is_signed))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -4914,6 +4919,7 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||
if (hist_field->flags & HIST_FIELD_FL_STRING) {
|
||||
unsigned int str_start, var_str_idx, idx;
|
||||
char *str, *val_str;
|
||||
unsigned int size;
|
||||
|
||||
str_start = hist_data->n_field_var_str +
|
||||
hist_data->n_save_var_str;
|
||||
@@ -4922,7 +4928,9 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||
|
||||
str = elt_data->field_var_str[idx];
|
||||
val_str = (char *)(uintptr_t)hist_val;
|
||||
strscpy(str, val_str, hist_field->size);
|
||||
|
||||
size = min(hist_field->size, STR_VAR_LEN_MAX);
|
||||
strscpy(str, val_str, size);
|
||||
|
||||
hist_val = (u64)(uintptr_t)str;
|
||||
}
|
||||
|
||||
@@ -1237,9 +1237,8 @@ static int __create_synth_event(const char *name, const char *raw_fields)
|
||||
argv + consumed, &consumed,
|
||||
&field_version);
|
||||
if (IS_ERR(field)) {
|
||||
argv_free(argv);
|
||||
ret = PTR_ERR(field);
|
||||
goto err;
|
||||
goto err_free_arg;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1262,18 +1261,19 @@ static int __create_synth_event(const char *name, const char *raw_fields)
|
||||
if (cmd_version > 1 && n_fields_this_loop >= 1) {
|
||||
synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
goto err_free_arg;
|
||||
}
|
||||
|
||||
fields[n_fields++] = field;
|
||||
if (n_fields == SYNTH_FIELDS_MAX) {
|
||||
synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
goto err_free_arg;
|
||||
}
|
||||
|
||||
n_fields_this_loop++;
|
||||
}
|
||||
argv_free(argv);
|
||||
|
||||
if (consumed < argc) {
|
||||
synth_err(SYNTH_ERR_INVALID_CMD, 0);
|
||||
@@ -1281,7 +1281,6 @@ static int __create_synth_event(const char *name, const char *raw_fields)
|
||||
goto err;
|
||||
}
|
||||
|
||||
argv_free(argv);
|
||||
}
|
||||
|
||||
if (n_fields == 0) {
|
||||
@@ -1307,6 +1306,8 @@ static int __create_synth_event(const char *name, const char *raw_fields)
|
||||
kfree(saved_fields);
|
||||
|
||||
return ret;
|
||||
err_free_arg:
|
||||
argv_free(argv);
|
||||
err:
|
||||
for (i = 0; i < n_fields; i++)
|
||||
free_synth_field(fields[i]);
|
||||
|
||||
@@ -1313,6 +1313,7 @@ static int uprobe_perf_open(struct trace_event_call *call,
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
|
||||
tu = container_of(pos, struct trace_uprobe, tp);
|
||||
err = uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
|
||||
if (err) {
|
||||
uprobe_perf_close(call, event);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/kmemleak.h>
|
||||
|
||||
#include "tracing_map.h"
|
||||
#include "trace.h"
|
||||
@@ -307,6 +308,7 @@ static void tracing_map_array_free(struct tracing_map_array *a)
|
||||
for (i = 0; i < a->n_pages; i++) {
|
||||
if (!a->pages[i])
|
||||
break;
|
||||
kmemleak_free(a->pages[i]);
|
||||
free_page((unsigned long)a->pages[i]);
|
||||
}
|
||||
|
||||
@@ -342,6 +344,7 @@ static struct tracing_map_array *tracing_map_array_alloc(unsigned int n_elts,
|
||||
a->pages[i] = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!a->pages[i])
|
||||
goto free;
|
||||
kmemleak_alloc(a->pages[i], PAGE_SIZE, 1, GFP_KERNEL);
|
||||
}
|
||||
out:
|
||||
return a;
|
||||
|
||||
Reference in New Issue
Block a user