summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>2026-09-28 19:15:10 +0200
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>2026-09-28 19:15:10 +0200
commit8157cd417d72137ba0bd4c8c8355a67223f1ad95 (patch)
tree81336279f25344980f8b24c0803581f6b4671965 /kernel
parentc6c159fcdb4e9a678eda0831f971e4a3a488ec47 (diff)
parent72d3fcf802c45d00b300f25b848a93c3a2bd7c7e (diff)
Merge tag 'v7.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux into pinctrl-qcom/for-current
Linux 7.3-rc5
Diffstat (limited to 'kernel')
-rw-r--r--kernel/bpf/btf.c136
-rw-r--r--kernel/bpf/check_btf.c12
-rw-r--r--kernel/bpf/core.c21
-rw-r--r--kernel/bpf/crypto.c5
-rw-r--r--kernel/bpf/fixups.c24
-rw-r--r--kernel/bpf/hashtab.c43
-rw-r--r--kernel/bpf/helpers.c60
-rw-r--r--kernel/bpf/memalloc.c50
-rw-r--r--kernel/bpf/offload.c2
-rw-r--r--kernel/bpf/states.c16
-rw-r--r--kernel/bpf/syscall.c15
-rw-r--r--kernel/bpf/verifier.c162
-rw-r--r--kernel/cgroup/cgroup.c7
-rw-r--r--kernel/cgroup/cpuset.c3
-rw-r--r--kernel/cgroup/pids.c5
-rw-r--r--kernel/dma/coherent.c3
-rw-r--r--kernel/dma/swiotlb.c4
-rw-r--r--kernel/entry/syscall_user_dispatch.c4
-rw-r--r--kernel/events/core.c23
-rw-r--r--kernel/exit.c48
-rw-r--r--kernel/fork.c13
-rw-r--r--kernel/kprobes.c22
-rw-r--r--kernel/nstree.c10
-rw-r--r--kernel/power/hibernate.c26
-rw-r--r--kernel/printk/nbcon.c6
-rw-r--r--kernel/printk/printk.c2
-rw-r--r--kernel/reboot.c19
-rw-r--r--kernel/sched/core.c20
-rw-r--r--kernel/sched/ext/cid.c26
-rw-r--r--kernel/sched/ext/ext.c294
-rw-r--r--kernel/sched/ext/idle.c11
-rw-r--r--kernel/sched/ext/inlines.h4
-rw-r--r--kernel/sched/ext/internal.h72
-rw-r--r--kernel/sched/ext/sub.c23
-rw-r--r--kernel/sched/ext/types.h4
-rw-r--r--kernel/sched/fair.c435
-rw-r--r--kernel/sched/topology.c22
-rw-r--r--kernel/signal.c127
-rw-r--r--kernel/time/clockevents.c33
-rw-r--r--kernel/time/hrtimer.c15
-rw-r--r--kernel/time/jiffies.c16
-rw-r--r--kernel/time/posix-cpu-timers.c40
-rw-r--r--kernel/time/tick-broadcast.c36
-rw-r--r--kernel/time/tick-internal.h2
-rw-r--r--kernel/trace/fgraph.c3
-rw-r--r--kernel/trace/fprobe.c15
-rw-r--r--kernel/trace/ftrace.c2
-rw-r--r--kernel/trace/rethook.c2
-rw-r--r--kernel/trace/ring_buffer.c23
-rw-r--r--kernel/trace/trace.c46
-rw-r--r--kernel/trace/trace.h2
-rw-r--r--kernel/trace/trace_events.c2
-rw-r--r--kernel/trace/trace_events_hist.c86
-rw-r--r--kernel/trace/trace_events_trigger.c4
-rw-r--r--kernel/trace/trace_events_user.c26
-rw-r--r--kernel/trace/trace_functions_graph.c3
-rw-r--r--kernel/trace/trace_remote.c6
-rw-r--r--kernel/workqueue.c2
58 files changed, 1507 insertions, 636 deletions
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 9f33e95d5741..d870bc5e50bc 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -4268,13 +4268,10 @@ int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec)
{
int i;
- /* There are three types that signify ownership of some other type:
- * kptr_ref, bpf_list_head, bpf_rb_root.
- * kptr_ref only supports storing kernel types, which can't store
- * references to program allocated local types.
- *
- * Hence we only need to ensure that bpf_{list_head,rb_root} ownership
- * does not form cycles.
+ /*
+ * Check fields which require the complete BTF and initialize runtime
+ * metadata. Ownership relationships are validated after every record has
+ * been fixed up.
*/
if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & (BPF_GRAPH_ROOT | BPF_UPTR)))
return 0;
@@ -4305,51 +4302,88 @@ int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec)
if (!meta)
return -EFAULT;
rec->fields[i].graph_root.value_rec = meta->record;
+ }
+ return 0;
+}
- /* We need to set value_rec for all root types, but no need
- * to check ownership cycle for a type unless it's also a
- * node type.
- */
- if (!(rec->field_mask & BPF_GRAPH_NODE))
+static int btf_owned_type_idx(const struct btf *btf, struct btf_struct_metas *tab,
+ const struct btf_field *field)
+{
+ struct btf_struct_meta *meta;
+ u32 btf_id;
+
+ if (field->type & BPF_GRAPH_ROOT) {
+ btf_id = field->graph_root.value_btf_id;
+ } else if (field->type == BPF_KPTR_REF || field->type == BPF_KPTR_PERCPU) {
+ if (btf_is_kernel(field->kptr.btf))
+ return -ENOENT;
+ btf_id = field->kptr.btf_id;
+ } else {
+ return -ENOENT;
+ }
+
+ meta = btf_find_struct_meta(btf, btf_id);
+ if (!meta)
+ return field->type & BPF_GRAPH_ROOT ? -EFAULT : -ENOENT;
+ return meta - tab->types;
+}
+
+/*
+ * Each ownership edge adds kernel frames through bpf_obj_free_fields() and
+ * __bpf_obj_drop_impl(). Keep the bound deliberately small because object
+ * destruction can itself run below a BPF call chain. A final pointee without
+ * special fields is not present in the struct metadata table and adds only a
+ * non-recursing drop.
+ */
+#define BTF_MAX_OWNERSHIP_DEPTH 8
+
+static int btf_ownership_depth(const struct btf *btf,
+ struct btf_struct_metas *tab, u8 *depth,
+ int idx, int depth_left)
+{
+ const struct btf_record *rec = tab->types[idx].record;
+ int i, ret, max_depth = 0;
+
+ if (!depth_left)
+ return -ELOOP;
+ if (depth[idx])
+ goto done;
+
+ for (i = 0; i < rec->cnt; i++) {
+ ret = btf_owned_type_idx(btf, tab, &rec->fields[i]);
+ if (ret == -ENOENT)
continue;
+ if (ret < 0)
+ return ret;
+ ret = btf_ownership_depth(btf, tab, depth, ret, depth_left - 1);
+ if (ret < 0)
+ return ret;
+ max_depth = max(max_depth, ret);
+ }
+ depth[idx] = max_depth + 1;
+done:
+ return depth[idx] > depth_left ? -ELOOP : depth[idx];
+}
- /* We need to ensure ownership acyclicity among all types. The
- * proper way to do it would be to topologically sort all BTF
- * IDs based on the ownership edges, since there can be multiple
- * bpf_{list_head,rb_node} in a type. Instead, we use the
- * following resaoning:
- *
- * - A type can only be owned by another type in user BTF if it
- * has a bpf_{list,rb}_node. Let's call these node types.
- * - A type can only _own_ another type in user BTF if it has a
- * bpf_{list_head,rb_root}. Let's call these root types.
- *
- * We ensure that if a type is both a root and node, its
- * element types cannot be root types.
- *
- * To ensure acyclicity:
- *
- * When A is an root type but not a node, its ownership
- * chain can be:
- * A -> B -> C
- * Where:
- * - A is an root, e.g. has bpf_rb_root.
- * - B is both a root and node, e.g. has bpf_rb_node and
- * bpf_list_head.
- * - C is only an root, e.g. has bpf_list_node
- *
- * When A is both a root and node, some other type already
- * owns it in the BTF domain, hence it can not own
- * another root type through any of the ownership edges.
- * A -> B
- * Where:
- * - A is both an root and node.
- * - B is only an node.
- */
- if (meta->record->field_mask & BPF_GRAPH_ROOT)
- return -ELOOP;
+static int btf_check_ownership_depth(const struct btf *btf,
+ struct btf_struct_metas *tab)
+{
+ u8 *depth;
+ int i, ret = 0;
+
+ depth = kvcalloc(tab->cnt, sizeof(*depth), GFP_KERNEL | __GFP_NOWARN);
+ if (!depth)
+ return -ENOMEM;
+
+ for (i = 0; i < tab->cnt; i++) {
+ ret = btf_ownership_depth(btf, tab, depth, i,
+ BTF_MAX_OWNERSHIP_DEPTH);
+ if (ret < 0)
+ break;
+ ret = 0;
}
- return 0;
+ kvfree(depth);
+ return ret;
}
static void __btf_struct_show(const struct btf *btf, const struct btf_type *t,
@@ -6044,6 +6078,10 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr,
if (err < 0)
goto errout_meta;
}
+
+ err = btf_check_ownership_depth(btf, struct_meta_tab);
+ if (err < 0)
+ goto errout_meta;
}
err = bpf_log_attr_finalize(attr_log, &env->log);
@@ -7187,7 +7225,7 @@ again:
if (btf_type_is_int(t))
return WALK_SCALAR;
- if (!btf_type_is_struct(t))
+ if (!btf_type_is_struct(t) || !t->size)
goto error;
off = (off - moff) % t->size;
diff --git a/kernel/bpf/check_btf.c b/kernel/bpf/check_btf.c
index 0e8b3ccc7a5b..4c1ed842f661 100644
--- a/kernel/bpf/check_btf.c
+++ b/kernel/bpf/check_btf.c
@@ -338,9 +338,9 @@ err_free:
#define MIN_CORE_RELO_SIZE sizeof(struct bpf_core_relo)
#define MAX_CORE_RELO_SIZE MAX_FUNCINFO_REC_SIZE
-static int check_core_relo(struct bpf_verifier_env *env,
- const union bpf_attr *attr,
- bpfptr_t uattr)
+int bpf_check_core_relo(struct bpf_verifier_env *env,
+ const union bpf_attr *attr,
+ bpfptr_t uattr)
{
u32 i, nr_core_relo, ncopy, expected_size, rec_size;
struct bpf_core_relo core_relo = {};
@@ -414,7 +414,7 @@ int bpf_prepare_btf_info(struct bpf_verifier_env *env,
struct btf *btf;
int err;
- if (!attr->func_info_cnt && !attr->line_info_cnt) {
+ if (!attr->func_info_cnt && !attr->line_info_cnt && !attr->core_relo_cnt) {
if (check_abnormal_return(env))
return -EINVAL;
return 0;
@@ -455,9 +455,5 @@ int bpf_check_btf_info(struct bpf_verifier_env *env,
if (err)
return err;
- err = check_core_relo(env, attr, uattr);
- if (err)
- return err;
-
return 0;
}
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 8b294dfc1ad4..2e3bf8113ae9 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -19,6 +19,7 @@
#include <uapi/linux/btf.h>
#include <linux/filter.h>
+#include <linux/sched/signal.h>
#include <linux/skbuff.h>
#include <linux/static_call.h>
#include <linux/vmalloc.h>
@@ -1619,6 +1620,8 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp
* fix it up here on error.
*/
bpf_jit_prog_release_other(prog, clone);
+ if (env && fatal_signal_pending(current))
+ return ERR_PTR(-EINTR);
return IS_ERR(tmp) ? tmp : ERR_PTR(-ENOMEM);
}
@@ -2636,11 +2639,14 @@ static struct bpf_prog *bpf_prog_jit_compile(struct bpf_verifier_env *env, struc
orig_prog = prog;
prog = bpf_jit_blind_constants(env, prog);
/*
- * If blinding was requested and we failed during blinding, we must fall
- * back to the interpreter.
+ * Fall back to the interpreter after blinding failures, except when
+ * the loader was killed.
*/
- if (IS_ERR(prog))
+ if (IS_ERR(prog)) {
+ if (PTR_ERR(prog) == -EINTR)
+ return prog;
goto out_restore;
+ }
prog = bpf_int_jit_compile(env, prog);
if (prog->jited) {
@@ -2659,6 +2665,8 @@ out_restore:
struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct bpf_prog *fp,
int *err)
{
+ struct bpf_prog *jit_prog;
+
/* In case of BPF to BPF calls, verifier did all the prep
* work with regards to JITing, etc.
*/
@@ -2681,7 +2689,12 @@ struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct
if (*err)
return fp;
- fp = bpf_prog_jit_compile(env, fp);
+ jit_prog = bpf_prog_jit_compile(env, fp);
+ if (IS_ERR(jit_prog)) {
+ *err = PTR_ERR(jit_prog);
+ return fp;
+ }
+ fp = jit_prog;
bpf_prog_jit_attempt_done(fp);
if (!fp->jited && jit_needed) {
*err = -ENOTSUPP;
diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
index 51f89cecefb4..3f3fe2450fc6 100644
--- a/kernel/bpf/crypto.c
+++ b/kernel/bpf/crypto.c
@@ -149,8 +149,9 @@ bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,
const struct bpf_crypto_type *type;
struct bpf_crypto_ctx *ctx;
- if (!params || params->reserved[0] || params->reserved[1] ||
- params__sz != sizeof(struct bpf_crypto_params)) {
+ if (!params ||
+ params__sz != sizeof(struct bpf_crypto_params) ||
+ params->reserved[0] || params->reserved[1]) {
*err = -EINVAL;
return NULL;
}
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 52d3cec33672..d6f83521fc78 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -8,6 +8,7 @@
#include <linux/bsearch.h>
#include <linux/sort.h>
#include <linux/perf_event.h>
+#include <linux/sched/signal.h>
#include <net/xdp.h>
#include "disasm.h"
@@ -306,12 +307,28 @@ static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)
}
}
+/*
+ * Some post-verification instruction rewriting passes require an
+ * O(prog->len) operation per instruction. Keep their shared primitives
+ * killable and preemptible.
+ */
+static bool bpf_rewrite_must_abort(void)
+{
+ if (fatal_signal_pending(current))
+ return true;
+ cond_resched();
+ return false;
+}
+
struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
const struct bpf_insn *patch, u32 len)
{
struct bpf_prog *new_prog;
struct bpf_insn_aux_data *new_data = NULL;
+ if (bpf_rewrite_must_abort())
+ return NULL;
+
if (len > 1) {
new_data = vrealloc(env->insn_aux_data,
array_size(env->prog->len + len - 1,
@@ -523,6 +540,9 @@ static int verifier_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt)
unsigned int orig_prog_len = env->prog->len;
int err;
+ if (bpf_rewrite_must_abort())
+ return -EINTR;
+
if (bpf_prog_is_offloaded(env->prog->aux))
bpf_prog_offload_remove_insns(env, off, cnt);
@@ -1356,7 +1376,7 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env)
}
prog = bpf_jit_blind_constants(env, prog);
if (IS_ERR(prog)) {
- err = -ENOMEM;
+ err = PTR_ERR(prog);
prog = orig_prog;
goto out_restore;
}
@@ -1433,7 +1453,7 @@ int bpf_fixup_call_args(struct bpf_verifier_env *env)
err = bpf_jit_subprogs(env);
if (err == 0)
return 0;
- if (err == -EFAULT)
+ if (err == -EFAULT || err == -EINTR)
return err;
}
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 6f331c80130d..f9464e566f10 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -1054,14 +1054,17 @@ static void pcpu_init_value(struct bpf_htab *htab, void __percpu *pptr,
/* When not setting the initial value on all cpus, zero-fill element
* values for other cpus. Otherwise, bpf program has no way to ensure
* known initial values for cpus other than current one
- * (onallcpus=false always when coming from bpf prog).
+ * (onallcpus=false always when coming from bpf prog,
+ * map_flags & BPF_F_CPU when coming from syscall but setting
+ * only one cpu).
*/
- if (!onallcpus) {
- int current_cpu = raw_smp_processor_id();
+ if (!onallcpus || (map_flags & BPF_F_CPU)) {
+ int init_cpu = (map_flags & BPF_F_CPU) ? map_flags >> 32 :
+ raw_smp_processor_id();
int cpu;
for_each_possible_cpu(cpu) {
- if (cpu == current_cpu)
+ if (cpu == init_cpu)
copy_map_value(&htab->map, per_cpu_ptr(pptr, cpu), value);
else /* Since elem is preallocated, we cannot touch special fields */
zero_map_value(&htab->map, per_cpu_ptr(pptr, cpu));
@@ -1772,6 +1775,12 @@ static int htab_lru_percpu_map_lookup_and_delete_elem(struct bpf_map *map,
flags);
}
+/*
+ * Max consecutive empty buckets to walk in one RCU +
+ * instrumentation-disabled section before rescheduling.
+ */
+#define HTAB_BATCH_EMPTY_RESCHED 64
+
static int
__htab_map_lookup_and_delete_batch(struct bpf_map *map,
const union bpf_attr *attr,
@@ -1793,6 +1802,7 @@ __htab_map_lookup_and_delete_batch(struct bpf_map *map,
unsigned long flags = 0;
bool locked = false;
struct htab_elem *l;
+ u32 empty_cnt = 0;
struct bucket *b;
int ret = 0;
@@ -1971,30 +1981,41 @@ again_nocopy:
}
next_batch:
- /* If we are not copying data, we can go to next bucket and avoid
- * unlocking the rcu.
+ /*
+ * If we are not copying data, we can go to next bucket and avoid
+ * unlocking the rcu. Bound the walk though: after
+ * HTAB_BATCH_EMPTY_RESCHED consecutive empty buckets, fully exit
+ * the critical section (no locks are held here) and reschedule.
*/
if (!bucket_cnt && (batch + 1 < htab->n_buckets)) {
batch++;
- goto again_nocopy;
+ if (++empty_cnt < HTAB_BATCH_EMPTY_RESCHED)
+ goto again_nocopy;
+ empty_cnt = 0;
+ rcu_read_unlock();
+ bpf_enable_instrumentation();
+ cond_resched_tasks_rcu_qs();
+ goto again;
}
rcu_read_unlock();
bpf_enable_instrumentation();
- if (bucket_cnt && (copy_to_user(ukeys + total * key_size, keys,
- key_size * bucket_cnt) ||
- copy_to_user(uvalues + total * value_size, values,
- value_size * bucket_cnt))) {
+ if (bucket_cnt && (copy_to_user(ukeys + (size_t)total * key_size, keys,
+ (size_t)key_size * bucket_cnt) ||
+ copy_to_user(uvalues + (size_t)total * value_size, values,
+ (size_t)value_size * bucket_cnt))) {
ret = -EFAULT;
goto after_loop;
}
total += bucket_cnt;
+ empty_cnt = 0;
batch++;
if (batch >= htab->n_buckets) {
ret = -ENOENT;
goto after_loop;
}
+ cond_resched_tasks_rcu_qs();
goto again;
after_loop:
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b3cc5c8fc875..712dca5a2c5b 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4883,7 +4883,7 @@ BTF_ID(func, bpf_cgroup_release_dtor)
BTF_KFUNCS_START(common_btf_ids)
BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx, KF_FASTCALL)
-BTF_ID_FLAGS(func, bpf_rdonly_cast, KF_FASTCALL)
+BTF_ID_FLAGS(func, bpf_rdonly_cast, KF_FASTCALL | KF_PERFMON)
BTF_ID_FLAGS(func, bpf_rcu_read_lock)
BTF_ID_FLAGS(func, bpf_rcu_read_unlock)
BTF_ID_FLAGS(func, bpf_dynptr_slice, KF_RET_NULL)
@@ -4920,26 +4920,26 @@ BTF_ID_FLAGS(func, bpf_wq_set_callback, KF_IMPLICIT_ARGS)
BTF_ID_FLAGS(func, bpf_wq_start)
BTF_ID_FLAGS(func, bpf_preempt_disable)
BTF_ID_FLAGS(func, bpf_preempt_enable)
-BTF_ID_FLAGS(func, bpf_iter_bits_new, KF_ITER_NEW)
+BTF_ID_FLAGS(func, bpf_iter_bits_new, KF_ITER_NEW | KF_PERFMON)
BTF_ID_FLAGS(func, bpf_iter_bits_next, KF_ITER_NEXT | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_iter_bits_destroy, KF_ITER_DESTROY)
-BTF_ID_FLAGS(func, bpf_copy_from_user_str, KF_SLEEPABLE)
-BTF_ID_FLAGS(func, bpf_copy_from_user_task_str, KF_SLEEPABLE)
-BTF_ID_FLAGS(func, bpf_get_kmem_cache)
+BTF_ID_FLAGS(func, bpf_copy_from_user_str, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_task_str, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_get_kmem_cache, KF_PERFMON)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_new, KF_ITER_NEW | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_local_irq_save)
BTF_ID_FLAGS(func, bpf_local_irq_restore)
#ifdef CONFIG_BPF_EVENTS
-BTF_ID_FLAGS(func, bpf_probe_read_user_dynptr)
-BTF_ID_FLAGS(func, bpf_probe_read_kernel_dynptr)
-BTF_ID_FLAGS(func, bpf_probe_read_user_str_dynptr)
-BTF_ID_FLAGS(func, bpf_probe_read_kernel_str_dynptr)
-BTF_ID_FLAGS(func, bpf_copy_from_user_dynptr, KF_SLEEPABLE)
-BTF_ID_FLAGS(func, bpf_copy_from_user_str_dynptr, KF_SLEEPABLE)
-BTF_ID_FLAGS(func, bpf_copy_from_user_task_dynptr, KF_SLEEPABLE)
-BTF_ID_FLAGS(func, bpf_copy_from_user_task_str_dynptr, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_probe_read_user_dynptr, KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_probe_read_kernel_dynptr, KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_probe_read_user_str_dynptr, KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_probe_read_kernel_str_dynptr, KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_dynptr, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_str_dynptr, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_task_dynptr, KF_SLEEPABLE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_copy_from_user_task_str_dynptr, KF_SLEEPABLE | KF_PERFMON)
#endif
#ifdef CONFIG_DMA_SHARED_BUFFER
BTF_ID_FLAGS(func, bpf_iter_dmabuf_new, KF_ITER_NEW | KF_SLEEPABLE)
@@ -4947,26 +4947,26 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
#endif
BTF_ID_FLAGS(func, __bpf_trap)
-BTF_ID_FLAGS(func, bpf_strcmp);
-BTF_ID_FLAGS(func, bpf_strcasecmp);
-BTF_ID_FLAGS(func, bpf_strncasecmp);
-BTF_ID_FLAGS(func, bpf_strchr);
-BTF_ID_FLAGS(func, bpf_strchrnul);
-BTF_ID_FLAGS(func, bpf_strnchr);
-BTF_ID_FLAGS(func, bpf_strrchr);
-BTF_ID_FLAGS(func, bpf_strlen);
-BTF_ID_FLAGS(func, bpf_strnlen);
-BTF_ID_FLAGS(func, bpf_strspn);
-BTF_ID_FLAGS(func, bpf_strcspn);
-BTF_ID_FLAGS(func, bpf_strstr);
-BTF_ID_FLAGS(func, bpf_strcasestr);
-BTF_ID_FLAGS(func, bpf_strnstr);
-BTF_ID_FLAGS(func, bpf_strncasestr);
+BTF_ID_FLAGS(func, bpf_strcmp, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strcasecmp, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strncasecmp, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strchr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strchrnul, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strnchr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strrchr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strlen, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strnlen, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strspn, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strcspn, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strstr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strcasestr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strnstr, KF_PERFMON);
+BTF_ID_FLAGS(func, bpf_strncasestr, KF_PERFMON);
#if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
#endif
-BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
-BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE)
+BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE | KF_PERFMON)
+BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS | KF_SPINLOCK_SAFE | KF_PERFMON)
BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_IMPLICIT_ARGS)
BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_IMPLICIT_ARGS)
BTF_ID_FLAGS(func, bpf_dynptr_from_file)
diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c
index e9662db7198f..8a8f088e83e6 100644
--- a/kernel/bpf/memalloc.c
+++ b/kernel/bpf/memalloc.c
@@ -119,6 +119,7 @@ struct bpf_mem_cache {
struct llist_head waiting_for_gp_ttrace;
struct rcu_head rcu_ttrace;
atomic_t call_rcu_ttrace_in_progress;
+ raw_spinlock_t lock;
};
struct bpf_mem_caches {
@@ -214,25 +215,24 @@ static void alloc_bulk(struct bpf_mem_cache *c, int cnt, int node, bool atomic)
gfp = __GFP_NOWARN | __GFP_ACCOUNT;
gfp |= atomic ? GFP_NOWAIT : GFP_KERNEL;
- for (i = 0; i < cnt; i++) {
- /*
- * For every 'c' llist_del_first(&c->free_by_rcu_ttrace); is
- * done only by one CPU == current CPU. Other CPUs might
- * llist_add() and llist_del_all() in parallel.
- */
- obj = llist_del_first(&c->free_by_rcu_ttrace);
- if (!obj)
- break;
- add_obj_to_free_list(c, obj);
- }
- if (i >= cnt)
- return;
+ /*
+ * c->lock serializes concurrent llist_del_first() against
+ * llist_del_all() in __free_rcu() and do_call_rcu_ttrace().
+ */
+ scoped_guard(raw_spinlock_irqsave, &c->lock) {
+ for (i = 0; i < cnt; i++) {
+ obj = llist_del_first(&c->free_by_rcu_ttrace);
+ if (!obj)
+ break;
+ add_obj_to_free_list(c, obj);
+ }
- for (; i < cnt; i++) {
- obj = llist_del_first(&c->waiting_for_gp_ttrace);
- if (!obj)
- break;
- add_obj_to_free_list(c, obj);
+ for (; i < cnt; i++) {
+ obj = llist_del_first(&c->waiting_for_gp_ttrace);
+ if (!obj)
+ break;
+ add_obj_to_free_list(c, obj);
+ }
}
if (i >= cnt)
return;
@@ -279,8 +279,12 @@ static int free_all(struct bpf_mem_cache *c, struct llist_node *llnode, bool per
static void __free_rcu(struct rcu_head *head)
{
struct bpf_mem_cache *c = container_of(head, struct bpf_mem_cache, rcu_ttrace);
+ struct llist_node *llnode;
+
+ scoped_guard(raw_spinlock_irqsave, &c->lock)
+ llnode = llist_del_all(&c->waiting_for_gp_ttrace);
- free_all(c, llist_del_all(&c->waiting_for_gp_ttrace), !!c->percpu_size);
+ free_all(c, llnode, !!c->percpu_size);
atomic_set(&c->call_rcu_ttrace_in_progress, 0);
}
@@ -300,7 +304,8 @@ static void do_call_rcu_ttrace(struct bpf_mem_cache *c)
if (atomic_xchg(&c->call_rcu_ttrace_in_progress, 1)) {
if (unlikely(READ_ONCE(c->draining))) {
- llnode = llist_del_all(&c->free_by_rcu_ttrace);
+ scoped_guard(raw_spinlock_irqsave, &c->lock)
+ llnode = llist_del_all(&c->free_by_rcu_ttrace);
free_all(c, llnode, !!c->percpu_size);
}
return;
@@ -535,6 +540,7 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu)
c->objcg = objcg;
c->percpu_size = percpu_size;
c->tgt = c;
+ raw_spin_lock_init(&c->lock);
init_refill_work(c);
prefill_mem_cache(c, cpu);
}
@@ -557,7 +563,7 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu)
c->objcg = objcg;
c->percpu_size = percpu_size;
c->tgt = c;
-
+ raw_spin_lock_init(&c->lock);
init_refill_work(c);
prefill_mem_cache(c, cpu);
}
@@ -609,7 +615,7 @@ int bpf_mem_alloc_percpu_unit_init(struct bpf_mem_alloc *ma, int size)
c->objcg = objcg;
c->percpu_size = percpu_size;
c->tgt = c;
-
+ raw_spin_lock_init(&c->lock);
init_refill_work(c);
prefill_mem_cache(c, cpu);
}
diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c
index 0d6f5569588c..d855399812ee 100644
--- a/kernel/bpf/offload.c
+++ b/kernel/bpf/offload.c
@@ -698,6 +698,8 @@ static bool __bpf_offload_dev_match(struct bpf_prog *prog,
return false;
if (offload->netdev == netdev)
return true;
+ if (!bpf_prog_is_offloaded(prog->aux))
+ return false;
ondev1 = bpf_offload_find_netdev(offload->netdev);
ondev2 = bpf_offload_find_netdev(netdev);
diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c
index 66fb11b6c6a7..012b82513a3b 100644
--- a/kernel/bpf/states.c
+++ b/kernel/bpf/states.c
@@ -491,7 +491,8 @@ static bool regs_exact(const struct bpf_reg_state *rold,
{
return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
check_ids(rold->id, rcur->id, idmap) &&
- check_ids(rold->parent_id, rcur->parent_id, idmap);
+ check_ids(rold->parent_id, rcur->parent_id, idmap) &&
+ check_ids(rold->map_uid, rcur->map_uid, idmap);
}
enum exact_level {
@@ -616,7 +617,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
range_within(rold, rcur) &&
tnum_in(rold->var_off, rcur->var_off) &&
check_ids(rold->id, rcur->id, idmap) &&
- check_ids(rold->parent_id, rcur->parent_id, idmap);
+ check_ids(rold->parent_id, rcur->parent_id, idmap) &&
+ check_ids(rold->map_uid, rcur->map_uid, idmap);
case PTR_TO_PACKET_META:
case PTR_TO_PACKET:
/* We must have at least as much range as the old ptr
@@ -635,14 +637,14 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
/* id relations must be preserved */
if (!check_ids(rold->id, rcur->id, idmap))
return false;
+ /* Preserve displacements between pointers sharing an ID. */
+ if (rold->id && rold->r64.base != rcur->r64.base)
+ return false;
/* new val must satisfy old val knowledge */
return range_within(rold, rcur) &&
tnum_in(rold->var_off, rcur->var_off);
case PTR_TO_STACK:
- /* two stack pointers are equal only if they're pointing to
- * the same stack frame, since fp-8 in foo != fp-8 in bar
- */
- return regs_exact(rold, rcur, idmap) && rold->frameno == rcur->frameno;
+ return regs_exact(rold, rcur, idmap);
case PTR_TO_ARENA:
return true;
case PTR_TO_INSN:
@@ -1121,7 +1123,7 @@ static bool states_maybe_looping(struct bpf_verifier_state *old,
fcur = cur->frame[fr];
for (i = 0; i < MAX_BPF_REG; i++)
if (memcmp(&fold->regs[i], &fcur->regs[i],
- offsetof(struct bpf_reg_state, frameno)))
+ offsetof(struct bpf_reg_state, precise)))
return false;
return true;
}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index c7bc9ba9b331..244a939b9d2d 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2036,7 +2036,7 @@ int generic_map_delete_batch(struct bpf_map *map,
for (cp = 0; cp < max_count; cp++) {
err = -EFAULT;
- if (copy_from_user(key, keys + cp * map->key_size,
+ if (copy_from_user(key, keys + (size_t)cp * map->key_size,
map->key_size))
break;
@@ -2098,9 +2098,9 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
for (cp = 0; cp < max_count; cp++) {
err = -EFAULT;
- if (copy_from_user(key, keys + cp * map->key_size,
+ if (copy_from_user(key, keys + (size_t)cp * map->key_size,
map->key_size) ||
- copy_from_user(value, values + cp * value_size, value_size))
+ copy_from_user(value, values + (size_t)cp * value_size, value_size))
break;
err = bpf_map_update_value(map, map_file, key, value,
@@ -2179,12 +2179,12 @@ int generic_map_lookup_batch(struct bpf_map *map,
if (err)
goto free_buf;
- if (copy_to_user(keys + cp * map->key_size, key,
+ if (copy_to_user(keys + (size_t)cp * map->key_size, key,
map->key_size)) {
err = -EFAULT;
goto free_buf;
}
- if (copy_to_user(values + cp * value_size, value, value_size)) {
+ if (copy_to_user(values + (size_t)cp * value_size, value, value_size)) {
err = -EFAULT;
goto free_buf;
}
@@ -6042,7 +6042,10 @@ struct bpf_link *bpf_link_get_curr_or_next(u32 *id)
again:
link = idr_get_next(&link_idr, id);
if (link) {
- link = bpf_link_inc_not_zero(link);
+ if (link->id)
+ link = bpf_link_inc_not_zero(link);
+ else
+ link = ERR_PTR(-EAGAIN);
if (IS_ERR(link)) {
(*id)++;
goto again;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 72a3f5998dd2..41b49c56e123 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -567,7 +567,7 @@ static int stack_slot_obj_get_spi(struct bpf_verifier_env *env, struct bpf_reg_s
}
off = reg->var_off.value;
- if (off % BPF_REG_SIZE) {
+ if (off >= 0 || off % BPF_REG_SIZE) {
verbose(env, "cannot pass in %s at an offset=%d\n", obj_kind, off);
return -EINVAL;
}
@@ -1864,6 +1864,7 @@ static void __mark_reg_known(struct bpf_reg_state *reg, u64 imm)
offsetof(struct bpf_reg_state, var_off) - sizeof(reg->type));
reg->id = 0;
reg->parent_id = 0;
+ reg->map_uid = 0;
___mark_reg_known(reg, imm);
}
@@ -1925,17 +1926,18 @@ static void refine_map_lookup_value(struct bpf_reg_state *reg)
if (map->inner_map_meta) {
reg->type = CONST_PTR_TO_MAP | maybe_null;
reg->map_ptr = map->inner_map_meta;
- /* transfer reg's id which is unique for every map_lookup_elem
+ /*
+ * transfer reg's id which is unique for every map_lookup_elem
* as UID of the inner map.
*/
- if (btf_record_has_field(map->inner_map_meta->record,
- BPF_TIMER | BPF_WORKQUEUE | BPF_TASK_WORK))
- reg->map_uid = reg->id;
+ reg->map_uid = reg->id;
} else if (map->map_type == BPF_MAP_TYPE_XSKMAP) {
reg->type = PTR_TO_XDP_SOCK | maybe_null;
+ reg->map_uid = 0;
} else if (map->map_type == BPF_MAP_TYPE_SOCKMAP ||
map->map_type == BPF_MAP_TYPE_SOCKHASH) {
reg->type = PTR_TO_SOCKET | maybe_null;
+ reg->map_uid = 0;
}
}
@@ -3042,6 +3044,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
subprog[cur_subprog].exit_idx = i;
goto next;
}
+ if (insn_is_gotox(&insn[i]))
+ goto next;
off = i + bpf_jmp_offset(&insn[i]) + 1;
if (off < subprog_start || off >= subprog_end) {
verbose(env, "jump out of range from insn %d to %d\n", i, off);
@@ -3061,7 +3065,8 @@ next:
*/
if (code != (BPF_JMP | BPF_EXIT) &&
code != (BPF_JMP32 | BPF_JA) &&
- code != (BPF_JMP | BPF_JA)) {
+ code != (BPF_JMP | BPF_JA) &&
+ !insn_is_gotox(&insn[i])) {
verbose(env, "last insn is not an exit or jmp\n");
bpf_diag_program_structure(
env, i, "subprogram can fall through",
@@ -3582,7 +3587,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
save_register_state(env, state, spi, reg, size);
/* Break the relation on a narrowing spill. */
if (!reg_value_fits)
- state->stack[spi].spilled_ptr.id = 0;
+ clear_scalar_id(&state->stack[spi].spilled_ptr);
} else if (!reg && !(off % BPF_REG_SIZE) && is_bpf_st_mem(insn) &&
env->bpf_capable) {
struct bpf_reg_state *tmp_reg = &env->fake_reg[0];
@@ -6453,6 +6458,15 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
return -EACCES;
}
+ if (rdonly_untrusted && !env->allow_ptr_leaks) {
+ verbose(env, "%s access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n",
+ reg_type_str(env, reg->type));
+ bpf_diag_policy(env, insn_idx, "read from untrusted read-only memory",
+ "the access requires CAP_PERFMON",
+ "Load the program with CAP_PERFMON, or avoid dereferencing untrusted pointers.");
+ return -EPERM;
+ }
+
/*
* Accesses to untrusted PTR_TO_MEM are done through probe
* instructions, hence no need to check bounds in that case.
@@ -9736,6 +9750,16 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
if (check_mem_reg(env, reg, argno, arg->mem_size, BPF_READ | BPF_WRITE, NULL,
NULL))
return -EINVAL;
+ /*
+ * PTR_TO_PACKET get passed as PTR_TO_MEM, preventing
+ * us from adjusting bounds tracking info.
+ */
+ if ((reg_is_pkt_pointer_any(reg) || reg_is_dynptr_slice_pkt(reg)) &&
+ sub->changes_pkt_data) {
+ bpf_log(log, "%s is a packet pointer, but func#%d may change packet data\n",
+ reg_arg_name(env, argno), subprog);
+ return -EINVAL;
+ }
if (!(arg->arg_type & PTR_MAYBE_NULL) &&
(type_may_be_null(reg->type) || bpf_register_is_null(reg))) {
bpf_log(log, "%s is expected to be non-NULL\n",
@@ -9919,6 +9943,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (err == -EFAULT)
return err;
if (bpf_subprog_is_global(env, subprog)) {
+ struct bpf_func_info_aux *sub_aux = subprog_aux(env, subprog);
const char *sub_name = bpf_subprog_name(env, subprog);
const char *operation;
bool returns_void;
@@ -9950,11 +9975,10 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (env->log.level & BPF_LOG_LEVEL)
verbose(env, "Func#%d ('%s') is global and assumed valid.\n",
subprog, sub_name);
+ sub_aux->called[in_sleepable_context(env)] = true;
returns_void = subprog_returns_void(env, subprog);
if (env->subprog_info[subprog].changes_pkt_data)
clear_all_pkt_pointers(env);
- /* mark global subprog for verifying after main prog */
- subprog_aux(env, subprog)->called = true;
if (returns_void)
bpf_diag_record_scrub(env, &caller->regs[BPF_REG_0], BPF_DIAG_MOD_CALLER_SAVED);
else
@@ -10036,6 +10060,7 @@ int map_set_for_each_callback_args(struct bpf_verifier_env *env,
__mark_reg_known_zero(&callee->regs[BPF_REG_3]);
callee->regs[BPF_REG_3].map_ptr = caller->regs[BPF_REG_1].map_ptr;
callee->regs[BPF_REG_3].map_uid = caller->regs[BPF_REG_1].map_uid;
+ callee->regs[BPF_REG_3].id = ++env->id_gen;
/* pointer to stack or null */
callee->regs[BPF_REG_4] = caller->regs[BPF_REG_3];
@@ -10132,6 +10157,7 @@ static int set_timer_callback_state(struct bpf_verifier_env *env,
__mark_reg_known_zero(&callee->regs[BPF_REG_3]);
callee->regs[BPF_REG_3].map_ptr = map_ptr;
callee->regs[BPF_REG_3].map_uid = map_uid;
+ callee->regs[BPF_REG_3].id = ++env->id_gen;
/* unused */
bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
@@ -10250,6 +10276,7 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env,
__mark_reg_known_zero(&callee->regs[BPF_REG_3]);
callee->regs[BPF_REG_3].map_ptr = map_ptr;
callee->regs[BPF_REG_3].map_uid = map_uid;
+ callee->regs[BPF_REG_3].id = ++env->id_gen;
/* unused */
bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
@@ -10772,11 +10799,7 @@ int bpf_get_helper_proto(struct bpf_verifier_env *env, int func_id,
/* Check if we're in a sleepable context. */
static inline bool in_sleepable_context(struct bpf_verifier_env *env)
{
- return !env->cur_state->active_rcu_locks &&
- !env->cur_state->active_preempt_locks &&
- !env->cur_state->active_locks &&
- !env->cur_state->active_irq_id &&
- in_sleepable(env);
+ return !in_rcu_cs(env);
}
static const char *non_sleepable_context_description(struct bpf_verifier_env *env)
@@ -11356,6 +11379,11 @@ static bool is_kfunc_destructive(struct bpf_call_arg_meta *meta)
return meta->kfunc_flags & KF_DESTRUCTIVE;
}
+static bool is_kfunc_perfmon(struct bpf_call_arg_meta *meta)
+{
+ return meta->kfunc_flags & KF_PERFMON;
+}
+
static bool is_kfunc_rcu(struct bpf_call_arg_meta *meta)
{
return meta->kfunc_flags & KF_RCU;
@@ -13834,6 +13862,15 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return -EACCES;
}
+ if (is_kfunc_perfmon(&meta) && !env->allow_ptr_leaks) {
+ verbose(env, "%s is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n",
+ func_name);
+ operation = bpf_diag_fmt(env, "kfunc %s", func_name);
+ bpf_diag_policy(env, insn_idx, operation, "the kfunc requires CAP_PERFMON",
+ "Load the program with CAP_PERFMON, or avoid the kfunc.");
+ return -EPERM;
+ }
+
sleepable = bpf_is_kfunc_sleepable(&meta);
if (sleepable && !in_sleepable(env)) {
verbose(env, "program must be sleepable to call sleepable kfunc %s\n", func_name);
@@ -15704,6 +15741,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,
struct bpf_reg_state *regs = state->regs, *dst_reg, *src_reg;
struct bpf_reg_state *ptr_reg = NULL, off_reg = {0};
bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
+ struct bpf_insn_aux_data *aux = cur_aux(env);
u8 opcode = BPF_OP(insn->code);
int err;
@@ -15715,13 +15753,24 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,
/* Case where at least one operand is an arena. */
if (dst_reg->type == PTR_TO_ARENA || (src_reg && src_reg->type == PTR_TO_ARENA)) {
- struct bpf_insn_aux_data *aux = cur_aux(env);
if (dst_reg->type != PTR_TO_ARENA)
*dst_reg = *src_reg;
if (BPF_CLASS(insn->code) == BPF_ALU64) {
/*
+ * Only arena pointers set needs_zext, but doing so
+ * modifies the instruction at fixup time to an ALU32
+ * and makes it unsuitable for 64-bit scalar args. We
+ * prevent zext from being set if the instruction has
+ * been previously called with non-arena registers.
+ */
+ if (aux->prevent_zext) {
+ verbose(env, "same insn cannot be used with and without arena pointer\n");
+ return -EINVAL;
+ }
+
+ /*
* 32-bit operations zero upper bits automatically.
* 64-bit operations need to be converted to 32.
*/
@@ -15733,6 +15782,16 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,
return 0;
}
+ /* Prevent the instruction from being used with arena pointers (see above). */
+ if (env->prog->aux->arena && BPF_CLASS(insn->code) == BPF_ALU64) {
+ if (aux->needs_zext) {
+ verbose(env, "same insn cannot be used with and without arena pointer\n");
+ return -EINVAL;
+ }
+
+ aux->prevent_zext = true;
+ }
+
if (dst_reg->type != SCALAR_VALUE)
ptr_reg = dst_reg;
@@ -19421,13 +19480,14 @@ static void free_states(struct bpf_verifier_env *env)
}
}
-static int do_check_common(struct bpf_verifier_env *env, int subprog)
+static int do_check_common(struct bpf_verifier_env *env, int subprog, bool is_sleepable)
{
bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
struct bpf_subprog_info *sub = subprog_info(env, subprog);
struct bpf_prog_aux *aux = env->prog->aux;
struct bpf_verifier_state *state;
struct bpf_reg_state *regs;
+ u32 old_insns_total = sub->insns_total;
u32 insn_processed = env->insn_processed;
int ret, i;
@@ -19440,7 +19500,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
state->curframe = 0;
state->speculative = false;
state->branches = 1;
- state->in_sleepable = env->prog->sleepable;
+ state->in_sleepable = is_sleepable;
state->frame[0] = kzalloc_obj(struct bpf_func_state, GFP_KERNEL_ACCOUNT);
if (!state->frame[0]) {
kfree(state);
@@ -19581,8 +19641,10 @@ out:
* not accounted as callees by account_current_path().
* Accumulate their total counts as total counts of the main or
* global subprog hosting the async call.
+ * Start from the saved total of earlier contexts: adding to the current
+ * total would count this pass's synchronous paths twice.
*/
- env->subprog_info[subprog].insns_total = env->insn_processed - insn_processed;
+ sub->insns_total = old_insns_total + (env->insn_processed - insn_processed);
return ret;
}
@@ -19610,14 +19672,19 @@ static int do_check_subprogs(struct bpf_verifier_env *env)
{
struct bpf_prog_aux *aux = env->prog->aux;
struct bpf_func_info_aux *sub_aux;
- int i, ret, new_cnt;
+ int context, i, ret, new_cnt;
if (!aux->func_info)
return 0;
- /* exception callback is presumed to be always called */
- if (env->exception_callback_subprog)
- subprog_aux(env, env->exception_callback_subprog)->called = true;
+ /*
+ * Callbacks cannot throw, so the exception callback always runs in the
+ * main program's context. It is presumed to be always called.
+ */
+ if (env->exception_callback_subprog) {
+ sub_aux = subprog_aux(env, env->exception_callback_subprog);
+ sub_aux->called[env->prog->sleepable] = true;
+ }
again:
new_cnt = 0;
@@ -19626,29 +19693,28 @@ again:
continue;
sub_aux = subprog_aux(env, i);
- if (!sub_aux->called || sub_aux->verified)
- continue;
+ for (context = 0; context < ARRAY_SIZE(sub_aux->called); context++) {
+ if (!sub_aux->called[context] || sub_aux->verified[context])
+ continue;
- env->insn_idx = env->subprog_info[i].start;
- WARN_ON_ONCE(env->insn_idx == 0);
- ret = do_check_common(env, i);
- if (ret) {
- return ret;
- } else if (env->log.level & BPF_LOG_LEVEL) {
- verbose(env, "Func#%d ('%s') is safe for any args that match its prototype\n",
- i, bpf_subprog_name(env, i));
- }
+ env->insn_idx = env->subprog_info[i].start;
+ WARN_ON_ONCE(env->insn_idx == 0);
+ ret = do_check_common(env, i, context);
+ if (ret)
+ return ret;
+ if (env->log.level & BPF_LOG_LEVEL)
+ verbose(env, "Func#%d ('%s') is safe for any args "
+ "that match its prototype\n",
+ i, bpf_subprog_name(env, i));
- /* We verified new global subprog, it might have called some
- * more global subprogs that we haven't verified yet, so we
- * need to do another pass over subprogs to verify those.
- */
- sub_aux->verified = true;
- new_cnt++;
+ sub_aux->verified[context] = true;
+ new_cnt++;
+ }
}
- /* We can't loop forever as we verify at least one global subprog on
- * each pass.
+ /*
+ * We can't loop forever as each pass verifies at least one new context,
+ * and there are only two contexts per global subprog.
*/
if (new_cnt)
goto again;
@@ -19661,7 +19727,7 @@ static int do_check_main(struct bpf_verifier_env *env)
int ret;
env->insn_idx = 0;
- ret = do_check_common(env, 0);
+ ret = do_check_common(env, 0, env->prog->sleepable);
if (!ret)
env->prog->aux->stack_depth = env->subprog_info[0].stack_depth;
return ret;
@@ -21170,6 +21236,11 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
ret = bpf_diag_init(env);
if (ret)
goto err_prep;
+ if (env->prog->insnsi[env->prog->len - 1].code == (BPF_LD | BPF_IMM | BPF_DW)) {
+ verbose(env, "invalid bpf_ld_imm64 insn\n");
+ ret = -EINVAL;
+ goto err_prep;
+ }
if (env->signature) {
ret = bpf_prog_calc_tag(env->prog);
if (ret < 0)
@@ -21245,6 +21316,11 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
if (ret < 0)
goto skip_full_check;
+ /* Apply CO-RE before validating the program's instruction layout. */
+ ret = bpf_check_core_relo(env, attr, uattr);
+ if (ret < 0)
+ goto skip_full_check;
+
/* Discover all subprograms before validating their layout and BTF. */
ret = add_subprogs(env);
if (ret < 0)
@@ -21254,7 +21330,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
if (ret < 0)
goto skip_full_check;
- /* Validate BTF against the complete subprogram layout and apply CO-RE. */
+ /* Validate BTF against the complete subprogram layout. */
ret = bpf_check_btf_info(env, attr, uattr);
if (ret < 0)
goto skip_full_check;
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 2d532bf2c0c7..227d09704ca5 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -5303,10 +5303,13 @@ struct task_struct *css_task_iter_next(struct css_task_iter *it)
if (it->flags & CSS_TASK_ITER_SKIPPED)
css_task_iter_advance(it);
- if (it->task_pos) {
+ while (it->task_pos && !it->cur_task) {
it->cur_task = list_entry(it->task_pos, struct task_struct,
cg_list);
- get_task_struct(it->cur_task);
+ /* a task on dying_tasks with zero refcount is only valid for
+ * RCU readers, not even interesting for
+ * CSS_TASK_ITER_WITH_DEAD, find another one */
+ it->cur_task = tryget_task_struct(it->cur_task);
css_task_iter_advance(it);
}
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 2538faac9aba..3f52717c1965 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1591,10 +1591,11 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
* above it or remote partition root underneath it is not allowed.
*/
compute_excpus(cs, tmp->new_cpus);
- WARN_ON_ONCE(cpumask_intersects(tmp->new_cpus, subpartitions_cpus));
if (!cpumask_intersects(tmp->new_cpus, cpu_active_mask) ||
cpumask_subset(top_cpuset.effective_cpus, tmp->new_cpus))
return PERR_INVCPUS;
+ if (cpumask_intersects(tmp->new_cpus, subpartitions_cpus))
+ return PERR_NOCPUS;
if (((new_prs == PRS_ISOLATED) &&
!isolated_cpus_can_update(tmp->new_cpus, NULL)) ||
prstate_housekeeping_conflict(new_prs, tmp->new_cpus))
diff --git a/kernel/cgroup/pids.c b/kernel/cgroup/pids.c
index ecbb839d2acb..78cdc0558d0c 100644
--- a/kernel/cgroup/pids.c
+++ b/kernel/cgroup/pids.c
@@ -253,6 +253,11 @@ static void pids_event(struct pids_cgroup *pids_forking,
}
if (!cgroup_subsys_on_dfl(pids_cgrp_subsys) ||
cgrp_dfl_root.flags & CGRP_ROOT_PIDS_LOCAL_EVENTS) {
+ /*
+ * pids.events reports the local counter on legacy hierarchies
+ * and when pids_localevents is enabled.
+ */
+ cgroup_file_notify(&p->events_file);
cgroup_file_notify(&p->events_local_file);
return;
}
diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c
index 45bbae947f4b..4d0266893bcc 100644
--- a/kernel/dma/coherent.c
+++ b/kernel/dma/coherent.c
@@ -352,8 +352,7 @@ static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit))
dev_warn(dev, "reserved memory is beyond device's set DMA address range\n");
- dma_assign_coherent_memory(dev, mem);
- return 0;
+ return dma_assign_coherent_memory(dev, mem);
}
static void rmem_dma_device_release(struct reserved_mem *rmem,
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index ded7016a46a7..aa2f1c4588b9 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -1019,7 +1019,6 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size
int index = (tlb_addr - mem->start) >> IO_TLB_SHIFT;
phys_addr_t orig_addr = mem->slots[index].orig_addr;
size_t alloc_size = mem->slots[index].alloc_size;
- unsigned long pfn = PFN_DOWN(orig_addr);
unsigned char *vaddr = mem->vaddr + tlb_addr - mem->start;
int tlb_offset;
@@ -1052,7 +1051,8 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size
size = alloc_size;
}
- if (PageHighMem(pfn_to_page(pfn))) {
+ if (PhysHighMem(orig_addr)) {
+ unsigned long pfn = PFN_DOWN(orig_addr);
unsigned int offset = orig_addr & ~PAGE_MASK;
struct page *page;
unsigned int sz = 0;
diff --git a/kernel/entry/syscall_user_dispatch.c b/kernel/entry/syscall_user_dispatch.c
index 2002c7aae435..59c861866941 100644
--- a/kernel/entry/syscall_user_dispatch.c
+++ b/kernel/entry/syscall_user_dispatch.c
@@ -178,7 +178,7 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz
(char __user *)(uintptr_t)cfg.selector);
}
-#ifdef CONFIG_PROC_SYSCTL
+#ifdef CONFIG_SYSCTL
static const struct ctl_table syscall_user_dispatch_sysctls[] = {
{
.procname = "syscall_user_dispatch",
@@ -195,4 +195,4 @@ static int __init syscall_user_dispatch_sysctl_init(void)
return 0;
}
late_initcall(syscall_user_dispatch_sysctl_init);
-#endif /* CONFIG_PROC_SYSCTL */
+#endif /* CONFIG_SYSCTL */
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 33210aff3ee6..634d2ccbab82 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -3764,6 +3764,9 @@ static void perf_ctx_sched_task_cb(struct perf_event_context *ctx,
list_for_each_entry(pmu_ctx, &ctx->pmu_ctx_list, pmu_ctx_entry) {
cpc = this_cpc(pmu_ctx->pmu);
+ if (cpc->task_epc != pmu_ctx)
+ continue;
+
if (cpc->sched_cb_usage && pmu_ctx->pmu->sched_task)
pmu_ctx->pmu->sched_task(pmu_ctx, task, sched_in);
}
@@ -3914,7 +3917,7 @@ static void __perf_pmu_sched_task(struct perf_cpu_pmu_context *cpc,
perf_ctx_lock(cpuctx, cpuctx->task_ctx);
perf_pmu_disable(pmu);
- pmu->sched_task(cpc->task_epc, task, sched_in);
+ pmu->sched_task(&cpc->epc, task, sched_in);
perf_pmu_enable(pmu);
perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
@@ -3924,15 +3927,17 @@ static void perf_pmu_sched_task(struct task_struct *prev,
struct task_struct *next,
bool sched_in)
{
- struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context);
- struct perf_cpu_pmu_context *cpc;
+ struct perf_cpu_pmu_context *cpc, *cpc2;
- /* cpuctx->task_ctx will be handled in perf_event_context_sched_in/out */
- if (prev == next || cpuctx->task_ctx)
+ if (prev == next)
return;
- list_for_each_entry(cpc, this_cpu_ptr(&sched_cb_list), sched_cb_entry)
+ list_for_each_entry_safe(cpc, cpc2, this_cpu_ptr(&sched_cb_list), sched_cb_entry) {
+ if (cpc->task_epc)
+ continue;
+
__perf_pmu_sched_task(cpc, sched_in ? next : prev, sched_in);
+ }
}
static void perf_event_switch(struct task_struct *task,
@@ -5454,6 +5459,8 @@ attach_task_ctx_data(struct task_struct *task, struct kmem_cache *ctx_cache,
}
if (refcount_inc_not_zero(&old->refcount)) {
+ if (global)
+ old->global = true;
free_perf_ctx_data(cd); /* unused */
return 0;
}
@@ -6350,6 +6357,9 @@ static DEFINE_MUTEX(perf_mediated_pmu_mutex);
/* !exclude_guest event of PMU with PERF_PMU_CAP_MEDIATED_VPMU */
static inline bool is_include_guest_event(struct perf_event *event)
{
+ if (!event->pmu)
+ return false;
+
if ((event->pmu->capabilities & PERF_PMU_CAP_MEDIATED_VPMU) &&
!event->attr.exclude_guest)
return true;
@@ -13002,6 +13012,7 @@ static void __pmu_detach_event(struct pmu *pmu, struct perf_event *event,
exclusive_event_destroy(event);
module_put(pmu->module);
+ mediated_pmu_unaccount_event(event);
event->pmu = NULL; /* force fault instead of UAF */
}
diff --git a/kernel/exit.c b/kernel/exit.c
index 97686af89501..282328d2b4cf 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -261,8 +261,11 @@ repeat:
pidfs_exit(p);
cgroup_task_release(p);
- /* Retrieve @thread_pid before __unhash_process() may set it to NULL. */
- thread_pid = task_pid(p);
+ /*
+ * Pin @thread_pid before __unhash_process() clears it. The last
+ * PIDTYPE detach can otherwise free it before proc_flush_pid().
+ */
+ thread_pid = get_pid(task_pid(p));
write_lock_irq(&tasklist_lock);
ptrace_release_task(p);
@@ -291,20 +294,21 @@ repeat:
}
write_unlock_irq(&tasklist_lock);
- /* @thread_pid can't go away until free_pids() below */
proc_flush_pid(thread_pid);
+ put_pid(thread_pid);
exit_cred_namespaces(p);
add_device_randomness(&p->se.sum_exec_runtime,
sizeof(p->se.sum_exec_runtime));
free_pids(post.pids);
release_thread(p);
/*
- * This task was already removed from the process/thread/pid lists
- * and lock_task_sighand(p) can't succeed. Nobody else can touch
- * ->pending or, if group dead, signal->shared_pending. We can call
- * flush_sigqueue() lockless.
+ * This task was already removed from the process/thread/pid lists and
+ * lock_task_sighand(p) can't succeed. If it's the group leader then
+ * flush tsk->signal->shared_pending. tsk->pending has been flushed
+ * already in exit_signals(). Nothing else can touch
+ * signal->shared_pending anymore, so flush_sigqueue() can be invoked
+ * lockless.
*/
- flush_sigqueue(&p->pending);
if (thread_group_leader(p))
flush_sigqueue(&p->signal->shared_pending);
@@ -547,32 +551,6 @@ void mm_update_next_owner(struct mm_struct *mm)
}
#endif /* CONFIG_MEMCG */
-#if defined(CONFIG_SCHED_CACHE) && defined(CONFIG_NUMA_BALANCING)
-/*
- * Subtract the memory footprint of the current task from
- * mm.
- */
-static void exit_mm_sched_cache(struct mm_struct *mm)
-{
- unsigned long fp, sub;
-
- if (!current->total_numa_faults)
- return;
- /*
- * No lock protection due to performance considerations.
- * Make sure mm->sc_stat.footprint does not become
- * negative.
- */
- fp = READ_ONCE(mm->sc_stat.footprint);
- sub = min(fp, current->total_numa_faults);
- WRITE_ONCE(mm->sc_stat.footprint, fp - sub);
-}
-#else
-static inline void exit_mm_sched_cache(struct mm_struct *mm)
-{
-}
-#endif /* CONFIG_SCHED_CACHE CONFIG_NUMA_BALANCING */
-
/*
* Turn us into a lazy TLB process if we
* aren't already..
@@ -585,7 +563,7 @@ static void exit_mm(void)
if (!mm)
return;
- exit_mm_sched_cache(mm);
+ sched_cache_exit_mm(current);
mmap_read_lock(mm);
mmgrab_lazy_tlb(mm);
diff --git a/kernel/fork.c b/kernel/fork.c
index 416758c8a3d4..10f2d05d816a 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1599,6 +1599,7 @@ static int copy_mm(u64 clone_flags, struct task_struct *tsk)
tsk->mm = mm;
tsk->active_mm = mm;
+ sched_cache_fork(tsk);
return 0;
}
@@ -1996,9 +1997,9 @@ static bool need_futex_hash_allocate_default(u64 clone_flags)
{
/*
* Allocate a default futex hash for any sibling that will
- * share the parent's mm, except vfork.
+ * share the parent's mm.
*/
- return (clone_flags & (CLONE_VM | CLONE_VFORK)) == CLONE_VM;
+ return clone_flags & CLONE_VM;
}
/*
@@ -2133,6 +2134,11 @@ __latent_entropy struct task_struct *copy_process(
p = dup_task_struct(current, node);
if (!p)
goto fork_out;
+ /*
+ * Must run before the first fallible op, so error paths never
+ * free the parent's ret_stack.
+ */
+ ftrace_graph_init_task(p);
retval = copy_exec_state(clone_flags, p);
if (retval)
goto bad_fork_free;
@@ -2159,8 +2165,6 @@ __latent_entropy struct task_struct *copy_process(
*/
p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? args->child_tid : NULL;
- ftrace_graph_init_task(p);
-
rt_mutex_init_task(p);
raw_spin_lock_init(&p->blocked_lock);
@@ -2599,6 +2603,7 @@ bad_fork_cleanup_io:
bad_fork_cleanup_namespaces:
exit_nsproxy_namespaces(p);
bad_fork_cleanup_mm:
+ sched_cache_fork_cleanup(p);
if (p->mm) {
mm_clear_owner(p->mm, p);
mmput(p->mm);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 6337da5cab9e..4edd8ca5c657 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -42,6 +42,7 @@
#include <linux/execmem.h>
#include <linux/cleanup.h>
#include <linux/wait.h>
+#include <linux/wait_bit.h>
#include <asm/sections.h>
#include <asm/cacheflush.h>
@@ -526,7 +527,8 @@ enum {
OPTIMIZER_ST_FLUSHING = 2,
};
-static DECLARE_COMPLETION(optimizer_completion);
+/* Bumped at the end of each kprobe_optimizer() pass, under 'kprobe_mutex' */
+static unsigned long optimizer_passes;
#define OPTIMIZE_DELAY 5
@@ -654,9 +656,9 @@ static void kprobe_optimizer(void)
do_free_cleaned_kprobes();
}
- /* Step 5: Kick optimizer again if needed. But if there is a flush requested, */
- if (completion_done(&optimizer_completion))
- complete(&optimizer_completion);
+ /* Step 5: Wake up flushers, and kick optimizer again if needed. */
+ optimizer_passes++;
+ wake_up_var_locked(&optimizer_passes, &kprobe_mutex);
if (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list))
kick_kprobe_optimizer(); /*normal kick*/
@@ -708,7 +710,8 @@ static void wait_for_kprobe_optimizer_locked(void)
lockdep_assert_held(&kprobe_mutex);
while (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list)) {
- init_completion(&optimizer_completion);
+ unsigned long passes = optimizer_passes;
+
/*
* Set state to OPTIMIZER_ST_FLUSHING and wake up the thread if it's
* idle. If it's already kicked, it will see the state change.
@@ -717,9 +720,12 @@ static void wait_for_kprobe_optimizer_locked(void)
OPTIMIZER_ST_FLUSHING) != OPTIMIZER_ST_FLUSHING)
wake_up(&kprobe_optimizer_wait);
- mutex_unlock(&kprobe_mutex);
- wait_for_completion(&optimizer_completion);
- mutex_lock(&kprobe_mutex);
+ /*
+ * kprobe_optimizer() holds 'kprobe_mutex' for a whole pass, which
+ * this drops while sleeping, so a new count means a full pass ran.
+ */
+ wait_var_event_mutex(&optimizer_passes,
+ optimizer_passes != passes, &kprobe_mutex);
}
}
diff --git a/kernel/nstree.c b/kernel/nstree.c
index 6d12e5900ac0..831f279d174a 100644
--- a/kernel/nstree.c
+++ b/kernel/nstree.c
@@ -533,19 +533,13 @@ DEFINE_FREE(ns_put, struct ns_common *, if (!IS_ERR_OR_NULL(_T)) ns_put(_T))
static inline struct ns_common *__must_check legitimize_ns(const struct klistns *kls,
struct ns_common *candidate)
{
- struct ns_common *ns __free(ns_put) = NULL;
-
if (!ns_requested(kls, candidate))
return NULL;
- ns = ns_get_unless_inactive(candidate);
- if (!ns)
- return NULL;
-
- if (!may_list_ns(kls, ns))
+ if (!may_list_ns(kls, candidate))
return NULL;
- return no_free_ptr(ns);
+ return ns_get_unless_inactive(candidate);
}
static ssize_t do_listns_userns(struct klistns *kls)
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index d2479c69d71a..c13f68ab7f6e 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -408,9 +408,18 @@ int hibernation_snapshot(int platform_mode)
if (error)
goto Close;
+ error = dpm_prepare(PMSG_FREEZE);
+ if (error)
+ goto Complete;
+
+ /* Preallocate image memory before freezing kernel threads and shutting down devices. */
+ error = hibernate_preallocate_memory();
+ if (error)
+ goto Complete;
+
error = freeze_kernel_threads();
if (error)
- goto Close;
+ goto Cleanup;
if (hibernation_test(TEST_FREEZER)) {
@@ -422,15 +431,6 @@ int hibernation_snapshot(int platform_mode)
goto Thaw;
}
- error = dpm_prepare(PMSG_FREEZE);
- if (error)
- goto Complete;
-
- /* Preallocate image memory before shutting down devices. */
- error = hibernate_preallocate_memory();
- if (error)
- goto Complete;
-
console_suspend_all();
pm_restrict_gfp_mask();
@@ -464,10 +464,12 @@ int hibernation_snapshot(int platform_mode)
platform_end(platform_mode);
return error;
- Complete:
- dpm_complete(PMSG_RECOVER);
Thaw:
thaw_kernel_threads();
+ Cleanup:
+ swsusp_free();
+ Complete:
+ dpm_complete(PMSG_RECOVER);
goto Close;
}
diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c
index 4b03b019cd5e..d17704fe93ae 100644
--- a/kernel/printk/nbcon.c
+++ b/kernel/printk/nbcon.c
@@ -1382,7 +1382,7 @@ bool nbcon_kthread_create(struct console *con)
return true;
kt = kthread_run(nbcon_kthread_func, con, "pr/%s%d", con->name, con->index);
- if (WARN_ON(IS_ERR(kt))) {
+ if (IS_ERR(kt)) {
con_printk(KERN_ERR, con, "failed to start printing thread\n");
return false;
}
@@ -1782,7 +1782,7 @@ bool nbcon_alloc(struct console *con)
}
rcuwait_init(&con->rcuwait);
- init_irq_work(&con->irq_work, nbcon_irq_work);
+ con->irq_work = IRQ_WORK_INIT_LAZY(nbcon_irq_work);
atomic_long_set(&ACCESS_PRIVATE(con, nbcon_prev_seq), -1UL);
nbcon_state_set(con, &state);
@@ -1837,6 +1837,8 @@ void nbcon_free(struct console *con)
/* Synchronize the kthread stop. */
lockdep_assert_console_list_lock_held();
+ irq_work_sync(&con->irq_work);
+
if (printk_kthreads_running) {
nbcon_kthread_stop(con);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 3fcdf4b4e2e5..6d3d18a50da7 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -3732,7 +3732,7 @@ static bool legacy_kthread_create(void)
lockdep_assert_console_list_lock_held();
kt = kthread_run(legacy_kthread_func, NULL, "pr/legacy");
- if (WARN_ON(IS_ERR(kt))) {
+ if (IS_ERR(kt)) {
pr_err("failed to start legacy printing thread\n");
return false;
}
diff --git a/kernel/reboot.c b/kernel/reboot.c
index f070c5c1103a..d177d89fcc33 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -13,7 +13,9 @@
#include <linux/kexec.h>
#include <linux/kmod.h>
#include <linux/kmsg_dump.h>
+#include <linux/rcupdate.h>
#include <linux/reboot.h>
+#include <linux/sched/signal.h>
#include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/syscore_ops.h>
@@ -24,8 +26,7 @@
*/
static int C_A_D = 1;
-struct pid *cad_pid;
-EXPORT_SYMBOL(cad_pid);
+struct pid __rcu *cad_pid;
#if defined(CONFIG_ARM)
#define DEFAULT_REBOOT_MODE = REBOOT_HARD
@@ -1371,10 +1372,14 @@ static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffe
{
struct ctl_table tmp_table = *table;
struct pid *new_pid;
+ struct pid *old_pid;
pid_t tmp_pid;
int r;
- tmp_pid = pid_vnr(cad_pid);
+ rcu_read_lock();
+ tmp_pid = pid_vnr(rcu_dereference(cad_pid));
+ rcu_read_unlock();
+
tmp_table.data = &tmp_pid;
r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
@@ -1385,7 +1390,13 @@ static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffe
if (!new_pid)
return -ESRCH;
- put_pid(xchg(&cad_pid, new_pid));
+ old_pid = unrcu_pointer(xchg(&cad_pid, RCU_INITIALIZER(new_pid)));
+ /*
+ * Wait for cad_pid readers before put_pid(). We cannot use
+ * call_rcu() here because free_pid() already owns pid->rcu.
+ */
+ synchronize_rcu();
+ put_pid(old_pid);
return 0;
}
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index b998ef6b87af..1fe40de6ebe3 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3351,6 +3351,8 @@ void relax_compatible_cpus_allowed_ptr(struct task_struct *p)
void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
{
unsigned int state = READ_ONCE(p->__state);
+ bool proxy_migrated = sched_proxy_exec() && p->is_blocked &&
+ task_cpu(p) != p->wake_cpu;
/*
* We should never call set_task_cpu() on a blocked task,
@@ -3386,7 +3388,12 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
*/
WARN_ON_ONCE(!cpu_online(new_cpu));
- WARN_ON_ONCE(is_migration_disabled(p));
+ /*
+ * Proxy execution can move a blocked task's scheduling context to any
+ * CPU without moving its migration-disabled execution context. The
+ * wakeup path will return the task to a CPU where it can execute.
+ */
+ WARN_ON_ONCE(is_migration_disabled(p) && !proxy_migrated);
trace_sched_migrate_task(p, new_cpu);
@@ -5776,8 +5783,8 @@ void sched_tick(void)
{
int cpu = smp_processor_id();
struct rq *rq = cpu_rq(cpu);
- /* accounting goes to the donor task */
- struct task_struct *donor;
+ /* scheduler accounting goes to the donor task */
+ struct task_struct *curr, *donor;
struct rq_flags rf;
unsigned long hw_pressure;
u64 resched_latency;
@@ -5788,9 +5795,10 @@ void sched_tick(void)
sched_clock_tick();
rq_lock(rq, &rf);
+ curr = rq->curr;
donor = rq->donor;
- psi_account_irqtime(rq, donor, NULL);
+ psi_account_irqtime(rq, curr, NULL);
update_rq_clock(rq);
hw_pressure = arch_scale_hw_pressure(cpu_of(rq));
@@ -5813,8 +5821,8 @@ void sched_tick(void)
perf_event_task_tick();
- if (donor->flags & PF_WQ_WORKER)
- wq_worker_tick(donor);
+ if (curr->flags & PF_WQ_WORKER)
+ wq_worker_tick(curr);
if (!scx_switched_all()) {
rq->idle_balance = idle_cpu(cpu);
diff --git a/kernel/sched/ext/cid.c b/kernel/sched/ext/cid.c
index bc4eee5bb4cb..4b08866d75f6 100644
--- a/kernel/sched/ext/cid.c
+++ b/kernel/sched/ext/cid.c
@@ -912,30 +912,36 @@ bool scx_cmask_empty(const struct scx_cmask *m)
/**
* scx_bpf_cid_topo - Copy out per-cid topology info
* @cid: cid to look up
- * @out__uninit: where to copy the topology info; fully written by this call
+ * @out: where to copy the topology info
+ * @out__sz: size of @out, the program's sizeof(struct scx_cid_topo)
* @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
*
- * Fill @out__uninit with the topology info for @cid. Trigger scx_error() if
- * @cid is out of range. If @cid is valid but in the no-topo section, all fields
- * are set to -1. All fields are also set to -1 when no cid tables have been
- * published yet, which a program may observe while racing the root enable.
+ * Fill @out with the topology info for @cid. Trigger scx_error() if @cid is out
+ * of range. If @cid is valid but in the no-topo section, all fields are set to
+ * -1. All fields are also set to -1 when no cid tables have been published yet,
+ * which a program may observe while racing the root enable.
+ *
+ * The program's struct may be older or newer than the kernel's. The smaller of
+ * @out__sz and the kernel's size is copied and the rest of @out is set to -1.
*/
-__bpf_kfunc void scx_bpf_cid_topo(s32 cid, struct scx_cid_topo *out__uninit,
+__bpf_kfunc void scx_bpf_cid_topo(s32 cid, struct scx_cid_topo *out, size_t out__sz,
const struct bpf_prog_aux *aux)
{
+ size_t len = min(out__sz, sizeof(*out));
struct scx_cid_topo *topo;
struct scx_sched *sch;
+ /* the error cases and fields the kernel lacks read as -1 */
+ memset(out, 0xff, out__sz);
+
guard(rcu)();
sch = scx_prog_sched(aux);
topo = rcu_dereference(scx_cid_topo);
- if (unlikely(!sch) || !cid_valid(sch, cid) || unlikely(!topo)) {
- *out__uninit = SCX_CID_TOPO_NEG;
+ if (unlikely(!sch) || !cid_valid(sch, cid) || unlikely(!topo))
return;
- }
- *out__uninit = topo[cid];
+ memcpy(out, &topo[cid], len);
}
__bpf_kfunc_end_defs();
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 51de1d8b72a1..e56c3c95018f 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -447,37 +447,45 @@ static void switch_rq_lock(struct rq *from, struct rq *to)
DEFINE_STATIC_KEY_FALSE(__scx_is_cid_type);
/**
- * scx_call_op_set_cpumask - invoke ops.set_cpumask / ops_cid.set_cmask for @task
+ * scx_fill_cmask_scratch - Build this cpu's arena cmask from @cpumask
+ * @sch: scx_sched whose scratch to fill
+ * @cpumask: cpus to translate into cids
+ *
+ * The scratch lives in BPF-writable arena memory and its header can't be
+ * trusted, so it is rewritten from kernel geometry rather than read. Caller
+ * must hold an rq lock so this cpu is the sole kernel writer for as long as the
+ * returned address is in use.
+ */
+static struct scx_cmask *scx_fill_cmask_scratch(struct scx_sched *sch,
+ const struct cpumask *cpumask)
+{
+ struct scx_cmask *kern_va = *this_cpu_ptr(sch->set_cmask_scratch);
+ struct scx_cmask_ref ref;
+
+ scx_cmask_ref_init_kern(sch, kern_va, 0, num_possible_cpus(), &ref);
+ scx_cmask_ref_from_cpumask(&ref, cpumask);
+ return kern_va;
+}
+
+/**
+ * scx_call_op_set_cpumask - Invoke the set_cpumask or set_cmask op for @task
* @sch: scx_sched being invoked
* @rq: rq to update as the currently-locked rq, or NULL
* @task: task whose affinity is changing
* @cpumask: new cpumask
*
- * For cid-form schedulers, translate @cpumask to a cmask via the per-cpu
- * scratch in cid.c and dispatch through the ops_cid union view. Caller
- * must hold @rq's rq lock so this_cpu_ptr is stable across the call.
+ * For cid-form schedulers, translate @cpumask to a cmask in the per-cpu scratch
+ * and dispatch through the ops_cid union view. Caller must hold @rq's rq lock.
*/
static inline void scx_call_op_set_cpumask(struct scx_sched *sch, struct rq *rq,
struct task_struct *task,
const struct cpumask *cpumask)
{
- if (scx_is_cid_type()) {
- struct scx_cmask *kern_va = *this_cpu_ptr(sch->set_cmask_scratch);
- struct scx_cmask_ref ref;
-
- /*
- * Build the per-cpu arena cmask from kernel geometry via @ref,
- * never reading its BPF-writable header. set_cmask()'s __arena
- * argument takes the kernel address and the struct_ops
- * trampoline rebases it into BPF's arena pointer form. The rq
- * lock makes this cpu the sole kernel writer.
- */
- scx_cmask_ref_init_kern(sch, kern_va, 0, num_possible_cpus(), &ref);
- scx_cmask_ref_from_cpumask(&ref, cpumask);
- SCX_CALL_CID_OP_TASK(sch, set_cmask, rq, task, kern_va);
- } else {
+ if (scx_is_cid_type())
+ SCX_CALL_CID_OP_TASK(sch, set_cmask, rq, task,
+ scx_fill_cmask_scratch(sch, cpumask));
+ else
SCX_CALL_OP_TASK(sch, set_cpumask, rq, task, cpumask);
- }
}
enum scx_dsq_iter_flags {
@@ -1499,27 +1507,22 @@ static inline bool task_scx_migrating(struct task_struct *p)
return p->scx.sticky_cpu >= 0;
}
-/*
- * Call ops.dequeue() if the task is in BPF custody and not migrating.
- * Clears %SCX_TASK_IN_CUSTODY when the callback is invoked.
- */
-static void call_task_dequeue(struct scx_sched *sch, struct rq *rq,
- struct task_struct *p, u64 deq_flags)
+/* Must be called under the lock serializing @p's custody transfers. */
+static bool task_leave_custody(struct task_struct *p)
{
if (!(p->scx.flags & SCX_TASK_IN_CUSTODY) || task_scx_migrating(p))
- return;
-
- if (SCX_HAS_OP(sch, dequeue))
- SCX_CALL_OP_TASK(sch, dequeue, rq, p, deq_flags);
+ return false;
p->scx.flags &= ~SCX_TASK_IN_CUSTODY;
+ return true;
}
static void rq_owned_post_enq(struct scx_sched *sch, struct rq *rq,
struct scx_dispatch_q *dsq, struct task_struct *p,
u64 enq_flags)
{
- call_task_dequeue(sch, rq, p, 0);
+ if (task_leave_custody(p) && SCX_HAS_OP(sch, dequeue))
+ SCX_CALL_OP_TASK(sch, dequeue, rq, p, 0);
/*
* Only local inserts get the wakeup treatment below. Rejects kick the
@@ -1705,20 +1708,28 @@ static void scx_dispatch_enqueue(struct scx_sched *sch, struct rq *rq,
if (is_rq_owned) {
rq_owned_post_enq(sch, rq, dsq, p, enq_flags);
} else {
+ bool call_dequeue = false;
+
/*
* Global and bypass DSQs are terminal - the task leaves the
- * scheduler's custody, so ops.dequeue() fires here. It can run
+ * scheduler's custody, so ops.dequeue() fires. It can run
* without @p's rq lock (finish_dispatch() passes the dispatch
* rq); that's safe because dequeue_task_scx() waits on
* SCX_OPSS_DISPATCHING (see the ops_state note above) and so
* can't race it. A non-terminal DSQ keeps the task in custody.
+ * The custody transfer happens under @dsq->lock so that later
+ * consumers see the flag clear; the callback runs after
+ * @dsq->lock is dropped because it may lock a DSQ itself.
*/
if (dsq->id == SCX_DSQ_GLOBAL || dsq->id == SCX_DSQ_BYPASS)
- call_task_dequeue(sch, rq, p, 0);
+ call_dequeue = task_leave_custody(p);
else
p->scx.flags |= SCX_TASK_IN_CUSTODY;
raw_spin_unlock(&dsq->lock);
+
+ if (call_dequeue && SCX_HAS_OP(sch, dequeue))
+ SCX_CALL_OP_TASK(sch, dequeue, rq, p, 0);
}
/*
@@ -2141,7 +2152,12 @@ static void enqueue_task_scx(struct rq *rq, struct task_struct *p, int core_enq_
int sticky_cpu = p->scx.sticky_cpu;
u64 enq_flags = core_enq_flags | rq->scx.remote_activate_enq_flags;
- if (enq_flags & ENQUEUE_WAKEUP)
+ /*
+ * SCX_RQ_IN_WAKEUP promises a task_woken_scx() call once this enqueue
+ * returns. Only the core's wakeup path delivers one. The flags stashed
+ * for a remote activation may carry the wakeup bit without it.
+ */
+ if (core_enq_flags & ENQUEUE_WAKEUP)
rq->scx.flags |= SCX_RQ_IN_WAKEUP;
/*
@@ -2210,7 +2226,7 @@ retry:
/*
* A queued task must always be in BPF scheduler's custody. If
* SCX_TASK_IN_CUSTODY is clear, finish_dispatch() on another
- * CPU has already passed call_task_dequeue() (which clears the
+ * CPU has already passed task_leave_custody() (which clears the
* flag), but has not yet written SCX_OPSS_NONE. That final
* store does not require this rq's lock, so retrying with
* cpu_relax() is bounded: we will observe NONE (or DISPATCHING,
@@ -2258,7 +2274,8 @@ retry:
* NONE but the task may still have %SCX_TASK_IN_CUSTODY set until
* it is enqueued on the destination.
*/
- call_task_dequeue(sch, rq, p, deq_flags);
+ if (task_leave_custody(p) && SCX_HAS_OP(sch, dequeue))
+ SCX_CALL_OP_TASK(sch, dequeue, rq, p, deq_flags);
}
static bool dequeue_task_scx(struct rq *rq, struct task_struct *p, int core_deq_flags)
@@ -2374,14 +2391,10 @@ static void wakeup_preempt_scx(struct rq *rq, struct task_struct *p, int wake_fl
}
void scx_move_local_task_to_local_dsq(struct scx_sched *sch, struct task_struct *p,
- u64 enq_flags, struct scx_dispatch_q *src_dsq,
- struct rq *dst_rq)
+ u64 enq_flags, struct rq *dst_rq)
{
struct scx_dispatch_q *dst_dsq = scx_resolve_local_dsq(sch, dst_rq, p, &enq_flags);
- /* @p is on @dst_rq, an rq-owned @src_dsq is covered by the rq lock */
- if (!dsq_is_rq_owned(src_dsq))
- lockdep_assert_held(&src_dsq->lock);
lockdep_assert_rq_held(dst_rq);
WARN_ON_ONCE(p->scx.holding_cpu >= 0);
@@ -2629,8 +2642,8 @@ static struct rq *move_task_between_dsqs(struct scx_sched *sch,
/* @p is going from a non-local DSQ to a local DSQ */
if (src_rq == dst_rq) {
scx_task_unlink_from_dsq(p, src_dsq);
- scx_move_local_task_to_local_dsq(sch, p, enq_flags, src_dsq, dst_rq);
raw_spin_unlock(&src_dsq->lock);
+ scx_move_local_task_to_local_dsq(sch, p, enq_flags, dst_rq);
} else {
raw_spin_unlock(&src_dsq->lock);
move_remote_task_to_local_dsq(sch, p, enq_flags, src_rq, dst_rq);
@@ -2680,8 +2693,8 @@ retry:
if (rq == task_rq) {
scx_task_unlink_from_dsq(p, dsq);
- scx_move_local_task_to_local_dsq(sch, p, enq_flags, dsq, rq);
raw_spin_unlock(&dsq->lock);
+ scx_move_local_task_to_local_dsq(sch, p, enq_flags, rq);
return true;
}
@@ -2919,7 +2932,7 @@ static inline void maybe_queue_balance_callback(struct rq *rq)
static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev)
{
- struct scx_sched *sch = scx_root_protected_live();
+ struct scx_sched *root_sch = scx_root_protected_live();
enum scx_dsp_verdict verdict;
s32 cpu = cpu_of(rq);
@@ -2928,7 +2941,7 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
scx_process_sync_ecaps(rq, prev);
- if ((sch->ops.flags & SCX_OPS_HAS_CPU_PREEMPT) &&
+ if ((root_sch->ops.flags & SCX_OPS_HAS_CPU_PREEMPT) &&
unlikely(rq->scx.cpu_released)) {
/*
* If the previous sched_class for the current CPU was not SCX,
@@ -2936,8 +2949,8 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
* core. This callback complements ->cpu_release(), which is
* emitted in switch_class().
*/
- if (sch->ops.cpu_acquire)
- SCX_CALL_OP(sch, cpu_acquire, rq, cpu, NULL);
+ if (root_sch->ops.cpu_acquire)
+ SCX_CALL_OP(root_sch, cpu_acquire, rq, cpu, NULL);
rq->scx.cpu_released = false;
}
@@ -2955,7 +2968,7 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
* test.
*/
if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice &&
- !scx_bypassing(sch, cpu)) {
+ !scx_bypassing(scx_task_sched(prev), cpu)) {
verdict = SCX_DSP_PREV;
goto has_tasks;
}
@@ -2967,20 +2980,25 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
goto has_tasks;
}
- verdict = scx_dispatch_sched(sch, rq, prev, false);
+ verdict = scx_dispatch_sched(root_sch, rq, prev, false);
if (verdict != SCX_DSP_NONE)
goto has_tasks;
/*
- * Didn't find another task to run. Keep running @prev unless
- * %SCX_OPS_ENQ_LAST is in effect.
+ * Didn't find another task to run. Keep running @prev unless its own
+ * scheduler set %SCX_OPS_ENQ_LAST and takes the enqueue instead, see
+ * put_prev_task_scx(). Read the scheduler here as the dispatch above
+ * may have dropped the rq lock while @prev changed class or scheduler.
*/
- if ((prev->scx.flags & SCX_TASK_QUEUED) &&
- (!(sch->ops.flags & SCX_OPS_ENQ_LAST) || scx_bypassing(sch, cpu)) &&
- scx_task_can_stay_on_cpu(rq, prev)) {
- __scx_add_event(sch, SCX_EV_DISPATCH_KEEP_LAST, 1);
- verdict = SCX_DSP_PREV;
- goto has_tasks;
+ if (prev->scx.flags & SCX_TASK_QUEUED) {
+ struct scx_sched *prev_sch = scx_task_sched(prev);
+
+ if ((!(prev_sch->ops.flags & SCX_OPS_ENQ_LAST) ||
+ scx_bypassing(prev_sch, cpu)) && scx_task_can_stay_on_cpu(rq, prev)) {
+ __scx_add_event(prev_sch, SCX_EV_DISPATCH_KEEP_LAST, 1);
+ verdict = SCX_DSP_PREV;
+ goto has_tasks;
+ }
}
rq->scx.flags &= ~SCX_RQ_IN_DISPATCH;
return SCX_DSP_NONE;
@@ -3624,8 +3642,12 @@ static void set_cpus_allowed_scx(struct task_struct *p,
*
* Fine-grained memory write control is enforced by BPF making the const
* designation pointless. Cast it away when calling the operation.
+ *
+ * The cid form receives the initial mask when the task is enabled and
+ * hears about changes only afterwards, see struct scx_enable_args.
*/
- if (SCX_HAS_OP(sch, set_cpumask))
+ if (SCX_HAS_OP(sch, set_cpumask) &&
+ (!scx_is_cid_type() || scx_get_task_state(p) == SCX_TASK_ENABLED))
scx_call_op_set_cpumask(sch, task_rq(p), p, (struct cpumask *)p->cpus_ptr);
}
@@ -3665,8 +3687,20 @@ static void handle_hotplug(struct rq *rq, bool online)
s16 *tbl = rcu_dereference_check(scx_cpu_to_cid_tbl,
lockdep_is_cpus_held());
- if (tbl)
+ if (tbl) {
+ struct scx_sched *pos;
+
cpu_or_cid = tbl[cpu];
+
+ guard(raw_spinlock_irqsave)(&scx_sched_lock);
+ list_for_each_entry(pos, &scx_sched_all, all) {
+ struct scx_cmask *mask = pos->online_cmask;
+
+ if (mask)
+ __assign_bit(cpu_or_cid, (unsigned long *)mask->bits,
+ online);
+ }
+ }
}
if (online && SCX_HAS_OP(sch, cpu_online))
@@ -3922,8 +3956,27 @@ static void __scx_enable_task(struct scx_sched *sch, struct task_struct *p)
p->scx.weight = sched_weight_to_cgroup(weight);
- if (SCX_HAS_OP(sch, enable))
- SCX_CALL_OP_TASK(sch, enable, rq, p);
+ if (SCX_HAS_OP(sch, enable)) {
+ if (scx_is_cid_type()) {
+ struct scx_cmask *cmask = scx_fill_cmask_scratch(sch, p->cpus_ptr);
+ struct scx_enable_args args = {
+ .cmask_arena_addr = scx_kaddr_to_arena(sch, cmask),
+ };
+
+ SCX_CALL_CID_OP_TASK(sch, enable, rq, p, &args);
+ } else {
+ SCX_CALL_OP_TASK(sch, enable, rq, p);
+ }
+ }
+
+ /*
+ * The initial mask also goes out through set_cmask() so a scheduler can
+ * track affinity there alone, and before set_weight() so that the mask
+ * is in place when weight-dependent state is derived, see struct
+ * scx_enable_args.
+ */
+ if (scx_is_cid_type() && SCX_HAS_OP(sch, set_cpumask))
+ scx_call_op_set_cpumask(sch, rq, p, p->cpus_ptr);
if (SCX_HAS_OP(sch, set_weight))
SCX_CALL_OP_TASK(sch, set_weight, rq, p, p->scx.weight);
@@ -4266,9 +4319,10 @@ static void switching_to_scx(struct rq *rq, struct task_struct *p)
/*
* set_cpus_allowed_scx() is not called while @p is associated with a
- * different scheduler class. Keep the BPF scheduler up-to-date.
+ * different scheduler class. Keep the BPF scheduler up-to-date. The cid
+ * form gets its mask from scx_enable_task().
*/
- if (SCX_HAS_OP(sch, set_cpumask))
+ if (!scx_is_cid_type() && SCX_HAS_OP(sch, set_cpumask))
scx_call_op_set_cpumask(sch, rq, p, (struct cpumask *)p->cpus_ptr);
}
@@ -4387,6 +4441,17 @@ static bool local_task_should_reenq(struct rq *rq, struct task_struct *p,
return *reenq_flags & SCX_REENQ_ANY;
}
+/*
+ * The dispatcher stores the final ops_state after dropping the DSQ lock, so @p
+ * can be found on a DSQ while still %SCX_OPSS_DISPATCHING. Reenqueueing @p
+ * before that store lands would have it clobber the new %SCX_OPSS_QUEUED.
+ */
+void scx_reenq_wait_dispatching(struct task_struct *p)
+{
+ if (unlikely(atomic_long_read_acquire(&p->scx.ops_state) == SCX_OPSS_DISPATCHING))
+ wait_ops_state(p, SCX_OPSS_DISPATCHING);
+}
+
static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags)
{
LIST_HEAD(tasks);
@@ -4430,6 +4495,7 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags)
if (!local_task_should_reenq(rq, p, &reenq_flags, &reason))
continue;
+ scx_reenq_wait_dispatching(p);
scx_dispatch_dequeue(rq, p);
if (WARN_ON_ONCE(p->scx.flags & SCX_TASK_REENQ_REASON_MASK))
@@ -4553,6 +4619,7 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag
}
/* @p is on @dsq, its rq and @dsq are locked */
+ scx_reenq_wait_dispatching(p);
dispatch_dequeue_locked(p, dsq);
raw_spin_unlock(&dsq->lock);
@@ -4766,7 +4833,8 @@ int scx_tg_online(struct task_group *tg)
{ .weight = tg->scx.weight,
.bw_period_us = tg->scx.bw_period_us,
.bw_quota_us = tg->scx.bw_quota_us,
- .bw_burst_us = tg->scx.bw_burst_us };
+ .bw_burst_us = tg->scx.bw_burst_us,
+ .sched_idle = tg->scx.idle };
ret = SCX_CALL_OP_RET(sch, cgroup_init,
NULL, tg->css.cgroup, &args);
@@ -4932,7 +5000,8 @@ void scx_group_set_idle(struct task_group *tg, bool idle)
percpu_down_read(&scx_cgroup_ops_rwsem);
sch = scx_tg_knob_sched(tg);
- if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle))
+ if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle) &&
+ tg->scx.idle != idle)
SCX_CALL_OP(sch, cgroup_set_idle, NULL, tg_cgrp(tg), idle);
/* Update the task group's idle state */
@@ -5187,6 +5256,7 @@ static int scx_cgroup_init(struct scx_sched *sch)
.bw_period_us = tg->scx.bw_period_us,
.bw_quota_us = tg->scx.bw_quota_us,
.bw_burst_us = tg->scx.bw_burst_us,
+ .sched_idle = tg->scx.idle,
};
ret = SCX_CALL_OP_RET(sch, cgroup_init, NULL, css->cgroup, &args);
@@ -5272,12 +5342,17 @@ static void free_exit_info(struct scx_exit_info *ei);
static const char *scx_exit_reason(enum scx_exit_kind kind);
static bool scx_claim_exit(struct scx_sched *sch, enum scx_exit_kind kind);
-s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch)
+s32 scx_alloc_kern_arena_objs(struct scx_sched *sch)
{
size_t size = struct_size_t(struct scx_cmask, bits,
SCX_CMASK_NR_WORDS(num_possible_cpus()));
+ struct scx_cmask *online;
+ struct scx_cmask_ref ref;
int cpu;
+ /* hotplug stays excluded until the online mask is published */
+ lockdep_assert_cpus_held();
+
if (!sch->is_cid_type || !sch->arena_pool)
return 0;
@@ -5293,15 +5368,28 @@ s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch)
return -ENOMEM;
scx_cmask_init(*slot, 0, num_possible_cpus());
}
+
+ /* pack the online mask alongside the scratch masks */
+ online = scx_arena_alloc(sch, size);
+ if (!online)
+ return -ENOMEM;
+
+ scoped_guard(rcu) {
+ scx_cmask_ref_init_kern(sch, online, 0, num_possible_cpus(), &ref);
+ scx_cmask_ref_from_cpumask(&ref, cpu_active_mask);
+ }
+ sch->online_cmask = online;
+
return 0;
}
-static void scx_set_cmask_scratch_free(struct scx_sched *sch)
+static void scx_free_kern_arena_objs(struct scx_sched *sch)
{
size_t size = struct_size_t(struct scx_cmask, bits,
SCX_CMASK_NR_WORDS(num_possible_cpus()));
int cpu;
+ scx_arena_free(sch, sch->online_cmask, size);
if (!sch->set_cmask_scratch)
return;
@@ -5388,7 +5476,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
rhashtable_free_and_destroy(&sch->dsq_hash, NULL, NULL);
free_exit_info(sch->exit_info);
- scx_set_cmask_scratch_free(sch);
+ scx_free_kern_arena_objs(sch);
scx_arena_pool_destroy(sch);
if (sch->arena_map)
bpf_map_put(sch->arena_map);
@@ -7508,22 +7596,24 @@ static void scx_root_enable_workfn(struct kthread_work *work)
#ifdef CONFIG_EXT_SUB_SCHED
cgroup_get(cgrp);
#endif
+ /*
+ * Transition to ENABLING to arm the disable path. Allocation failure
+ * still unwinds locally. Full disabling on failure applies only after
+ * scx_alloc_and_add_sched() succeeds.
+ */
+ WARN_ON_ONCE(scx_set_enable_state(SCX_ENABLING) != SCX_DISABLED);
+ WARN_ON_ONCE(scx_root);
+
sch = scx_alloc_and_add_sched(cmd, cgrp, NULL);
if (IS_ERR(sch)) {
ret = PTR_ERR(sch);
+ WARN_ON_ONCE(scx_set_enable_state(SCX_DISABLED) != SCX_ENABLING);
goto err_free_tid_hash;
}
if (sch->is_cid_type)
static_branch_enable(&__scx_is_cid_type);
- /*
- * Transition to ENABLING and clear exit info to arm the disable path.
- * Failure triggers full disabling from here on.
- */
- WARN_ON_ONCE(scx_set_enable_state(SCX_ENABLING) != SCX_DISABLED);
- WARN_ON_ONCE(scx_root);
-
atomic_long_set(&scx_nr_rejected, 0);
for_each_possible_cpu(cpu) {
@@ -7591,7 +7681,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
goto err_disable;
}
- ret = scx_set_cmask_scratch_alloc(sch);
+ ret = scx_alloc_kern_arena_objs(sch);
if (ret) {
cpus_read_unlock();
goto err_disable;
@@ -8316,10 +8406,11 @@ static struct bpf_struct_ops bpf_sched_ext_ops = {
/*
* cid-form cfi stubs. Stubs whose signatures match the cpu-form (param types
* identical, only param names differ across structs) are reused. Some need
- * fresh stubs, set_cmask due to an argument type difference and the sub-sched
- * notifiers because no cpu-form stub exists to reuse.
+ * fresh stubs, set_cmask and enable due to argument differences and the
+ * sub-sched notifiers because no cpu-form stub exists to reuse.
*/
static void sched_ext_ops_cid__set_cmask(struct task_struct *p, const struct scx_cmask *cmask__arena) {}
+static void sched_ext_ops_cid__enable(struct task_struct *p, struct scx_enable_args *args) {}
static void sched_ext_ops__sub_caps_updated(const struct scx_cmask *cmask__arena, u64 caps) {}
static void sched_ext_ops__sub_ecaps_updated(s32 cid, u64 before, u64 after) {}
@@ -8340,7 +8431,7 @@ static struct sched_ext_ops_cid __bpf_ops_sched_ext_ops_cid = {
.update_idle = sched_ext_ops__update_idle,
.init_task = sched_ext_ops__init_task,
.exit_task = sched_ext_ops__exit_task,
- .enable = sched_ext_ops__enable,
+ .enable = sched_ext_ops_cid__enable,
.disable = sched_ext_ops__disable,
#ifdef CONFIG_EXT_GROUP_SCHED
.cpuctl_init = sched_ext_ops__cgroup_init,
@@ -8946,10 +9037,17 @@ __bpf_kfunc void scx_bpf_dsq_insert_vtime(struct task_struct *p, u64 dsq_id,
#ifdef CONFIG_EXT_SUB_SCHED
/*
* Disallow if any sub-scheds are attached. There is no way to tell
- * which scheduler called us, just error out @p's scheduler.
+ * which scheduler called us, so error out @p's scheduler -- read it
+ * under RCU as @p's locks aren't necessarily held here. @p may be a
+ * task past sched_ext_dead() or an idle task, in which case its
+ * scheduler can't be determined and there is nothing obviously wrong
+ * to report; just refuse the call.
*/
if (unlikely(!list_empty(&sch->children))) {
- scx_error(scx_task_sched(p), "__scx_bpf_dsq_insert_vtime() must be used");
+ struct scx_sched *tsch = scx_task_sched_rcu(p);
+
+ if (tsch)
+ scx_error(tsch, "__scx_bpf_dsq_insert_vtime() must be used");
return;
}
#endif
@@ -10321,7 +10419,8 @@ __bpf_kfunc u32 scx_bpf_nr_cids(void)
* hotplug, which lets schedulers treat [0, nr_online_cids) as the online
* range. Schedulers that prefer to handle hotplug without a restart should
* install a custom mapping via scx_bpf_cid_override() and track onlining
- * through the ops.cid_online / ops.cid_offline callbacks.
+ * through the ops.cid_online / ops.cid_offline callbacks, starting from the
+ * mask scx_bpf_online_cmask() returns.
*/
__bpf_kfunc u32 scx_bpf_nr_online_cids(void)
{
@@ -10329,6 +10428,36 @@ __bpf_kfunc u32 scx_bpf_nr_online_cids(void)
}
/**
+ * scx_bpf_online_cmask - Return the online cid mask in the scheduler arena
+ * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
+ *
+ * Return a kernel-maintained cmask covering [0, scx_bpf_nr_cids()), or NULL if
+ * the calling program is not associated with a live cid-form scheduler or the
+ * mask is not allocated yet, as in ops.init_cids(). Treat the mask as read-only
+ * even though arena memory stays writable by the BPF scheduler. The mask
+ * follows the SCX hotplug notifications: a cid's bit is updated before
+ * ops.cid_online/offline() runs for it. The pointer is valid from ops.init()
+ * through ops.exit(). Root ops.init() runs with hotplug excluded. Other
+ * contexts can observe concurrent updates.
+ */
+__bpf_kfunc const void *scx_bpf_online_cmask(const struct bpf_prog_aux *aux)
+{
+ struct scx_sched *sch;
+ struct scx_cmask *online;
+
+ guard(rcu)();
+
+ sch = scx_prog_sched(aux);
+ if (unlikely(!sch))
+ return NULL;
+ online = sch->online_cmask;
+ if (unlikely(!online))
+ return NULL;
+
+ return (void *)scx_kaddr_to_arena(sch, online);
+}
+
+/**
* scx_bpf_this_cid - Return the cid of the CPU this program is running on
*
* cid-addressed equivalent of bpf_get_smp_processor_id() for scx programs.
@@ -10691,6 +10820,7 @@ BTF_ID_FLAGS(func, scx_bpf_nr_node_ids)
BTF_ID_FLAGS(func, scx_bpf_nr_cpu_ids)
BTF_ID_FLAGS(func, scx_bpf_nr_cids)
BTF_ID_FLAGS(func, scx_bpf_nr_online_cids)
+BTF_ID_FLAGS(func, scx_bpf_online_cmask, KF_IMPLICIT_ARGS | KF_ARENA_RET)
BTF_ID_FLAGS(func, scx_bpf_this_cid)
BTF_ID_FLAGS(func, scx_bpf_get_possible_cpumask, KF_ACQUIRE)
BTF_ID_FLAGS(func, scx_bpf_get_online_cpumask, KF_ACQUIRE)
diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c
index d2973fb3af6d..aa9fb6de0ad6 100644
--- a/kernel/sched/ext/idle.c
+++ b/kernel/sched/ext/idle.c
@@ -1142,10 +1142,17 @@ __bpf_kfunc s32 scx_bpf_select_cpu_and(struct task_struct *p, s32 prev_cpu, u64
#ifdef CONFIG_EXT_SUB_SCHED
/*
* Disallow if any sub-scheds are attached. There is no way to tell
- * which scheduler called us, just error out @p's scheduler.
+ * which scheduler called us, so error out @p's scheduler -- read it
+ * under RCU as @p's locks aren't necessarily held here. @p may be a
+ * task past sched_ext_dead() or an idle task, in which case its
+ * scheduler can't be determined and there is nothing obviously wrong
+ * to report; just refuse the call.
*/
if (unlikely(!list_empty(&sch->children))) {
- scx_error(scx_task_sched(p), "__scx_bpf_select_cpu_and() must be used");
+ struct scx_sched *tsch = scx_task_sched_rcu(p);
+
+ if (tsch)
+ scx_error(tsch, "__scx_bpf_select_cpu_and() must be used");
return -EINVAL;
}
#endif
diff --git a/kernel/sched/ext/inlines.h b/kernel/sched/ext/inlines.h
index ed423bcc26b8..2ff5479334cb 100644
--- a/kernel/sched/ext/inlines.h
+++ b/kernel/sched/ext/inlines.h
@@ -129,8 +129,10 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
* scheduler's ops.dispatch() doesn't yield any tasks.
*/
if (scx_bypass_dsp_enabled(sch) &&
- scx_consume_dispatch_q(sch, rq, scx_bypass_dsq(sch, cpu), 0))
+ scx_consume_dispatch_q(sch, rq, scx_bypass_dsq(sch, cpu), 0)) {
+ __scx_add_event(sch, SCX_EV_SUB_BYPASS_DISPATCH, 1);
return SCX_DSP_LOCAL;
+ }
return SCX_DSP_NONE;
}
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 0967b99a4948..1df8f583b0ec 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -250,6 +250,31 @@ struct scx_exit_task_args {
bool cancelled;
};
+/**
+ * struct scx_enable_args - Argument container for cid-form ops.enable()
+ * @cmask_arena_addr: BPF arena address of the cmask of cids the task may run on
+ *
+ * @cmask_arena_addr is the task's affinity as it enters the scheduler.
+ * set_cmask() delivers the same mask right after enable(), before set_weight()
+ * and the first enqueue, then every affinity change afterwards, and is never
+ * called before enable(). A scheduler may therefore track affinity in
+ * set_cmask() alone.
+ *
+ * The kernel builds the mask in the scheduler arena from its own geometry, so
+ * the header is valid regardless of what the scheduler last wrote there. The
+ * memory is per-cpu scratch reused once the callback returns: copy the bits
+ * out, don't keep the address. The set_cmask() argument follows the same rules.
+ *
+ * The address is a plain value rather than a typed pointer because BTF can't
+ * mark a struct member as an arena pointer yet and a pointer member would reach
+ * the program typed as a kernel pointer. Cast it to struct scx_cmask __arena *
+ * before use. Once arena members can be typed, a typed alias will join this
+ * field in an anonymous union at the same offset.
+ */
+struct scx_enable_args {
+ u64 cmask_arena_addr;
+};
+
/* argument container for ops.cgroup_init() */
struct scx_cgroup_init_args {
/* the weight of the cgroup [1..10000] */
@@ -259,6 +284,9 @@ struct scx_cgroup_init_args {
u64 bw_period_us;
u64 bw_quota_us;
u64 bw_burst_us;
+
+ /* whether the cgroup is configured SCHED_IDLE via cpu.idle */
+ bool sched_idle;
};
enum scx_cpu_preempt_reason {
@@ -569,6 +597,12 @@ struct sched_ext_ops {
*
* Specify the %SCX_OPS_KEEP_BUILTIN_IDLE flag to keep the built-in idle
* tracking.
+ *
+ * Only actual transitions are reported. A CPU that is claimed with an
+ * idle pick and kicked but dispatches no task returns to idle without a
+ * transition. A scheduler tracking idle CPUs itself must restore the
+ * idle state from ops.dispatch() when it returns without the next task
+ * to run.
*/
void (*update_idle)(s32 cpu, bool idle);
@@ -1028,6 +1062,7 @@ struct sched_ext_ops {
* - dispatch -> dispatch (cpu arg is now cid)
* - update_idle -> update_idle (cpu arg is now cid)
* - set_cpumask -> set_cmask (cmask instead of cpumask)
+ * - enable -> enable (takes struct scx_enable_args)
* - cpu_online -> cid_online
* - cpu_offline -> cid_offline
* - dump_cpu -> dump_cid
@@ -1061,7 +1096,7 @@ struct sched_ext_ops_cid {
struct scx_init_task_args *args);
void (*exit_task)(struct task_struct *p,
struct scx_exit_task_args *args);
- void (*enable)(struct task_struct *p);
+ void (*enable)(struct task_struct *p, struct scx_enable_args *args);
void (*disable)(struct task_struct *p);
void (*dump)(struct scx_dump_ctx *ctx);
void (*dump_cid)(struct scx_dump_ctx *ctx, s32 cid, bool idle);
@@ -1524,7 +1559,8 @@ struct scx_sched {
* by BUILD_BUG_ON in scx_init()). The anonymous union lets the kernel
* access either view of the same storage without function-pointer
* casts: use .ops for cpu-form and shared fields, .ops_cid for the
- * cid-renamed callbacks (set_cmask, select_cid, cid_online, ...).
+ * callbacks whose cid-form signature differs (set_cmask, enable,
+ * select_cid, cid_online, ...).
*/
union {
struct sched_ext_ops ops;
@@ -1547,11 +1583,12 @@ struct scx_sched {
uintptr_t arena_kern_base;
/*
- * Per-CPU arena cmask used by scx_call_op_set_cpumask() to hand a cmask
- * to ops_cid.set_cmask(). The kernel writes through the stored kern_va
- * and passes it to the callback's __arena argument.
+ * Per-CPU arena cmask the kernel fills from a task's cpumask and hands
+ * to ops_cid.enable() and ops_cid.set_cmask(). The stored pointers are
+ * the kernel addresses.
*/
struct scx_cmask * __percpu *set_cmask_scratch;
+ struct scx_cmask *online_cmask;
DECLARE_BITMAP(has_op, SCX_OPI_END);
@@ -1659,6 +1696,19 @@ static inline void *scx_arena_to_kaddr(struct scx_sched *sch, const void *bpf_pt
return (void *)(sch->arena_kern_base + (u32)(uintptr_t)bpf_ptr);
}
+/**
+ * scx_kaddr_to_arena - Translate a kernel arena address to the BPF form
+ * @sch: scheduler whose arena hosts @kaddr
+ * @kaddr: kernel address inside @sch's arena
+ *
+ * __arena callback arguments need no translation. Addresses handed to BPF any
+ * other way, such as struct fields and kfunc return values, go through this.
+ */
+static inline uintptr_t scx_kaddr_to_arena(struct scx_sched *sch, const void *kaddr)
+{
+ return (uintptr_t)kaddr - sch->arena_kern_base;
+}
+
enum scx_wake_flags {
/* expose select WF_* flags as enums */
SCX_WAKE_FORK = WF_FORK,
@@ -2055,8 +2105,7 @@ void scx_dispatch_dequeue(struct rq *rq, struct task_struct *p);
void scx_do_enqueue_task(struct rq *rq, struct task_struct *p, u64 enq_flags,
int sticky_cpu);
void scx_move_local_task_to_local_dsq(struct scx_sched *sch, struct task_struct *p,
- u64 enq_flags, struct scx_dispatch_q *src_dsq,
- struct rq *dst_rq);
+ u64 enq_flags, struct rq *dst_rq);
bool scx_consume_dispatch_q(struct scx_sched *sch, struct rq *rq,
struct scx_dispatch_q *dsq, u64 enq_flags);
bool scx_consume_global_dsq(struct scx_sched *sch, struct rq *rq);
@@ -2068,6 +2117,7 @@ void scx_kick_cpu(struct scx_sched *sch, s32 cpu, u64 flags);
u64 __scx_bpf_now(struct rq *rq);
void schedule_dsq_reenq(struct scx_sched *sch, struct scx_dispatch_q *dsq,
u64 reenq_flags, struct rq *locked_rq);
+void scx_reenq_wait_dispatching(struct task_struct *p);
int __scx_init_task(struct scx_sched *sch, struct task_struct *p,
struct cgroup *cgrp, bool fork);
void scx_enable_task(struct scx_sched *sch, struct task_struct *p);
@@ -2078,7 +2128,7 @@ void scx_disable_and_exit_task(struct scx_sched *sch, struct task_struct *p);
void scx_cgroup_lock(void);
void scx_cgroup_unlock(void);
#endif
-s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch);
+s32 scx_alloc_kern_arena_objs(struct scx_sched *sch);
void scx_disable_bypass_dsp(struct scx_sched *sch);
void scx_bypass(struct scx_sched *sch, bool bypass);
s32 scx_link_sched(struct scx_sched *sch);
@@ -2292,9 +2342,9 @@ do { \
} while (0)
/*
- * Dispatch a task op through the cid-form ops_cid table. Only set_cmask() needs
- * this: it takes an arena cmask address instead of a cpumask, so it cannot be
- * invoked via its cpu-form set_cpumask() slot.
+ * Dispatch a task op through the cid-form ops_cid table, for the ops whose
+ * cid-form signature differs from the cpu-form slot: set_cmask() takes an arena
+ * cmask instead of a cpumask and enable() takes scx_enable_args.
*/
#define SCX_CALL_CID_OP_TASK(sch, op, locked_rq, task, args...) \
__SCX_CALL_OP_TASK(sch, ops_cid, op, locked_rq, task, ##args)
diff --git a/kernel/sched/ext/sub.c b/kernel/sched/ext/sub.c
index 9e7040482bde..0472eaf41c7c 100644
--- a/kernel/sched/ext/sub.c
+++ b/kernel/sched/ext/sub.c
@@ -555,8 +555,8 @@ static void scx_rescue_timerfn(struct timer_list *timer)
scx.dsq_list.node);
scx_task_unlink_from_dsq(p, &rq->scx.rescue.dsq);
scx_rescue_admit(rq, p, slice);
- scx_move_local_task_to_local_dsq(scx_task_sched(p), p, SCX_ENQ_IGNORE_CAPS,
- &rq->scx.rescue.dsq, rq);
+ scx_move_local_task_to_local_dsq(scx_task_sched(p), p,
+ SCX_ENQ_IGNORE_CAPS, rq);
if (sched_class_above(&ext_sched_class, rq->curr->sched_class))
resched_curr(rq);
} else if (p->scx.dsq && rq->scx.rescue.budget > 2 * scx_rescue_quantum_ns) {
@@ -572,7 +572,7 @@ static void scx_rescue_timerfn(struct timer_list *timer)
scx_task_unlink_from_dsq(p, &rq->scx.local_dsq);
scx_move_local_task_to_local_dsq(scx_task_sched(p), p,
SCX_ENQ_HEAD | SCX_ENQ_PREEMPT | SCX_ENQ_IGNORE_CAPS,
- &rq->scx.local_dsq, rq);
+ rq);
}
out_arm:
scx_rescue_timer_arm(rq);
@@ -596,8 +596,8 @@ void scx_rescue_flush(struct rq *rq)
/* and flush out all pending ones */
list_for_each_entry_safe(p, n, &rq->scx.rescue.dsq.list, scx.dsq_list.node) {
scx_task_unlink_from_dsq(p, &rq->scx.rescue.dsq);
- scx_move_local_task_to_local_dsq(scx_task_sched(p), p, SCX_ENQ_IGNORE_CAPS,
- &rq->scx.rescue.dsq, rq);
+ scx_move_local_task_to_local_dsq(scx_task_sched(p), p,
+ SCX_ENQ_IGNORE_CAPS, rq);
}
timer_delete(&rq->scx.rescue.timer);
@@ -801,6 +801,7 @@ void scx_reenq_reject(struct rq *rq)
if (WARN_ON_ONCE(p->migration_pending))
continue;
+ scx_reenq_wait_dispatching(p);
scx_dispatch_dequeue(rq, p);
if (WARN_ON_ONCE(p->scx.flags & SCX_TASK_REENQ_REASON_MASK))
@@ -1361,6 +1362,7 @@ static s32 scx_cgroup_claim_subtree(struct scx_sched *sch)
.bw_period_us = tg->scx.bw_period_us,
.bw_quota_us = tg->scx.bw_quota_us,
.bw_burst_us = tg->scx.bw_burst_us,
+ .sched_idle = tg->scx.idle,
};
if (tg->scx.sched != parent ||
@@ -1464,6 +1466,7 @@ static void scx_cgroup_return_subtree(struct scx_sched *sch)
.bw_period_us = tg->scx.bw_period_us,
.bw_quota_us = tg->scx.bw_quota_us,
.bw_burst_us = tg->scx.bw_burst_us,
+ .sched_idle = tg->scx.idle,
};
/* the first pass must have transferred everything */
@@ -1803,6 +1806,12 @@ void scx_sub_enable_workfn(struct kthread_work *work)
goto err_disable;
}
+ scoped_guard(cpus_read_lock) {
+ ret = scx_alloc_kern_arena_objs(sch);
+ if (ret)
+ goto err_disable;
+ }
+
if (sch->ops.init) {
ret = SCX_CALL_OP_RET(sch, init, NULL);
if (ret) {
@@ -1813,10 +1822,6 @@ void scx_sub_enable_workfn(struct kthread_work *work)
sch->exit_info->flags |= SCX_EFLAG_INITIALIZED;
}
- ret = scx_set_cmask_scratch_alloc(sch);
- if (ret)
- goto err_disable;
-
struct scx_sub_attach_args sub_attach_args = {
.ops = &sch->ops,
.cgroup_path = sch->cgrp_path,
diff --git a/kernel/sched/ext/types.h b/kernel/sched/ext/types.h
index 943d8d429a2c..139176cf9fc6 100644
--- a/kernel/sched/ext/types.h
+++ b/kernel/sched/ext/types.h
@@ -70,6 +70,10 @@ enum scx_consts {
* smaller shards if the LLC exceeds the target size. No-topo cids are packed
* into their own max-sized shards.
*
+ * New fields are appended, never inserted: scx_bpf_cid_topo() copies this
+ * struct out sized by the program's own layout, and an older program's copy
+ * must stay a prefix of the kernel's.
+ *
* @core_cid: first cid of this cid's core (smt-sibling group)
* @core_idx: global index of that core, in [0, nr_cores_at_init)
* @llc_cid: first cid of this cid's LLC
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index ade1eceb39b8..57360f5cdde4 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1032,6 +1032,13 @@ static inline void __max_slice_update(struct sched_entity *se, struct rb_node *n
}
}
+static inline void min_vruntime_copy(struct sched_entity *new, struct sched_entity *old)
+{
+ new->min_vruntime = old->min_vruntime;
+ new->min_slice = old->min_slice;
+ new->max_slice = old->max_slice;
+}
+
/*
* se->min_vruntime = min(se->vruntime, {left,right}->min_vruntime)
*/
@@ -1059,8 +1066,9 @@ static inline bool min_vruntime_update(struct sched_entity *se, bool exit)
se->max_slice == old_max_slice;
}
-RB_DECLARE_CALLBACKS(static, min_vruntime_cb, struct sched_entity,
- run_node, min_vruntime, min_vruntime_update);
+
+RB_DECLARE_CALLBACKS_MULTI(static, min_vruntime_cb, struct sched_entity,
+ run_node, min_vruntime_copy, min_vruntime_update);
/*
* Enqueue an entity into the rb-tree:
@@ -1073,6 +1081,8 @@ static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
sum_w_vruntime_add(cfs_rq, se);
se->min_vruntime = se->vruntime;
se->min_slice = se->slice;
+ se->max_slice = se->slice;
+
rb_add_augmented_cached(&se->run_node, &cfs_rq->tasks_timeline,
__entity_less, &min_vruntime_cb);
}
@@ -1404,7 +1414,6 @@ static s64 update_se(struct rq *rq, struct sched_entity *se)
se->exec_start = now;
if (entity_is_task(se)) {
- struct task_struct *donor = task_of(se);
struct task_struct *running = rq->curr;
/*
* If se is a task, we account the time against the running
@@ -1417,8 +1426,7 @@ static s64 update_se(struct rq *rq, struct sched_entity *se)
account_group_exec_runtime(running, delta_exec);
account_mm_sched(rq, running, delta_exec);
- /* cgroup time is always accounted against the donor */
- cgroup_account_cputime(donor, delta_exec);
+ cgroup_account_cputime(running, delta_exec);
} else {
/* If not task, account the time against donor se */
se->sum_exec_runtime += delta_exec;
@@ -1470,7 +1478,7 @@ static inline int get_sched_cache_scale(int mul)
return (1 + (tol - 1) * mul);
}
-static bool exceed_llc_capacity(struct mm_struct *mm, int cpu)
+static bool exceed_llc_capacity(struct sched_cache_group *grp, int cpu)
{
#ifdef CONFIG_NUMA_BALANCING
unsigned long llc, footprint;
@@ -1489,7 +1497,7 @@ static bool exceed_llc_capacity(struct mm_struct *mm, int cpu)
* excluded.
*/
llc = sd->llc_bytes;
- footprint = READ_ONCE(mm->sc_stat.footprint);
+ footprint = READ_ONCE(grp->footprint);
/*
* Scale the LLC size by 256*llc_aggr_tolerance
@@ -1518,7 +1526,7 @@ static bool exceed_llc_capacity(struct mm_struct *mm, int cpu)
return false;
}
-static bool invalid_llc_nr(struct mm_struct *mm, struct task_struct *p,
+static bool invalid_llc_nr(struct sched_cache_group *grp, struct task_struct *p,
int cpu)
{
int scale;
@@ -1534,10 +1542,32 @@ static bool invalid_llc_nr(struct mm_struct *mm, struct task_struct *p,
if (scale == INT_MAX)
return false;
- return !fits_capacity((mm->sc_stat.nr_running_avg * cpu_smt_num_threads),
+ return !fits_capacity((READ_ONCE(grp->nr_running_avg) * cpu_smt_num_threads),
(scale * per_cpu(sd_llc_size, cpu)));
}
+/*
+ * A task counts in nr_pref_llc_running while it is queued on its preferred
+ * LLC (pref_llc_queued) and runnable (!sched_delayed), keeping the counter in
+ * the runnable domain so alb_break_llc() can compare it with h_nr_runnable.
+ */
+static bool task_pref_llc_runnable(struct task_struct *p)
+{
+ return p->pref_llc_queued && !p->se.sched_delayed;
+}
+
+static void pref_llc_running_inc(struct rq *rq, struct task_struct *p)
+{
+ if (task_pref_llc_runnable(p))
+ rq->nr_pref_llc_running++;
+}
+
+static void pref_llc_running_dec(struct rq *rq, struct task_struct *p)
+{
+ if (task_pref_llc_runnable(p))
+ rq->nr_pref_llc_running--;
+}
+
static void account_llc_enqueue(struct rq *rq, struct task_struct *p)
{
int pref_llc, pref_llc_queued;
@@ -1549,7 +1579,6 @@ static void account_llc_enqueue(struct rq *rq, struct task_struct *p)
pref_llc_queued = (pref_llc == task_llc(p));
rq->nr_llc_running++;
- rq->nr_pref_llc_running += pref_llc_queued;
/*
* Record whether p is enqueued on its preferred
@@ -1567,6 +1596,9 @@ static void account_llc_enqueue(struct rq *rq, struct task_struct *p)
*/
p->pref_llc_queued = pref_llc_queued;
+ /* Skipped while delayed; clear_delayed() adds it back on wake. */
+ pref_llc_running_inc(rq, p);
+
sd = rcu_dereference_all(rq->sd);
if (sd && (unsigned int)pref_llc < sd->llc_max)
sd->llc_counts[pref_llc]++;
@@ -1583,7 +1615,12 @@ static void account_llc_dequeue(struct rq *rq, struct task_struct *p)
rq->nr_llc_running--;
if (p->pref_llc_queued) {
- rq->nr_pref_llc_running--;
+ /*
+ * Skipped if still delayed (set_delayed() already removed it);
+ * clearing pref_llc_queued below also stops clear_delayed()
+ * from re-adding it.
+ */
+ pref_llc_running_dec(rq, p);
/*
* Update the status in case
* other logic might query
@@ -1611,12 +1648,20 @@ static void account_llc_dequeue(struct rq *rq, struct task_struct *p)
}
}
-void mm_init_sched(struct mm_struct *mm,
- struct sched_cache_time __percpu *_pcpu_sched)
+int mm_init_sched(struct mm_struct *mm,
+ struct sched_cache_time __percpu *_pcpu_sched)
{
+ struct sched_cache_group *grp;
unsigned long epoch = 0;
int i;
+ grp = kzalloc_obj(*grp);
+ if (!grp) {
+ free_percpu(_pcpu_sched);
+ mm->sched_cache_grp = NULL;
+ return -ENOMEM;
+ }
+
for_each_possible_cpu(i) {
struct sched_cache_time *pcpu_sched = per_cpu_ptr(_pcpu_sched, i);
struct rq *rq = cpu_rq(i);
@@ -1627,18 +1672,141 @@ void mm_init_sched(struct mm_struct *mm,
epoch = rq->cpu_epoch;
}
- raw_spin_lock_init(&mm->sc_stat.lock);
- mm->sc_stat.epoch = epoch;
- mm->sc_stat.cpu = -1;
- mm->sc_stat.next_scan = jiffies;
- mm->sc_stat.nr_running_avg = 0;
- mm->sc_stat.footprint = 0;
+ raw_spin_lock_init(&grp->lock);
+ grp->epoch = epoch;
+ grp->cpu = -1;
+ grp->next_scan = jiffies;
+ grp->nr_running_avg = 0;
+ grp->footprint = 0;
+ refcount_set(&grp->refcnt, 1);
/*
- * The update to mm->sc_stat should not be reordered
- * before initialization to mm's other fields, in case
+ * The update to grp->pcpu_sched should not be reordered
+ * before initialization to grp's other fields, in case
* the readers may get invalid mm_sched_epoch, etc.
*/
- smp_store_release(&mm->sc_stat.pcpu_sched, _pcpu_sched);
+ smp_store_release(&grp->pcpu_sched, _pcpu_sched);
+ /*
+ * Publish the group last. Not every reader qualifies it by
+ * grp->pcpu_sched - can_migrate_llc_task() only checks that the
+ * pointer is non-NULL before reading grp->footprint and
+ * grp->nr_running_avg - so a reachable group must already be
+ * fully initialized.
+ */
+ smp_store_release(&mm->sched_cache_grp, grp);
+ return 0;
+}
+
+static void sched_cache_group_free_rcu(struct rcu_head *rcu)
+{
+ struct sched_cache_group *grp =
+ container_of(rcu, struct sched_cache_group, rcu);
+
+ free_percpu(grp->pcpu_sched);
+ kfree(grp);
+}
+
+static void sched_cache_group_put(struct sched_cache_group *grp)
+{
+ if (!grp || !refcount_dec_and_test(&grp->refcnt))
+ return;
+
+ call_rcu(&grp->rcu, sched_cache_group_free_rcu);
+}
+
+DEFINE_FREE(sched_cache_group_put, struct sched_cache_group *,
+ sched_cache_group_put(_T));
+
+#define rcu_deref_sched_cache_grp(tsk) \
+ rcu_dereference_check((tsk)->sched_cache_grp, (tsk) == current)
+
+static struct sched_cache_group *sched_cache_replace_grp(struct task_struct *p,
+ struct sched_cache_group *new)
+{
+ struct sched_cache_group *old;
+
+ old = rcu_deref_sched_cache_grp(p);
+ rcu_assign_pointer(p->sched_cache_grp, new);
+
+ return old;
+}
+
+struct sched_cache_group *sched_cache_group_get(struct sched_cache_group *grp)
+{
+ /*
+ * refcount_inc_not_zero() is the acquire primitive for lockless
+ * (RCU) lookups; plain refcount_inc() would scribble the count if
+ * it already reached zero. Return NULL in that case.
+ */
+ if (grp && !refcount_inc_not_zero(&grp->refcnt))
+ grp = NULL;
+
+ return grp;
+}
+
+struct sched_cache_group *task_cache_group_get(struct task_struct *p)
+{
+ guard(rcu)();
+ return sched_cache_group_get(rcu_dereference(p->sched_cache_grp));
+}
+
+void sched_cache_fork(struct task_struct *p)
+{
+ /*
+ * The child takes its own reference on the mm's cache group, separate
+ * from the reference held by the mm. @p is not yet visible to readers,
+ * so a plain initializing store is enough.
+ */
+ RCU_INIT_POINTER(p->sched_cache_grp,
+ sched_cache_group_get(p->mm->sched_cache_grp));
+}
+
+void sched_cache_fork_cleanup(struct task_struct *p)
+{
+ /*
+ * A fork that fails after sched_cache_fork() never reaches exit_mm(),
+ * so drop the reference here. @p never became visible, so there are no
+ * concurrent readers and the reference we hold keeps the group alive.
+ */
+ sched_cache_group_put(rcu_access_pointer(p->sched_cache_grp));
+ RCU_INIT_POINTER(p->sched_cache_grp, NULL);
+}
+
+void sched_cache_exec_mmap(struct task_struct *p, struct mm_struct *mm)
+{
+ struct sched_cache_group *old;
+
+ /*
+ * Acquire the new reference before publishing the pointer, then drop
+ * the old one. @p is current and the only writer of its own pointer.
+ */
+ old = sched_cache_replace_grp(p, sched_cache_group_get(mm->sched_cache_grp));
+ sched_cache_group_put(old);
+}
+
+void sched_cache_exit_mm(struct task_struct *p)
+{
+ struct sched_cache_group *grp = sched_cache_replace_grp(p, NULL);
+
+#ifdef CONFIG_NUMA_BALANCING
+ /*
+ * Subtract this task's footprint from the group before dropping the
+ * reference, so the group footprint converges as its threads exit.
+ * Unlocked for performance; clamp to avoid underflow.
+ */
+ if (grp && p->total_numa_faults) {
+ unsigned long fp = READ_ONCE(grp->footprint);
+ unsigned long sub = min(fp, p->total_numa_faults);
+
+ WRITE_ONCE(grp->footprint, fp - sub);
+ }
+#endif
+ sched_cache_group_put(grp);
+}
+
+void mm_destroy_sched(struct mm_struct *mm)
+{
+ sched_cache_group_put(mm->sched_cache_grp);
+ mm->sched_cache_grp = NULL;
}
/* because why would C be fully specified */
@@ -1689,14 +1857,14 @@ static unsigned long fraction_mm_sched(struct rq *rq,
return div64_u64(NICE_0_LOAD * pcpu_sched->runtime, rq->cpu_runtime + 1);
}
-static int get_pref_llc(struct task_struct *p, struct mm_struct *mm)
+static int get_pref_llc(struct task_struct *p, struct sched_cache_group *grp)
{
int mm_sched_llc = -1, mm_sched_cpu;
- if (!mm)
+ if (!grp)
return -1;
- mm_sched_cpu = READ_ONCE(mm->sc_stat.cpu);
+ mm_sched_cpu = READ_ONCE(grp->cpu);
if (mm_sched_cpu != -1) {
mm_sched_llc = llc_id(mm_sched_cpu);
@@ -1726,8 +1894,8 @@ static unsigned int task_running_on_cpu(int cpu, struct task_struct *p);
static inline
void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec)
{
+ struct sched_cache_group *grp = rcu_dereference_all(p->sched_cache_grp);
struct sched_cache_time *pcpu_sched;
- struct mm_struct *mm = p->mm;
int mm_sched_llc = -1;
unsigned long epoch;
@@ -1738,12 +1906,18 @@ void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec)
return;
/*
* init_task, kthreads and user thread created
- * by user_mode_thread() don't have mm.
+ * by user_mode_thread() don't have a cache group.
+ * In theory a kernel thread does not have any valid
+ * cache group, because sched_cache_fork() is not
+ * invoked for a kernel thread - !grp should gate the
+ * kernel thread. Use the PF_KTHREAD check explicitly
+ * here for safety reasons, to guard against future
+ * modifications and to pair with task_tick_cache().
*/
- if (!mm || !mm->sc_stat.pcpu_sched)
+ if (p->flags & PF_KTHREAD || !grp || !grp->pcpu_sched)
return;
- pcpu_sched = per_cpu_ptr(mm->sc_stat.pcpu_sched, cpu_of(rq));
+ pcpu_sched = per_cpu_ptr(grp->pcpu_sched, cpu_of(rq));
scoped_guard (raw_spinlock, &rq->cpu_epoch_lock) {
__update_mm_sched(rq, pcpu_sched);
@@ -1756,14 +1930,14 @@ void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec)
* If this process hasn't hit task_cache_work() for a while invalidate
* its preferred state.
*/
- if ((long)(epoch - READ_ONCE(mm->sc_stat.epoch)) > llc_epoch_affinity_timeout ||
- invalid_llc_nr(mm, p, cpu_of(rq)) ||
- exceed_llc_capacity(mm, cpu_of(rq))) {
- if (READ_ONCE(mm->sc_stat.cpu) != -1)
- WRITE_ONCE(mm->sc_stat.cpu, -1);
+ if ((long)(epoch - READ_ONCE(grp->epoch)) > llc_epoch_affinity_timeout ||
+ invalid_llc_nr(grp, p, cpu_of(rq)) ||
+ exceed_llc_capacity(grp, cpu_of(rq))) {
+ if (READ_ONCE(grp->cpu) != -1)
+ WRITE_ONCE(grp->cpu, -1);
}
- mm_sched_llc = get_pref_llc(p, mm);
+ mm_sched_llc = get_pref_llc(p, grp);
/* task not on rq accounted later in account_entity_enqueue() */
if (task_running_on_cpu(rq->cpu, p) &&
@@ -1776,31 +1950,32 @@ void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec)
static void task_tick_cache(struct rq *rq, struct task_struct *p)
{
+ struct sched_cache_group *grp = rcu_dereference_all(p->sched_cache_grp);
struct callback_head *work = &p->cache_work;
- struct mm_struct *mm = p->mm;
unsigned long epoch;
if (!sched_cache_enabled())
return;
- if (!mm || p->flags & PF_KTHREAD ||
- !mm->sc_stat.pcpu_sched)
+ if (!grp || p->flags & PF_KTHREAD ||
+ !grp->pcpu_sched)
return;
epoch = rq->cpu_epoch;
/* avoid moving backwards */
- if (time_after_eq(mm->sc_stat.epoch, epoch))
+ if (time_after_eq(grp->epoch, epoch))
return;
- guard(raw_spinlock)(&mm->sc_stat.lock);
+ guard(raw_spinlock)(&grp->lock);
if (work->next == work) {
task_work_add(p, work, TWA_RESUME);
- WRITE_ONCE(mm->sc_stat.epoch, epoch);
+ WRITE_ONCE(grp->epoch, epoch);
}
}
-static void get_scan_cpumasks(cpumask_var_t cpus, struct task_struct *p)
+static void get_scan_cpumasks(cpumask_var_t cpus, struct task_struct *p,
+ struct sched_cache_group *grp)
{
#ifdef CONFIG_NUMA_BALANCING
int cpu, curr_cpu, nid, pref_nid;
@@ -1808,7 +1983,7 @@ static void get_scan_cpumasks(cpumask_var_t cpus, struct task_struct *p)
if (!static_branch_likely(&sched_numa_balancing))
goto out;
- cpu = READ_ONCE(p->mm->sc_stat.cpu);
+ cpu = READ_ONCE(grp->cpu);
if (cpu != -1)
nid = cpu_to_node(cpu);
curr_cpu = task_cpu(p);
@@ -1865,13 +2040,13 @@ static inline void update_avg_scale(u64 *avg, u64 sample)
static void task_cache_work(struct callback_head *work)
{
+ struct sched_cache_group *grp __free(sched_cache_group_put) = NULL;
+ cpumask_var_t cpus __free(free_cpumask_var) = CPUMASK_VAR_NULL;
int cpu, m_a_cpu = -1, nr_running = 0, curr_cpu;
unsigned long next_scan, now = jiffies;
struct task_struct *p = current, *cur;
unsigned long curr_m_a_occ = 0;
- struct mm_struct *mm = p->mm;
unsigned long m_a_occ = 0;
- cpumask_var_t cpus;
WARN_ON_ONCE(work != &p->cache_work);
@@ -1880,21 +2055,30 @@ static void task_cache_work(struct callback_head *work)
if (p->flags & PF_EXITING)
return;
- next_scan = READ_ONCE(mm->sc_stat.next_scan);
+ /*
+ * A reference makes sure grp is not released by others. The rcu
+ * lock can not be held till after zalloc_cpumask_var() below,
+ * because the latter might sleep.
+ */
+ grp = task_cache_group_get(p);
+ if (!grp)
+ return;
+
+ next_scan = READ_ONCE(grp->next_scan);
if (time_before(now, next_scan))
return;
/* only 1 thread is allowed to scan */
- if (!try_cmpxchg(&mm->sc_stat.next_scan, &next_scan,
+ if (!try_cmpxchg(&grp->next_scan, &next_scan,
now + max_t(unsigned long,
READ_ONCE(llc_epoch_period), 1)))
return;
curr_cpu = task_cpu(p);
- if (invalid_llc_nr(mm, p, curr_cpu) ||
- exceed_llc_capacity(mm, curr_cpu)) {
- if (READ_ONCE(mm->sc_stat.cpu) != -1)
- WRITE_ONCE(mm->sc_stat.cpu, -1);
+ if (invalid_llc_nr(grp, p, curr_cpu) ||
+ exceed_llc_capacity(grp, curr_cpu)) {
+ if (READ_ONCE(grp->cpu) != -1)
+ WRITE_ONCE(grp->cpu, -1);
return;
}
@@ -1905,7 +2089,7 @@ static void task_cache_work(struct callback_head *work)
scoped_guard (cpus_read_lock) {
guard(rcu)();
- get_scan_cpumasks(cpus, p);
+ get_scan_cpumasks(cpus, p, grp);
for_each_cpu(cpu, cpus) {
/* XXX sched_cluster_active */
@@ -1918,16 +2102,20 @@ static void task_cache_work(struct callback_head *work)
for_each_cpu(i, sched_domain_span(sd)) {
occ = fraction_mm_sched(cpu_rq(i),
- per_cpu_ptr(mm->sc_stat.pcpu_sched, i));
+ per_cpu_ptr(grp->pcpu_sched, i));
a_occ += occ;
if (occ > m_occ) {
m_occ = occ;
m_cpu = i;
}
+ /*
+ * rcu_access_pointer() is used because the
+ * pointer is only compared, never dereferenced.
+ */
cur = rcu_dereference_all(cpu_rq(i)->curr);
if (cur && !(cur->flags & (PF_EXITING | PF_KTHREAD)) &&
- cur->mm == mm)
+ rcu_access_pointer(cur->sched_cache_grp) == grp)
nr_running++;
}
@@ -1951,7 +2139,7 @@ static void task_cache_work(struct callback_head *work)
m_a_cpu = m_cpu;
}
- if (llc_id(cpu) == llc_id(READ_ONCE(mm->sc_stat.cpu)))
+ if (llc_id(cpu) == llc_id(READ_ONCE(grp->cpu)))
curr_m_a_occ = a_occ;
cpumask_andnot(cpus, cpus, sched_domain_span(sd));
@@ -1960,7 +2148,7 @@ static void task_cache_work(struct callback_head *work)
if (m_a_occ > (2 * curr_m_a_occ)) {
/*
- * Avoid switching sc_stat.cpu too fast.
+ * Avoid switching sched_cache_grp->cpu too fast.
* The reason to choose 2X is because:
* 1. It is better to keep the preferred LLC stable,
* rather than changing it frequently and cause migrations
@@ -1969,11 +2157,10 @@ static void task_cache_work(struct callback_head *work)
* 3. 2X is chosen based on test results, as it delivers
* the optimal performance gain so far.
*/
- WRITE_ONCE(mm->sc_stat.cpu, m_a_cpu);
+ WRITE_ONCE(grp->cpu, m_a_cpu);
}
- update_avg_scale(&mm->sc_stat.nr_running_avg, nr_running);
- free_cpumask_var(cpus);
+ update_avg_scale(&grp->nr_running_avg, nr_running);
}
void init_sched_mm(struct task_struct *p)
@@ -1983,10 +2170,18 @@ void init_sched_mm(struct task_struct *p)
init_task_work(work, task_cache_work);
work->next = work;
/*
+ * dup_task_struct() copies the parent's task_struct, including its
+ * sched_cache_grp, for which the child holds no reference. Clear it
+ * here - before copy_mm() runs - so the child never carries a
+ * borrowed pointer that the fork error path would put.
+ */
+ RCU_INIT_POINTER(p->sched_cache_grp, NULL);
+ /*
* Reset new task's preference to avoid
* polluting account_llc_enqueue().
*/
p->preferred_llc = -1;
+ p->pref_llc_queued = 0;
}
#else /* CONFIG_SCHED_CACHE */
@@ -2008,6 +2203,10 @@ static void account_llc_enqueue(struct rq *rq, struct task_struct *p) {}
static void account_llc_dequeue(struct rq *rq, struct task_struct *p) {}
+static void pref_llc_running_inc(struct rq *rq, struct task_struct *p) {}
+
+static void pref_llc_running_dec(struct rq *rq, struct task_struct *p) {}
+
#endif /* CONFIG_SCHED_CACHE */
/*
@@ -3684,6 +3883,7 @@ static int preferred_group_nid(struct task_struct *p, int nid)
static void task_numa_placement(struct task_struct *p)
__context_unsafe(/* conditional locking */)
{
+ struct sched_cache_group __maybe_unused *grp;
int seq, nid, max_nid = NUMA_NO_NODE;
unsigned long max_faults = 0;
unsigned long fault_types[2] = { 0, 0 };
@@ -3776,19 +3976,24 @@ static void task_numa_placement(struct task_struct *p)
* heuristic and occasional lost updates are tolerable.
*
* If a task exits, its corresponding footprint must
- * be subtracted from the mm->sc_stat.footprint, otherwise
- * the mm->sc_stat.footprint will not converge:
- * the exiting thread's footprint remains unchanged/undecayed
- * in mm->sc_stat.footprint. See exit_mm().
+ * be subtracted from p->sched_cache_grp->footprint,
+ * otherwise the footprint will not converge: the
+ * exiting thread's footprint remains unchanged/undecayed.
+ * See exit_mm().
*
* Lost updates and unsynchronized subtraction
* in exit_mm() can cause footprint + diff to
* go negative. Clamp to zero to prevent the
* unsigned footprint from wrapping.
*/
- new_fp = (long)READ_ONCE(p->mm->sc_stat.footprint) + diff;
- WRITE_ONCE(p->mm->sc_stat.footprint,
- max(new_fp, 0L));
+ scoped_guard(rcu) {
+ grp = rcu_dereference(p->sched_cache_grp);
+
+ if (grp) {
+ new_fp = (long)READ_ONCE(grp->footprint) + diff;
+ WRITE_ONCE(grp->footprint, max(new_fp, 0L));
+ }
+ }
#endif
}
@@ -6382,15 +6587,27 @@ static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq);
static void set_delayed(struct sched_entity *se)
{
- se->sched_delayed = 1;
-
/*
* Delayed se of cfs_rq have no tasks queued on them.
* Do not adjust h_nr_runnable since __dequeue_task()
* will account it for blocked tasks.
+ *
+ * This check can be removed because when flat pick
+ * patches get merged as only task can get delayed,
+ * same for clear_delayed().
*/
- if (!entity_is_task(se))
+ if (!entity_is_task(se)) {
+ se->sched_delayed = 1;
return;
+ }
+
+ /*
+ * Drop a task leaving the runnable set.
+ * Needs to be called before sched_delayed is set.
+ * clear_delayed() mirrors this after clearing the flag.
+ */
+ pref_llc_running_dec(rq_of(cfs_rq_of(se)), task_of(se));
+ se->sched_delayed = 1;
for_each_sched_entity(se) {
struct cfs_rq *cfs_rq = cfs_rq_of(se);
@@ -6412,6 +6629,13 @@ static void clear_delayed(struct sched_entity *se)
if (!entity_is_task(se))
return;
+ /*
+ * Re-add on wake, after sched_delayed is cleared. On a final delayed
+ * dequeue account_llc_dequeue() already cleared pref_llc_queued, so
+ * this does nothing.
+ */
+ pref_llc_running_inc(rq_of(cfs_rq_of(se)), task_of(se));
+
for_each_sched_entity(se) {
struct cfs_rq *cfs_rq = cfs_rq_of(se);
@@ -10387,6 +10611,7 @@ enum migration_type {
#define LBF_SOME_PINNED 0x08
#define LBF_ACTIVE_LB 0x10
#define LBF_LLC_PINNED 0x20
+#define LBF_ACTIVE_LB_LLC 0x40
struct lb_env {
struct sched_domain *sd;
@@ -10716,7 +10941,7 @@ static inline bool task_misfits_asym_cpu(struct lb_env *env, struct task_struct
static enum llc_mig can_migrate_llc_task(struct lb_env *env,
struct task_struct *p)
{
- struct mm_struct *mm;
+ struct sched_cache_group *grp;
bool to_pref;
int cpu, src_cpu, dst_cpu;
@@ -10725,19 +10950,19 @@ static enum llc_mig can_migrate_llc_task(struct lb_env *env,
src_cpu = env->src_cpu;
dst_cpu = env->dst_cpu;
- mm = p->mm;
- if (!mm)
+ grp = rcu_dereference_all(p->sched_cache_grp);
+ if (!grp)
return mig_unrestricted;
- cpu = READ_ONCE(mm->sc_stat.cpu);
+ cpu = READ_ONCE(grp->cpu);
if (cpu < 0 || cpus_share_cache(src_cpu, dst_cpu))
return mig_unrestricted;
/* skip cache aware load balance for too many threads */
- if (invalid_llc_nr(mm, p, dst_cpu) ||
- exceed_llc_capacity(mm, dst_cpu)) {
- if (READ_ONCE(mm->sc_stat.cpu) != -1)
- WRITE_ONCE(mm->sc_stat.cpu, -1);
+ if (invalid_llc_nr(grp, p, dst_cpu) ||
+ exceed_llc_capacity(grp, dst_cpu)) {
+ if (READ_ONCE(grp->cpu) != -1)
+ WRITE_ONCE(grp->cpu, -1);
return mig_unrestricted;
}
@@ -10806,6 +11031,21 @@ alb_break_llc(struct lb_env *env)
}
/*
+ * Returns true if p's preferred LLC does not match the destination CPU
+ * under migrate_llc_task semantics. Passive LB passes migrate_llc_task
+ * in env->migration_type, while active LB carries LBF_ACTIVE_LB_LLC in
+ * env->flags to avoid overwriting env->migration_type.
+ */
+static inline bool
+migrate_llc_task_wrong_dst(struct task_struct *p, struct lb_env *env)
+{
+ return sched_cache_enabled() &&
+ (env->migration_type == migrate_llc_task ||
+ env->flags & LBF_ACTIVE_LB_LLC) &&
+ READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu);
+}
+
+/*
* Check if migrating task p from env->src_cpu to
* env->dst_cpu breaks LLC localiy.
*/
@@ -10833,8 +11073,7 @@ static bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env)
* run on env->dst_cpu, skip the tasks do not prefer
* env->dst_cpu, and find the one that prefers.
*/
- if (env->migration_type == migrate_llc_task &&
- READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu))
+ if (migrate_llc_task_wrong_dst(p, env))
return true;
if (can_migrate_llc_task(env, p) != mig_forbid)
@@ -10857,6 +11096,12 @@ alb_break_llc(struct lb_env *env)
}
static inline bool
+migrate_llc_task_wrong_dst(struct task_struct *p, struct lb_env *env)
+{
+ return false;
+}
+
+static inline bool
migrate_degrades_llc(struct task_struct *p, struct lb_env *env)
{
return false;
@@ -10955,7 +11200,7 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env)
* 4) too many balance attempts have failed.
*/
if (env->flags & LBF_ACTIVE_LB)
- return 1;
+ return !migrate_llc_task_wrong_dst(p, env);
degrades = migrate_degrades_locality(p, env);
if (!degrades) {
@@ -13354,6 +13599,20 @@ static int need_active_balance(struct lb_env *env)
}
static int active_load_balance_cpu_stop(void *data);
+static int active_load_balance_llc_cpu_stop(void *data);
+
+/*
+ * migration_type is checked elsewhere to decide migration policy, so
+ * it shouldn't be repurposed just to flag an LLC-directed active
+ * balance across the stopper. Pick the callback here instead.
+ */
+static inline cpu_stop_fn_t alb_stop_fn(struct lb_env *env)
+{
+ if (env->migration_type == migrate_llc_task)
+ return active_load_balance_llc_cpu_stop;
+
+ return active_load_balance_cpu_stop;
+}
static int should_we_balance(struct lb_env *env)
{
@@ -13699,7 +13958,7 @@ more_balance:
}
if (active_balance) {
stop_one_cpu_nowait(cpu_of(busiest),
- active_load_balance_cpu_stop, busiest,
+ alb_stop_fn(&env), busiest,
&busiest->active_balance_work);
}
preempt_enable();
@@ -13804,7 +14063,7 @@ update_next_balance(struct sched_domain *sd, unsigned long *next_balance)
* least 1 task to be running on each physical CPU where possible, and
* avoids physical / logical imbalances.
*/
-static int active_load_balance_cpu_stop(void *data)
+static int __active_load_balance_cpu_stop(void *data, unsigned int lb_flags)
{
struct rq *busiest_rq = data;
int busiest_cpu = cpu_of(busiest_rq);
@@ -13854,7 +14113,7 @@ static int active_load_balance_cpu_stop(void *data)
.src_cpu = busiest_rq->cpu,
.src_rq = busiest_rq,
.idle = CPU_IDLE,
- .flags = LBF_ACTIVE_LB,
+ .flags = LBF_ACTIVE_LB | lb_flags,
};
schedstat_inc(sd->alb_count);
@@ -13882,6 +14141,16 @@ out_unlock:
return 0;
}
+static int active_load_balance_cpu_stop(void *data)
+{
+ return __active_load_balance_cpu_stop(data, 0);
+}
+
+static int active_load_balance_llc_cpu_stop(void *data)
+{
+ return __active_load_balance_cpu_stop(data, LBF_ACTIVE_LB_LLC);
+}
+
/*
* Scale the max sched_balance_rq interval with the number of CPUs in the system.
* This trades load-balance latency on larger machines for less cross talk.
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 0248227d983a..3dab0253976f 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -985,8 +985,8 @@ void sched_cache_active_set(void)
}
/*
- * Update the bottom sched_domain's llc_bytes for @cpu and all its
- * LLC siblings. Called from cacheinfo_cpu_online() or
+ * Update the bottom sched_domain's llc_bytes for @cpus sharing a physical
+ * LLC. Called from cacheinfo_cpu_online() or
* cacheinfo_cpu_pre_down() with cpu hotplug lock held.
*
* Note: get_effective_llc_bytes() returns 0 on PowerPC.
@@ -996,17 +996,13 @@ void sched_cache_active_set(void)
* and does not populates the per-CPU struct cpu_cacheinfo array
* that get_cpu_cacheinfo_llc() reads.
*/
-void sched_update_llc_bytes(unsigned int cpu)
+void sched_update_llc_bytes(const struct cpumask *cpus)
{
struct sched_domain *sd, *sdp;
unsigned int i;
sched_domains_mutex_lock();
- sdp = rcu_dereference_sched_domain(per_cpu(sd_llc, cpu));
- if (!sdp)
- goto unlock;
-
/*
* ci->shared_cpu_map is built incrementally as CPUs come
* online, so the first CPU in an LLC initially sees
@@ -1014,14 +1010,22 @@ void sched_update_llc_bytes(unsigned int cpu)
* get_effective_llc_bytes(). Re-evaluating every LLC
* sibling on each online event corrects this once the full
* shared_cpu_map is known.
+ *
+ * The departing CPU's domains have already been detached when
+ * cacheinfo removes it. Use the surviving cache siblings instead.
+ * They may belong to different cpuset partitions, so use each CPU's
+ * own LLC domain to scale its share of the physical cache.
*/
- for_each_cpu(i, sched_domain_span(sdp)) {
+ for_each_cpu(i, cpus) {
+ sdp = rcu_dereference_sched_domain(per_cpu(sd_llc, i));
+ if (!sdp)
+ continue;
+
sd = rcu_dereference_sched_domain(cpu_rq(i)->sd);
if (sd)
sd->llc_bytes = get_effective_llc_bytes(i, sdp);
}
-unlock:
sched_domains_mutex_unlock();
}
diff --git a/kernel/signal.c b/kernel/signal.c
index a5e15bf09d31..d31ebcb6ed4d 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -457,18 +457,42 @@ static void __sigqueue_free(struct sigqueue *q)
kmem_cache_free(sigqueue_cachep, q);
}
-void flush_sigqueue(struct sigpending *queue)
+/*
+ * flush_sigqueue_list() can only be invoked without holding sighand::siglock in
+ * the following cases:
+ *
+ * 1) When flushing task::pending _after_ setting task::flags PF_EXITING
+ *
+ * All functions which try to send a signal to @task will observe PF_EXITING
+ * and drop the signal.
+ *
+ * 2) When flushing task::signal::shared_pending _after_ the last task in a
+ * thread group was unhashed and task::sighand is NULL.
+ *
+ * Nothing can queue a signal anymore because sighand is NULL.
+ */
+static void flush_sigqueue_list(struct list_head *head)
{
- struct sigqueue *q;
+ struct sigqueue *q, *tmp;
- sigemptyset(&queue->signal);
- while (!list_empty(&queue->list)) {
- q = list_entry(queue->list.next, struct sigqueue , list);
+ list_for_each_entry_safe(q, tmp, head, list) {
list_del_init(&q->list);
__sigqueue_free(q);
}
}
+void flush_sigqueue(struct sigpending *queue)
+{
+ sigemptyset(&queue->signal);
+ flush_sigqueue_list(&queue->list);
+}
+
+static void sigqueue_dequeue_pending(struct sigpending *queue, struct list_head *head)
+{
+ sigemptyset(&queue->signal);
+ list_splice_init(&queue->list, head);
+}
+
/*
* Flush all pending signals for this kthread.
*/
@@ -1019,6 +1043,21 @@ static inline bool legacy_queue(struct sigpending *signals, int sig)
return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);
}
+/*
+ * When PF_EXITING is set the task is on the way out and has t::pending
+ * flushed already. Prevent queueing of PIDTYPE_PID signals as they would
+ * be leaked.
+ */
+static inline bool task_can_queue_signal(struct task_struct *t, enum pid_type type)
+{
+ lockdep_assert_held(&t->sighand->siglock);
+
+ if (!(t->flags & PF_EXITING))
+ return true;
+
+ return type != PIDTYPE_PID;
+}
+
static int __send_signal_locked(int sig, struct kernel_siginfo *info,
struct task_struct *t, enum pid_type type, bool force)
{
@@ -1030,6 +1069,10 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info,
lockdep_assert_held(&t->sighand->siglock);
result = TRACE_SIGNAL_IGNORED;
+
+ if (!task_can_queue_signal(t, type))
+ goto ret;
+
if (!prepare_signal(sig, t, force))
goto ret;
@@ -1892,6 +1935,18 @@ int kill_pid(struct pid *pid, int sig, int priv)
}
EXPORT_SYMBOL(kill_pid);
+int kill_cad_pid(int sig, int priv)
+{
+ int ret;
+
+ rcu_read_lock();
+ ret = kill_pid(rcu_dereference(cad_pid), sig, priv);
+ rcu_read_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(kill_cad_pid);
+
#ifdef CONFIG_POSIX_TIMERS
/*
* These functions handle POSIX timer signals. POSIX timers use
@@ -1968,11 +2023,25 @@ static inline struct task_struct *posixtimer_get_target(struct k_itimer *tmr)
struct task_struct *t = pid_task(tmr->it_pid, tmr->it_pid_type);
if (t && tmr->it_pid_type != PIDTYPE_PID &&
- same_thread_group(t, current) && !current->exit_state)
+ same_thread_group(t, current) && !(current->flags & PF_EXITING))
t = current;
return t;
}
+/*
+ * Find the target task for the POSIX timer signal and prevent that a
+ * PIDTYPE_PID signal is queued on a task which has PF_EXITING set.
+ */
+static inline struct task_struct *posixtimer_get_unignore_target(struct k_itimer *tmr)
+{
+ struct task_struct *t = posixtimer_get_target(tmr);
+
+ if (t && task_can_queue_signal(t, tmr->it_pid_type))
+ return t;
+
+ return NULL;
+}
+
void posixtimer_send_sigqueue(struct k_itimer *tmr)
{
struct sigqueue *q = &tmr->sigq;
@@ -1990,6 +2059,9 @@ void posixtimer_send_sigqueue(struct k_itimer *tmr)
if (!likely(lock_task_sighand(t, &flags)))
return;
+ if (!task_can_queue_signal(t, tmr->it_pid_type))
+ goto unlock;
+
/*
* Update @tmr::sigqueue_seq for posix timer signals with sighand
* locked to prevent a race against dequeue_signal().
@@ -2081,6 +2153,7 @@ void posixtimer_send_sigqueue(struct k_itimer *tmr)
result = TRACE_SIGNAL_DELIVERED;
out:
trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
+unlock:
unlock_task_sighand(t, &flags);
}
@@ -2136,7 +2209,7 @@ static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
* has exited by now, drop the reference count.
*/
guard(rcu)();
- target = posixtimer_get_target(tmr);
+ target = posixtimer_get_unignore_target(tmr);
if (target)
posixtimer_queue_sigqueue(&tmr->sigq, target, tmr->it_pid_type);
else
@@ -3120,42 +3193,36 @@ static void retarget_shared_pending(struct task_struct *tsk, sigset_t *which)
void exit_signals(struct task_struct *tsk)
{
+ LIST_HEAD(sigq_list);
int group_stop = 0;
- sigset_t unblocked;
/*
* @tsk is about to have PF_EXITING set - lock out users which
- * expect stable threadgroup.
+ * expect a stable threadgroup.
*/
cgroup_threadgroup_change_begin(tsk);
- if (thread_group_empty(tsk) || (tsk->signal->flags & SIGNAL_GROUP_EXIT)) {
+ scoped_guard(spinlock_irq, &tsk->sighand->siglock) {
tsk->flags |= PF_EXITING;
- cgroup_threadgroup_change_end(tsk);
- return;
- }
- spin_lock_irq(&tsk->sighand->siglock);
- /*
- * From now this task is not visible for group-wide signals,
- * see wants_signal(), do_signal_stop().
- */
- tsk->flags |= PF_EXITING;
+ sigqueue_dequeue_pending(&tsk->pending, &sigq_list);
- cgroup_threadgroup_change_end(tsk);
+ if (task_sigpending(tsk) && !thread_group_empty(tsk) &&
+ !(tsk->signal->flags & SIGNAL_GROUP_EXIT)) {
+ sigset_t unblocked = tsk->blocked;
- if (!task_sigpending(tsk))
- goto out;
+ signotset(&unblocked);
+ retarget_shared_pending(tsk, &unblocked);
- unblocked = tsk->blocked;
- signotset(&unblocked);
- retarget_shared_pending(tsk, &unblocked);
+ if (unlikely(tsk->jobctl & JOBCTL_STOP_PENDING) &&
+ task_participate_group_stop(tsk))
+ group_stop = CLD_STOPPED;
+ }
+ }
- if (unlikely(tsk->jobctl & JOBCTL_STOP_PENDING) &&
- task_participate_group_stop(tsk))
- group_stop = CLD_STOPPED;
-out:
- spin_unlock_irq(&tsk->sighand->siglock);
+ cgroup_threadgroup_change_end(tsk);
+
+ flush_sigqueue_list(&sigq_list);
/*
* If group stop has completed, deliver the notification. This
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 0014d163f989..62ad7c000386 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -615,34 +615,41 @@ void clockevents_handle_noop(struct clock_event_device *dev)
{
}
-/**
- * clockevents_exchange_device - release and request clock devices
- * @old: device to release (can be NULL)
- * @new: device to request (can be NULL)
- *
- * Called from various tick functions with clockevents_lock held and
- * interrupts disabled.
- */
-void clockevents_exchange_device(struct clock_event_device *old,
- struct clock_event_device *new)
+void __clockevents_exchange_device(struct clock_event_device *old,
+ struct clock_event_device *new)
{
/*
* Caller releases a clock event device. We queue it into the
* released list and do a notify add later.
*/
if (old) {
- module_put(old->owner);
clockevents_switch_state(old, CLOCK_EVT_STATE_DETACHED);
list_move(&old->list, &clockevents_released);
}
if (new) {
- BUG_ON(!clockevent_state_detached(new));
+ WARN_ON(!clockevent_state_detached(new));
clockevents_shutdown(new);
}
}
/**
+ * clockevents_exchange_device - release and request clock devices
+ * @old: device to release (can be NULL)
+ * @new: device to request (can be NULL)
+ *
+ * Called from various tick functions with clockevents_lock held and
+ * interrupts disabled.
+ */
+void clockevents_exchange_device(struct clock_event_device *old,
+ struct clock_event_device *new)
+{
+ __clockevents_exchange_device(old, new);
+ if (old)
+ module_put(old->owner);
+}
+
+/**
* clockevents_suspend - suspend clock devices
*/
void clockevents_suspend(void)
@@ -699,7 +706,7 @@ void tick_offline_cpu(unsigned int cpu)
if (cpumask_test_cpu(cpu, dev->cpumask) &&
cpumask_weight(dev->cpumask) == 1 &&
!tick_is_broadcast_device(dev)) {
- BUG_ON(!clockevent_state_detached(dev));
+ WARN_ON(!clockevent_state_detached(dev));
list_del(&dev->list);
}
}
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 530d61257b9a..cbf1693c86b3 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -1263,13 +1263,23 @@ remove_and_enqueue_same_base(struct hrtimer *timer, struct hrtimer_clock_base *b
{
bool was_first = false;
+ /*
+ * Updating the sort key while @timer is queued can temporarily
+ * make the tree inconsistent. This is safe under cpu_base->lock:
+ * no other queue operation can observe that state.
+ * hrtimer_can_update_in_place() either confirms that the new expiry
+ * fits between the neighbours or timerqueue_linked_del() removes the
+ * timer without consulting the expiry.
+ */
+ hrtimer_set_expires_range_ns(timer, expires, delta_ns);
+ expires = hrtimer_get_expires(timer);
+
/* Remove it from the timer queue if active */
if (timer->is_queued) {
was_first = !timerqueue_linked_prev(&timer->node);
/* Try to update in place to avoid the de/enqueue dance */
if (hrtimer_can_update_in_place(timer, base, expires)) {
- hrtimer_set_expires_range_ns(timer, expires, delta_ns);
trace_hrtimer_start(timer, mode, true);
if (was_first)
base->expires_next = expires;
@@ -1280,9 +1290,6 @@ remove_and_enqueue_same_base(struct hrtimer *timer, struct hrtimer_clock_base *b
timerqueue_linked_del(&base->active, &timer->node);
}
- /* Set the new expiry time */
- hrtimer_set_expires_range_ns(timer, expires, delta_ns);
-
debug_activate(timer, mode, timer->is_queued);
base->cpu_base->active_bases |= 1 << base->index;
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
index 213ae1d6a014..80c354811538 100644
--- a/kernel/time/jiffies.c
+++ b/kernel/time/jiffies.c
@@ -136,6 +136,8 @@ static int sysctl_k2u_int_conv_userhz(bool *negp, ulong *u_ptr, const int *k_ptr
static ulong sysctl_msecs_to_jiffies(const ulong val)
{
+ if (val > jiffies_to_msecs(MAX_JIFFY_OFFSET))
+ return MAX_JIFFY_OFFSET;
return msecs_to_jiffies(val);
}
@@ -181,7 +183,7 @@ static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
- return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
+ return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true,
sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
}
@@ -195,10 +197,10 @@ static int sysctl_k2u_ulong_conv_ms(ulong *u_ptr, const ulong *k_ptr)
return proc_ulong_k2u_conv_kop(u_ptr, k_ptr, sysctl_jiffies_to_msecs);
}
-static int do_proc_ulong_conv_ms_jiffies(bool *negp, ulong *u_ptr, ulong *k_ptr,
- int dir, const struct ctl_table *tbl)
+static int do_proc_ulong_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, ulong *k_ptr,
+ int dir, const struct ctl_table *tbl)
{
- return proc_ulong_conv(u_ptr, k_ptr, dir, tbl, false,
+ return proc_ulong_conv(u_ptr, k_ptr, dir, tbl, true,
sysctl_u2k_ulong_conv_ms, sysctl_k2u_ulong_conv_ms);
}
@@ -229,8 +231,8 @@ static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
return -ENOSYS;
}
-static int do_proc_ulong_conv_ms_jiffies(bool *negp, ulong *u_ptr, ulong *k_ptr,
- int dir, const struct ctl_table *tbl)
+static int do_proc_ulong_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, ulong *k_ptr,
+ int dir, const struct ctl_table *tbl)
{
return -ENOSYS;
}
@@ -333,7 +335,7 @@ int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return proc_doulongvec_conv(table, dir, buffer, lenp, ppos,
- do_proc_ulong_conv_ms_jiffies);
+ do_proc_ulong_conv_ms_jiffies_minmax);
}
EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index d73d31c7994f..0bf4fcd969c8 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -408,6 +408,7 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer)
new_timer->kclock = &clock_posix_cpu;
timerqueue_init(&new_timer->it.cpu.node);
+ INIT_LIST_HEAD(&new_timer->it.cpu.elist);
new_timer->it.cpu.pid = get_pid(pid);
rcu_read_unlock();
return 0;
@@ -566,6 +567,24 @@ static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned l
}
/*
+ * If the timer is queued on the expiry list, then it cannot be dequeued because
+ * the firing list is not protected by sighand->lock. The delivery path is
+ * waiting for the timer lock. So go back, unlock and retry.
+ */
+static bool posix_cpu_timer_on_expiry_list(struct k_itimer *timer)
+{
+ if (list_empty(&timer->it.cpu.elist))
+ return false;
+
+ /*
+ * Prevent signal delivery as there is no point in delivering a signal
+ * which is made obsolete right away.
+ */
+ timer->it.cpu.firing = false;
+ return true;
+}
+
+/*
* Clean up a CPU-clock timer that is about to be destroyed.
* This is called from timer deletion with the timer already locked.
* If we return TIMER_RETRY, it's necessary to release the timer's lock
@@ -580,18 +599,10 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
p = timer_lock_sighand(timer, &flags);
if (likely(p)) {
- if (timer->it.cpu.firing) {
- /*
- * Prevent signal delivery. The timer cannot be dequeued
- * because it is on the firing list which is not protected
- * by sighand->lock. The delivery path is waiting for
- * the timer lock. So go back, unlock and retry.
- */
- timer->it.cpu.firing = false;
+ if (posix_cpu_timer_on_expiry_list(timer))
ret = TIMER_RETRY;
- } else {
+ else
disarm_timer(timer, p);
- }
unlock_task_sighand(p, &flags);
}
@@ -731,14 +742,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
/* Retrieve the current expiry time before disarming the timer */
old_expires = cpu_timer_getexpires(ctmr);
- if (unlikely(timer->it.cpu.firing)) {
- /*
- * Prevent signal delivery. The timer cannot be dequeued
- * because it is on the firing list which is not protected
- * by sighand->lock. The delivery path is waiting for
- * the timer lock. So go back, unlock and retry.
- */
- timer->it.cpu.firing = false;
+ if (posix_cpu_timer_on_expiry_list(timer)) {
ret = TIMER_RETRY;
} else {
cpu_timer_dequeue(ctmr);
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 115e0bf01276..bda3d2391a60 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -165,23 +165,31 @@ static bool tick_set_oneshot_wakeup_device(struct clock_event_device *newdev,
*/
void tick_install_broadcast_device(struct clock_event_device *dev, int cpu)
{
- struct clock_event_device *cur = tick_broadcast_device.evtdev;
+ struct clock_event_device *cur;
- if (tick_set_oneshot_wakeup_device(dev, cpu))
- return;
+ scoped_guard(raw_spinlock_irqsave, &tick_broadcast_lock) {
- if (!tick_check_broadcast_device(cur, dev))
- return;
+ if (tick_set_oneshot_wakeup_device(dev, cpu))
+ return;
- if (!try_module_get(dev->owner))
- return;
+ cur = tick_broadcast_device.evtdev;
+ if (!tick_check_broadcast_device(cur, dev))
+ return;
- clockevents_exchange_device(cur, dev);
+ if (!try_module_get(dev->owner))
+ return;
+
+ __clockevents_exchange_device(cur, dev);
+ if (cur)
+ cur->event_handler = clockevents_handle_noop;
+ WRITE_ONCE(tick_broadcast_device.evtdev, dev);
+ if (!cpumask_empty(tick_broadcast_mask))
+ tick_broadcast_start_periodic(dev);
+ }
+
+ /* Module release must be outside of the lock */
if (cur)
- cur->event_handler = clockevents_handle_noop;
- tick_broadcast_device.evtdev = dev;
- if (!cpumask_empty(tick_broadcast_mask))
- tick_broadcast_start_periodic(dev);
+ module_put(cur->owner);
if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
return;
@@ -1218,7 +1226,7 @@ int tick_broadcast_oneshot_active(void)
*/
bool tick_broadcast_oneshot_available(void)
{
- struct clock_event_device *bc = tick_broadcast_device.evtdev;
+ struct clock_event_device *bc = READ_ONCE(tick_broadcast_device.evtdev);
return bc ? bc->features & CLOCK_EVT_FEAT_ONESHOT : false;
}
@@ -1226,7 +1234,7 @@ bool tick_broadcast_oneshot_available(void)
#else
int __tick_broadcast_oneshot_control(enum tick_broadcast_state state)
{
- struct clock_event_device *bc = tick_broadcast_device.evtdev;
+ struct clock_event_device *bc = READ_ONCE(tick_broadcast_device.evtdev);
if (!bc || (bc->features & CLOCK_EVT_FEAT_HRTIMER))
return -EBUSY;
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h
index 182974c4f21b..65680db95053 100644
--- a/kernel/time/tick-internal.h
+++ b/kernel/time/tick-internal.h
@@ -55,6 +55,8 @@ static inline void clockevent_set_state(struct clock_event_device *dev,
}
extern void clockevents_shutdown(struct clock_event_device *dev);
+extern void __clockevents_exchange_device(struct clock_event_device *old,
+ struct clock_event_device *new);
extern void clockevents_exchange_device(struct clock_event_device *old,
struct clock_event_device *new);
extern void clockevents_switch_state(struct clock_event_device *dev,
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 40d373d65f9b..ed455b53513b 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -143,9 +143,6 @@ enum {
#define FGRAPH_DATA_INDEX_MASK GENMASK(FGRAPH_DATA_INDEX_BITS - 1, 0)
#define FGRAPH_DATA_INDEX_SHIFT (FGRAPH_DATA_SHIFT + FGRAPH_DATA_BITS)
-#define FGRAPH_MAX_INDEX \
- ((FGRAPH_INDEX_SIZE << FGRAPH_DATA_BITS) + FGRAPH_RET_INDEX)
-
#define FGRAPH_ARRAY_SIZE FGRAPH_INDEX_BITS
/*
diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
index 1e9b00997ff2..9f2d98181779 100644
--- a/kernel/trace/fprobe.c
+++ b/kernel/trace/fprobe.c
@@ -171,6 +171,11 @@ static inline bool write_fprobe_header(unsigned long *stack,
static inline void read_fprobe_header(unsigned long *stack,
struct fprobe **fp, unsigned int *size_words)
{
+ if (!*stack) {
+ *fp = NULL;
+ *size_words = 0;
+ return;
+ }
*fp = arch_decode_fprobe_header_fp(*stack);
*size_words = arch_decode_fprobe_header_size(*stack);
}
@@ -203,6 +208,12 @@ static inline void read_fprobe_header(unsigned long *stack,
{
struct __fprobe_header *fph = (struct __fprobe_header *)stack;
+ if (!*stack) {
+ *fp = NULL;
+ *size_words = 0;
+ return;
+ }
+
*fp = fph->fp;
*size_words = fph->size_words;
}
@@ -635,6 +646,10 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops
}
}
+ /* Terminate the list, fgraph_reserve_data() does not clear it. */
+ if (used && used < reserved_words)
+ fgraph_data[used] = 0;
+
/* If any exit_handler is set, data must be used. */
return used != 0;
}
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 53d5db60bfa5..673a54fdf392 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -6675,7 +6675,7 @@ int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, b
/* Enable the tmp_ops to have the same functions as the hash object. */
ftrace_ops_init(&tmp_ops);
- tmp_ops.func_hash->filter_hash = hash;
+ rcu_assign_pointer(tmp_ops.func_hash->filter_hash, hash);
err = register_ftrace_function_nolock(&tmp_ops);
if (err)
diff --git a/kernel/trace/rethook.c b/kernel/trace/rethook.c
index 5a8bdf88999a..87a27f3aa4a6 100644
--- a/kernel/trace/rethook.c
+++ b/kernel/trace/rethook.c
@@ -171,7 +171,7 @@ struct rethook_node *rethook_try_get(struct rethook *rh)
* This expects the caller will set up a rethook on a function entry.
* When the function returns, the rethook will eventually be reclaimed
* or released in the rethook_recycle() with call_rcu().
- * This means the caller must be run in the RCU-availabe context.
+ * This means the caller must be run in the RCU-available context.
*/
if (unlikely(!rcu_is_watching()))
return NULL;
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 9c03a555a6ba..04bb94c29f58 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -904,14 +904,13 @@ static void rb_wake_up_waiters(struct irq_work *work)
struct ring_buffer_per_cpu *cpu_buffer =
container_of(rbwork, struct ring_buffer_per_cpu, irq_work);
- /* Called from interrupt context */
- raw_spin_lock(&cpu_buffer->reader_lock);
- rbwork->wakeup_full = false;
- rbwork->full_waiters_pending = false;
+ scoped_guard(raw_spinlock_irqsave, &cpu_buffer->reader_lock) {
+ rbwork->wakeup_full = false;
+ rbwork->full_waiters_pending = false;
- /* Waking up all waiters, they will reset the shortest full */
- cpu_buffer->shortest_full = 0;
- raw_spin_unlock(&cpu_buffer->reader_lock);
+ /* Waking up all waiters, they will reset the shortest full */
+ cpu_buffer->shortest_full = 0;
+ }
wake_up_all(&rbwork->full_waiters);
}
@@ -7384,7 +7383,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_read_page_data);
/**
* ring_buffer_read_page_size - get size of the read page.
- * @page: the page to get the size from
+ * @rpage: the page to get the size from
*
* Returns size of the page in bytes.
*/
@@ -7474,6 +7473,14 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
old_capacity = rb_subbuf_capacity(buffer);
+ /* The mmap fast path reads subbuf_order without buffer->mutex. */
+ for_each_buffer_cpu(buffer, cpu) {
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
+ continue;
+ if (atomic_read(&buffer->buffers[cpu]->resize_disabled))
+ return -EBUSY;
+ }
+
atomic_inc(&buffer->record_disabled);
/* Make sure all commits have finished */
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8658cad53cb5..e4a490d3d08c 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -7717,12 +7717,55 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt,
return cnt;
}
+static bool tr_option_match(struct trace_array *tr, void *topt)
+{
+ for (int i = 0; i < tr->nr_topts; i++) {
+ struct trace_options *tr_topts = &tr->topts[i];
+
+ if (topt >= (void *)&tr_topts->topts[0] &&
+ topt < (void *)&tr_topts->topts[tr_topts->nr_topts])
+ return true;
+ }
+ return false;
+}
+
+/*
+ * The topt is the address of a trace_array->topts[] element that holds the
+ * the tracer options descriptor. But since the trace_array reference has not
+ * been taken yet, it cannot be dereferenced as it could have been freed by
+ * a rmdir of the instance the trace_array represents.
+ *
+ * Search the list of trace_arrays and compare the topt to the address of
+ * the entire trace_array topts array for each trace_array in the list.
+ * If one is matched, then take the reference and return it. If not, the
+ * trace_array no longer exits.
+ */
+static int trace_array_tracer_options_get(void *topt)
+{
+ struct trace_array *tr;
+ int ret;
+
+ ret = security_locked_down(LOCKDOWN_TRACEFS);
+ if (ret)
+ return ret;
+
+ if (tracing_disabled)
+ return -ENODEV;
+
+ guard(mutex)(&trace_types_lock);
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+ if (tr_option_match(tr, topt))
+ return __trace_array_get(tr);
+ }
+ return -ENODEV;
+}
+
static int tracing_open_options(struct inode *inode, struct file *filp)
{
struct trace_option_dentry *topt = inode->i_private;
int ret;
- ret = tracing_check_open_get_tr(topt->tr);
+ ret = trace_array_tracer_options_get(topt);
if (ret)
return ret;
@@ -7984,6 +8027,7 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer,
tr->topts = tr_topts;
tr->topts[tr->nr_topts].tracer = tracer;
tr->topts[tr->nr_topts].topts = topts;
+ tr->topts[tr->nr_topts].nr_topts = cnt;
tr->nr_topts++;
for (cnt = 0; opts[cnt].name; cnt++) {
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 5e76f94e7a80..3749485a7d85 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -227,6 +227,7 @@ struct array_buffer {
struct trace_options {
struct tracer *tracer;
struct trace_option_dentry *topts;
+ int nr_topts;
};
struct trace_pid_list *trace_pid_list_alloc(void);
@@ -1952,6 +1953,7 @@ struct event_trigger_data {
struct enable_trigger_data {
struct trace_event_file *file;
+ struct trace_event_call *call;
bool enable;
bool hist;
};
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 9dbc2441763b..30c0ddf90887 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1489,6 +1489,8 @@ int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
/* Put back the colon to allow this to be called again */
if (buf)
*(buf - 1) = ':';
+ if (mod)
+ *(mod - 5) = ':';
return ret;
}
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 963e0d6b61fd..8af97fd4ee2d 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -169,7 +169,6 @@ struct hist_field {
struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
struct hist_trigger_data *hist_data;
enum hist_field_fn fn_num;
- unsigned int ref;
unsigned int size;
unsigned int offset;
unsigned int is_signed;
@@ -1913,16 +1912,8 @@ out:
return field_op;
}
-static void get_hist_field(struct hist_field *hist_field)
-{
- hist_field->ref++;
-}
-
static void __destroy_hist_field(struct hist_field *hist_field)
{
- if (--hist_field->ref > 1)
- return;
-
kfree(hist_field->var.name);
kfree(hist_field->name);
@@ -1969,8 +1960,6 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
if (!hist_field)
return NULL;
- hist_field->ref = 1;
-
hist_field->hist_data = hist_data;
if (flags & HIST_FIELD_FL_EXPR || flags & HIST_FIELD_FL_ALIAS)
@@ -2223,10 +2212,8 @@ static struct hist_field *create_var_ref(struct hist_trigger_data *hist_data,
for (i = 0; i < hist_data->n_var_refs; i++) {
ref_field = hist_data->var_refs[i];
if (ref_field->var.idx == var_field->var.idx &&
- ref_field->var.hist_data == var_field->hist_data) {
- get_hist_field(ref_field);
+ ref_field->var.hist_data == var_field->hist_data)
return ref_field;
- }
}
/* Sanity check to avoid out-of-bound write on 'hist_data->var_refs' */
if (hist_data->n_var_refs >= TRACING_MAP_VARS_MAX)
@@ -2234,7 +2221,7 @@ static struct hist_field *create_var_ref(struct hist_trigger_data *hist_data,
ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL);
if (ref_field) {
if (init_var_ref(ref_field, var_field, system, event_name)) {
- destroy_hist_field(ref_field, 0);
+ __destroy_hist_field(ref_field);
return NULL;
}
@@ -2330,6 +2317,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
struct ftrace_event_field *field = NULL;
char *field_name, *modifier, *str;
struct trace_array *tr = file->tr;
+ bool stack_modifier = false;
modifier = str = kstrdup(field_str, GFP_KERNEL);
if (!modifier)
@@ -2352,9 +2340,10 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
*flags |= HIST_FIELD_FL_EXECNAME;
else if (strcmp(modifier, "syscall") == 0)
*flags |= HIST_FIELD_FL_SYSCALL;
- else if (strcmp(modifier, "stacktrace") == 0)
+ else if (strcmp(modifier, "stacktrace") == 0) {
*flags |= HIST_FIELD_FL_STACKTRACE;
- else if (strcmp(modifier, "log2") == 0)
+ stack_modifier = true;
+ } else if (strcmp(modifier, "log2") == 0)
*flags |= HIST_FIELD_FL_LOG2;
else if (strcmp(modifier, "usecs") == 0)
*flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
@@ -2415,6 +2404,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
*flags |= HIST_FIELD_FL_CPU;
} else if (field && field->filter_type == FILTER_STACKTRACE) {
*flags |= HIST_FIELD_FL_STACKTRACE;
+ field = NULL;
} else if (field && field->filter_type == FILTER_COMM) {
*flags |= HIST_FIELD_FL_COMM | HIST_FIELD_FL_STRING;
} else {
@@ -2425,6 +2415,12 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
}
}
}
+
+ if (stack_modifier &&
+ (!field || field->filter_type != FILTER_STACKTRACE)) {
+ hist_err(tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str));
+ field = ERR_PTR(-EINVAL);
+ }
out:
kfree(str);
@@ -3276,7 +3272,6 @@ static struct hist_field *create_var(struct hist_trigger_data *hist_data,
goto out;
}
- var->ref = 1;
var->flags = HIST_FIELD_FL_VAR;
var->var.idx = idx;
var->var.hist_data = var->hist_data = hist_data;
@@ -4313,8 +4308,7 @@ static int __create_val_field(struct hist_trigger_data *hist_data,
goto err;
} else {
/* Value */
- if (hist_field->flags & (HIST_FIELD_FL_GRAPH | HIST_FIELD_FL_PERCENT |
- HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2 |
+ if (hist_field->flags & (HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2 |
HIST_FIELD_FL_SYM | HIST_FIELD_FL_SYM_OFFSET |
HIST_FIELD_FL_SYSCALL | HIST_FIELD_FL_STACKTRACE))
goto err;
@@ -4331,6 +4325,7 @@ static int __create_val_field(struct hist_trigger_data *hist_data,
return ret;
err:
hist_err(file->tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str));
+ destroy_hist_field(hist_field, 0);
return -EINVAL;
}
@@ -5690,7 +5685,7 @@ static int print_entries(struct seq_file *m,
{
struct tracing_map_sort_entry **sort_entries = NULL;
struct tracing_map *map = hist_data->map;
- int i, j, n_entries;
+ int i, j, n_entries, ret;
struct hist_val_stat *stats = NULL;
u64 val;
@@ -5700,6 +5695,8 @@ static int print_entries(struct seq_file *m,
if (n_entries < 0)
return n_entries;
+ ret = n_entries;
+
/* Calculate the max and the total for each field if needed. */
for (j = 0; j < hist_data->n_vals; j++) {
if (!(hist_data->fields[j]->flags &
@@ -5708,7 +5705,7 @@ static int print_entries(struct seq_file *m,
if (!stats) {
stats = kzalloc_objs(*stats, hist_data->n_vals);
if (!stats) {
- n_entries = -ENOMEM;
+ ret = -ENOMEM;
goto out;
}
}
@@ -5729,7 +5726,7 @@ static int print_entries(struct seq_file *m,
out:
tracing_map_destroy_sort_entries(sort_entries, n_entries);
- return n_entries;
+ return ret;
}
static void hist_trigger_show(struct seq_file *m,
@@ -6383,17 +6380,18 @@ static int event_hist_trigger_named_init(struct event_trigger_data *data)
{
int ret;
- data->ref++;
-
- save_named_trigger(data->named_data->name, data);
-
ret = event_hist_trigger_init(data->named_data);
if (ret < 0) {
kfree(data->cmd_ops);
data->cmd_ops = &trigger_hist_cmd;
+ return ret;
}
- return ret;
+ data->ref++;
+
+ save_named_trigger(data->named_data->name, data);
+
+ return 0;
}
static void event_hist_trigger_named_free(struct event_trigger_data *data)
@@ -6643,12 +6641,6 @@ static int hist_register_trigger(char *glob,
data->cmd_ops = cmd_ops;
}
- if (data->cmd_ops->init) {
- ret = data->cmd_ops->init(data);
- if (ret < 0)
- goto out;
- }
-
if (hist_data->enable_timestamps) {
char *clock = hist_data->attrs->clock;
@@ -6661,6 +6653,15 @@ static int hist_register_trigger(char *glob,
tracing_set_filter_buffering(file->tr, true);
}
+ if (data->cmd_ops->init) {
+ ret = data->cmd_ops->init(data);
+ if (ret < 0) {
+ if (hist_data->enable_timestamps)
+ tracing_set_filter_buffering(file->tr, false);
+ goto out;
+ }
+ }
+
if (named_data) {
remove_hist_vars(hist_data);
destroy_hist_data(hist_data);
@@ -6678,11 +6679,12 @@ static int hist_trigger_enable(struct event_trigger_data *data,
update_cond_flag(file);
- if (trace_event_trigger_enable_disable(file, 1) < 0) {
- list_del_rcu(&data->list);
- update_cond_flag(file);
+ /*
+ * On failure the caller undoes the registration, and
+ * hist_unregister_trigger() can only find the trigger here.
+ */
+ if (trace_event_trigger_enable_disable(file, 1) < 0)
ret--;
- }
return ret;
}
@@ -6760,13 +6762,13 @@ static void hist_unregister_trigger(char *glob,
}
}
- if (test && test->cmd_ops->free)
- test->cmd_ops->free(test);
-
if (hist_data->enable_timestamps) {
if (!hist_data->remove || test)
tracing_set_filter_buffering(file->tr, false);
}
+
+ if (test && test->cmd_ops->free)
+ test->cmd_ops->free(test);
}
static bool hist_file_check_refs(struct trace_event_file *file)
@@ -6971,6 +6973,8 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,
return ret;
out_unreg:
event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
+ /* The unregister frees trigger_data, skip out_free */
+ goto out;
out_free:
remove_hist_vars(hist_data);
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 149300cc5e8a..4d2fde996c0f 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -1728,7 +1728,8 @@ static void enable_trigger_private_data_free(struct event_trigger_data *data)
{
struct enable_trigger_data *enable_data = data->private_data;
- trace_event_put_ref(enable_data->file->event_call);
+ /* The file may already be freed here, only the call is kept alive */
+ trace_event_put_ref(enable_data->call);
kfree(enable_data);
}
@@ -1801,6 +1802,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
enable_data->hist = hist;
enable_data->enable = enable;
enable_data->file = event_enable_file;
+ enable_data->call = event_enable_file->event_call;
trigger_data = trigger_data_alloc(cmd_ops, cmd, param, enable_data);
if (!trigger_data)
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index 93cda2f6f269..f658c3a77aa7 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -1122,10 +1122,9 @@ static void user_event_destroy_validators(struct user_event *user)
}
}
-static void user_event_destroy_fields(struct user_event *user)
+static void user_event_destroy_fields(struct list_head *head)
{
struct ftrace_event_field *field, *next;
- struct list_head *head = &user->fields;
list_for_each_entry_safe(field, next, head, link) {
list_del(&field->link);
@@ -1502,17 +1501,32 @@ static int user_event_set_call_visible(struct user_event *user, bool visible)
static int destroy_user_event(struct user_event *user)
{
+ LIST_HEAD(fields);
int ret = 0;
lockdep_assert_held(&event_mutex);
- /* Must destroy fields before call removal */
- user_event_destroy_fields(user);
+ /*
+ * Detach the fields before removing the call. Removing the event
+ * frees the field list memory (trace_destroy_fields() is run on
+ * successful removal and kmem_cache_free()s the fields), but the
+ * fields here are allocated and owned by user_events. Destroy
+ * them separately once removal has succeeded.
+ */
+ list_splice_init(&user->fields, &fields);
ret = user_event_set_call_visible(user, false);
- if (ret)
+ if (ret) {
+ /*
+ * Removal failed and the event stays registered, recover
+ * the fields so it is left in a consistent state.
+ */
+ list_splice(&fields, &user->fields);
return ret;
+ }
+
+ user_event_destroy_fields(&fields);
dyn_event_remove(&user->devent);
hash_del(&user->node);
@@ -2212,7 +2226,7 @@ static int user_event_parse(struct user_event_group *group, char *name,
put_user_lock:
mutex_unlock(&event_mutex);
put_user:
- user_event_destroy_fields(user);
+ user_event_destroy_fields(&user->fields);
user_event_destroy_validators(user);
kfree(user->call.print_fmt);
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index ff7cb1a76b95..c5befd0c5b9a 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -52,6 +52,7 @@ struct fgraph_data {
};
struct ftrace_graph_ret_entry ret;
int failed;
+ int ent_size;
int cpu;
};
@@ -1274,6 +1275,7 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
if (s->full) {
data->failed = 1;
data->cpu = cpu;
+ data->ent_size = iter->ent_size;
} else
data->failed = 0;
}
@@ -1457,6 +1459,7 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags)
if (data && data->failed) {
field = &data->ent.ent;
iter->cpu = data->cpu;
+ iter->ent_size = data->ent_size;
ret = print_graph_entry(field, s, iter, flags);
if (ret == TRACE_TYPE_HANDLED && iter->cpu != cpu) {
per_cpu_ptr(data->cpu_data, iter->cpu)->ignore = 1;
diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
index 75fa1ffc4c96..2d5bc423efca 100644
--- a/kernel/trace/trace_remote.c
+++ b/kernel/trace/trace_remote.c
@@ -980,9 +980,12 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size,
const struct cpumask *cpumask)
{
size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask));
- unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1;
struct ring_buffer_desc *rb_desc;
int cpu, ret = -ENOMEM;
+ unsigned int nr_pages;
+
+ if (min_desc_size == SIZE_MAX)
+ return -E2BIG;
if (desc_size < min_desc_size)
return -EINVAL;
@@ -991,6 +994,7 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size,
desc->struct_len = min_desc_size;
rb_desc = __first_ring_buffer_desc(desc);
+ nr_pages = __calc_nr_pages_ring_buffer_desc(buffer_size);
for_each_cpu(cpu, cpumask) {
unsigned int id;
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 1ae3732a2c51..959525393739 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3918,7 +3918,7 @@ static void check_flush_dependency(struct workqueue_struct *target_wq,
WARN_ONCE(current->flags & PF_MEMALLOC,
"workqueue: PF_MEMALLOC task %d(%s) is flushing !WQ_MEM_RECLAIM %s:%ps",
current->pid, current->comm, target_wq->name, target_func);
- WARN_ONCE(worker && ((worker->current_pwq->wq->flags &
+ WARN_ONCE(worker && worker->current_pwq && ((worker->current_pwq->wq->flags &
(WQ_MEM_RECLAIM | __WQ_LEGACY)) == WQ_MEM_RECLAIM),
"workqueue: WQ_MEM_RECLAIM %s:%ps is flushing !WQ_MEM_RECLAIM %s:%ps",
worker->current_pwq->wq->name, worker->current_func,