sched_ext and large cpu counts
Subsystems (2)
Hi Andrea (and other sched_ext folks),
I've got some partners with systems with > 4096 cpus. On those systems sched_ext crashes at boot due to:
init_sched_ext_class() {
...
scx_kick_cpus_pnt_seqs =
__alloc_percpu(sizeof(scx_kick_cpus_pnt_seqs[0]) * nr_cpu_ids,
__alignof__(scx_kick_cpus_pnt_seqs[0]));
BUG_ON(!scx_kick_cpus_pnt_seqs);...
4096 * 8 bytes is 32768 and is the max you can precpu allocate. Anything more and the _alloc_percpu fails and WARNs
[ 0.000000] illegal size (33792) or align (8) for percpu allocation [ 0.000000] WARNING: CPU: 0 PID: 0 at mm/percpu.c:1779 pcpu_alloc_noprof+0x715/0x820
I started looking into changing that to static which would have to be based on NR_CPUS (8192 in our case). Because it's N^2 that starts to be a lot space.
While looking at how it's used I had a different question. The comment says
* We busy-wait here to guarantee that no other task can
* be scheduled on our core before the target CPU has
* entered the resched path.
But pnt_seq is actually only updated if we enter the resched path AND switch classes. That seems more restrictive that the comment seems to require, no?
Any ideas on how to do this in different way?
I'd rather not have to turn off the CONFIG but 512 MB is a lot of space to allocate for this.
Thanks for taking a look and any suggestions.
Cheers,
Phil
Tejun Heo
reviewerHello,
Can you please see whether the following patch resolves the problem?
Thanks.
--
tejun
----- 8< -----
From 4d7f7d24e90fba47bb08ddbeb8668123b4bbab1b Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Tue, 7 Oct 2025 16:23:43 -1000
Subject: [PATCH] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using
kvzalloc()
On systems with >4096 CPUs, scx_kick_cpus_pnt_seqs allocation fails during boot because it exceeds the 32,768 byte percpu allocator limit. The allocation size is sizeof(unsigned long) * nr_cpu_ids, which becomes 33,792 bytes with 4224 CPUs.
Restructure scx_kick_cpus_pnt_seqs to use DEFINE_PER_CPU() for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. This avoids percpu allocator size limits. Additionally, move allocation from boot time to scx_enable() and free in scx_disable(), so the O(nr_cpu_ids^2) memory is only consumed when sched_ext is active.
--- kernel/sched/ext.c | 59 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-)
kernel/sched/ext.c +49 -10
@@ -67,8 +67,13 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of sequence numbers. +* The arrays are allocated with kvzalloc() as size can exceed percpu allocator +* limits on large machines. O(nr_cpu_ids^2) allocation, allocated lazily when +* enabling and freed when disabling to avoid waste when sched_ext isn't active. +*/ +static DEFINE_PER_CPU(unsigned long *, scx_kick_cpus_pnt_seqs);/* * Direct dispatch marker.
@@ -3850,6 +3855,16 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_cpus_pnt_seqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +kvfree (per_cpu (scx_kick_cpus_pnt_seqs, cpu)); +per_cpu (scx_kick_cpus_pnt_seqs, cpu) =NULL ; + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);
@@ -3986,6 +4001,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_cpus_pnt_seqs ();mutex_unlock (&scx_enable_mutex);
@@ -4348,6 +4364,28 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_cpus_pnt_seqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +WARN_ON_ONCE (per_cpu (scx_kick_cpus_pnt_seqs, cpu)); +per_cpu (scx_kick_cpus_pnt_seqs, cpu) = +kvzalloc_node (sizeof (unsigned long ) * nr_cpu_ids, +GFP_KERNEL ,cpu_to_node (cpu)); +if (!per_cpu (scx_kick_cpus_pnt_seqs, cpu)) { +free_kick_cpus_pnt_seqs (); +return -ENOMEM ; + } + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;
@@ -4490,15 +4528,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_cpus_pnt_seqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/*
@@ -4701,6 +4743,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_cpus_pnt_seqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;
@@ -5082,7 +5126,7 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +unsigned long *pseqs =__this_cpu_read (scx_kick_cpus_pnt_seqs);bool should_wait = false;s32 cpu;
@@ -5208,11 +5252,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);
Signature · 1 line
2.51.0
Andrea Righi
reviewerHi Tejun and Phil,
Quoted 140 lines · Tejun Heo
Hello,
Can you please see whether the following patch resolves the problem?
Thanks.
--
tejun----- 8< -----
From4d7f7d24e90fba47bb08ddbeb8668123b4bbab1bMon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Tue, 7 Oct 2025 16:23:43 -1000
Subject: [PATCH]sched_ext: Allocatescx_kick_cpus_pnt_seqslazily using
kvzalloc()On systems with >4096 CPUs,
scx_kick_cpus_pnt_seqsallocation fails during boot because it exceeds the 32,768 byte percpu allocator limit. The allocation size is sizeof(unsigned long) *nr_cpu_ids, which becomes 33,792 bytes with 4224 CPUs.Restructure
scx_kick_cpus_pnt_seqsto useDEFINE_PER_CPU()for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. This avoids percpu allocator size limits. Additionally, move allocation from boot time toscx_enable()and free inscx_disable(), so theO(nr_cpu_ids^2)memory is only consumed whensched_extis active.Reported-by Phil Auld <[email protected]>Signed-off-by Tejun Heo <[email protected]>--- kernel/sched/ext.c | 59 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-)kernel/sched/ext.c +48
-4@@ -67,8 +67,13 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of sequence numbers. +* The arrays are allocated with kvzalloc() as size can exceed percpu allocator +* limits on large machines. O(nr_cpu_ids^2) allocation, allocated lazily when +* enabling and freed when disabling to avoid waste when sched_ext isn't active. +*/ +static DEFINE_PER_CPU(unsigned long *, scx_kick_cpus_pnt_seqs);/* * Direct dispatch marker. @@ -3850,6 +3855,16 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_cpus_pnt_seqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +kvfree (per_cpu (scx_kick_cpus_pnt_seqs, cpu)); +per_cpu (scx_kick_cpus_pnt_seqs, cpu) =NULL ; + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);@@ -3986,6 +4001,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_cpus_pnt_seqs ();mutex_unlock (&scx_enable_mutex);@@ -4348,6 +4364,28 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_cpus_pnt_seqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +WARN_ON_ONCE (per_cpu (scx_kick_cpus_pnt_seqs, cpu)); +per_cpu (scx_kick_cpus_pnt_seqs, cpu) = +kvzalloc_node (sizeof (unsigned long ) * nr_cpu_ids, +GFP_KERNEL ,cpu_to_node (cpu)); +if (!per_cpu (scx_kick_cpus_pnt_seqs, cpu)) { +free_kick_cpus_pnt_seqs (); +return -ENOMEM ; + } + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;@@ -4490,15 +4528,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_cpus_pnt_seqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/* @@ -4701,6 +4743,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_cpus_pnt_seqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;
kernel/sched/ext.c static void kick_cpus_irq_workfn(struct irq_work *irq_work) · line 5131
Quoted 7 lines
kernel/sched/ext.c +1
-1@@ -5082,7 +5126,7 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +unsigned long *pseqs =__this_cpu_read (scx_kick_cpus_pnt_seqs);bool should_wait = false;s32 cpu;
Should we add:
if (WARN_ON_ONCE(!pseqs)) return;
Not sure if we can race with scx_disable_workfn() here. But for a test the patch looks fine as it is.
Thanks!
-Andrea
Quoted 16 lines
kernel/sched/ext.c +0
-5@@ -5208,11 +5252,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);Signature · 1 line
2.51.0
Tejun Heo
reviewerQuoted 11 lines · Andrea Righi
kernel/sched/ext.c static void kick_cpus_irq_workfn(struct irq_work *irq_work) · line 5131
Quoted 8 lines
kernel/sched/ext.c +1
-1@@ -5082,7 +5126,7 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +unsigned long *pseqs =__this_cpu_read (scx_kick_cpus_pnt_seqs);bool should_wait = false;s32 cpu;Should we add:
Quoted 5 lines
if (WARN_ON_ONCE(!pseqs)) return;Not sure if we can race with
scx_disable_workfn()here. But for a test the patch looks fine as it is.
Oh yeah, I think it can. We should probably RCU free the arrays and add a NULL check here.
Thanks.
Signature · 1 line
tejun
Tejun Heo
reviewerPATCH v2 1/1 sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
On systems with >4096 CPUs, scx_kick_cpus_pnt_seqs allocation fails during boot because it exceeds the 32,768 byte percpu allocator limit.
Restructure to use DEFINE_PER_CPU() for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. Move allocation from boot time to scx_enable() and free in scx_disable(), so the O(nr_cpu_ids^2) memory is only consumed when sched_ext is active.
Use RCU to guard against racing with free. Arrays are freed via call_rcu() and kick_cpus_irq_workfn() uses rcu_dereference_bh() with a NULL check.
While at it, rename to scx_kick_pseqs for brevity and update comments to clarify these are pick_task sequence numbers.
--- kernel/sched/ext.c | 88 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 10 deletions(-)
kernel/sched/ext.c +78 -10
@@ -67,8 +67,19 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of pick_task sequence +* numbers. The arrays are allocated with kvzalloc() as size can exceed percpu +* allocator limits on large machines. O(nr_cpu_ids^2) allocation, allocated +* lazily when enabling and freed when disabling to avoid waste when sched_ext +* isn't active. +*/ +struct scx_kick_pseqs { +struct rcu_head rcu; +unsigned long seqs[]; +}; + +static DEFINE_PER_CPU(struct scx_kick_pseqs __rcu *, scx_kick_pseqs);/* * Direct dispatch marker.
@@ -3850,6 +3861,25 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_pseqs_rcu (struct rcu_head *rcu) +{ + struct scx_kick_pseqs *pseqs = container_of(rcu, struct scx_kick_pseqs, rcu); + +kvfree (pseqs); +} + +static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); + +call_rcu (&(*pseqs)->rcu, free_kick_pseqs_rcu); +RCU_INIT_POINTER (*pseqs,NULL ); + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);
@@ -3986,6 +4016,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_pseqs ();mutex_unlock (&scx_enable_mutex);
@@ -4348,6 +4379,34 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_pseqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *new_pseqs; + + +WARN_ON_ONCE (rcu_access_pointer (*pseqs)); + + new_pseqs =kvzalloc_node (sizeof (unsigned long ) * nr_cpu_ids, +GFP_KERNEL ,cpu_to_node (cpu)); +if (!new_pseqs) { +free_kick_pseqs (); +return -ENOMEM ; + } + +rcu_assign_pointer (*pseqs, new_pseqs); + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;
@@ -4490,15 +4549,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_pseqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/*
@@ -4701,6 +4764,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_pseqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;
@@ -5082,10 +5147,18 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +struct scx_kick_pseqs __rcu *pseqs_pcpu =__this_cpu_read (scx_kick_pseqs);bool should_wait = false; +unsigned long *pseqs;s32 cpu; +if (unlikely (!pseqs_pcpu)) { +pr_warn_once ("kick_cpus_irq_workfn() called with NULL scx_kick_pseqs" ); +return ; + } + + pseqs =rcu_dereference_bh (pseqs_pcpu)->seqs; +for_each_cpu (cpu, this_scx->cpus_to_kick) { should_wait |=kick_one_cpu (cpu, this_rq, pseqs);cpumask_clear_cpu (cpu, this_scx->cpus_to_kick);
@@ -5208,11 +5281,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);
Signature · 1 line
2.51.0
Andrea Righi
reviewerRe: [PATCH v2] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
Hi Tejun,
Quoted 57 lines · Tejun Heo
On systems with >4096 CPUs,
scx_kick_cpus_pnt_seqsallocation fails during boot because it exceeds the 32,768 byte percpu allocator limit.Restructure to use
DEFINE_PER_CPU()for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. Move allocation from boot time toscx_enable()and free inscx_disable(), so theO(nr_cpu_ids^2)memory is only consumed whensched_extis active.Use RCU to guard against racing with free. Arrays are freed via
call_rcu()andkick_cpus_irq_workfn()usesrcu_dereference_bh()with a NULL check.While at it, rename to
scx_kick_pseqsfor brevity and update comments to clarify these arepick_tasksequence numbers.Reported-by Phil Auld <[email protected]>Signed-off-by Tejun Heo <[email protected]>--- kernel/sched/ext.c | 88 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 10 deletions(-)kernel/sched/ext.c +19
-2@@ -67,8 +67,19 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of pick_task sequence +* numbers. The arrays are allocated with kvzalloc() as size can exceed percpu +* allocator limits on large machines. O(nr_cpu_ids^2) allocation, allocated +* lazily when enabling and freed when disabling to avoid waste when sched_ext +* isn't active. +*/ +struct scx_kick_pseqs { +struct rcu_head rcu; +unsigned long seqs[]; +}; + +static DEFINE_PER_CPU(struct scx_kick_pseqs __rcu *, scx_kick_pseqs);/* * Direct dispatch marker. @@ -3850,6 +3861,25 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_pseqs_rcu (struct rcu_head *rcu) +{ + struct scx_kick_pseqs *pseqs = container_of(rcu, struct scx_kick_pseqs, rcu); + +kvfree (pseqs); +}
kernel/sched/ext.c static const char *scx_exit_reason(enum scx_exit_kind kind) · line 3879
Quoted 10 lines
kernel/sched/ext.c +10
-0+ +static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); + +call_rcu (&(*pseqs)->rcu, free_kick_pseqs_rcu); +RCU_INIT_POINTER (*pseqs,NULL );
Is this safe? I think we should replace the pointer first and then schedule the free via call_rcu(), like:
old = rcu_replace_pointer(*pseqs, NULL, true);
if (old)
call_rcu(&old->rcu, free_kick_pseqs_rcu);Quoted 21 lines
kernel/sched/ext.c +7
-0+ } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);@@ -3986,6 +4016,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_pseqs ();mutex_unlock (&scx_enable_mutex);@@ -4348,6 +4379,34 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_pseqs (void ) +{ +int cpu;
kernel/sched/ext.c static void scx_vexit(struct scx_sched *sch, · line 4394
Quoted 10 lines
kernel/sched/ext.c +10
-0+ +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *new_pseqs; + +
nit: extra newline.
kernel/sched/ext.c static void scx_vexit(struct scx_sched *sch, · line 4398
Quoted 4 lines
kernel/sched/ext.c +4
-0+WARN_ON_ONCE (rcu_access_pointer (*pseqs)); + + new_pseqs =kvzalloc_node (sizeof (unsigned long ) * nr_cpu_ids, +GFP_KERNEL ,cpu_to_node (cpu));
Don't we need to allocate the struct as well? This should be something like:
new_pseqs = kvzalloc_node(struct_size(new_pseqs, seqs, nr_cpu_ids), GFP_KERNEL, cpu_to_node(cpu));
Quoted 80 lines
kernel/sched/ext.c +28
-8+if (!new_pseqs) { +free_kick_pseqs (); +return -ENOMEM ; + } + +rcu_assign_pointer (*pseqs, new_pseqs); + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;@@ -4490,15 +4549,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_pseqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/* @@ -4701,6 +4764,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_pseqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;@@ -5082,10 +5147,18 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +struct scx_kick_pseqs __rcu *pseqs_pcpu =__this_cpu_read (scx_kick_pseqs);bool should_wait = false; +unsigned long *pseqs;s32 cpu; +if (unlikely (!pseqs_pcpu)) { +pr_warn_once ("kick_cpus_irq_workfn() called with NULL scx_kick_pseqs" ); +return ; + } + + pseqs =rcu_dereference_bh (pseqs_pcpu)->seqs; +for_each_cpu (cpu, this_scx->cpus_to_kick) { should_wait |=kick_one_cpu (cpu, this_rq, pseqs);cpumask_clear_cpu (cpu, this_scx->cpus_to_kick);@@ -5208,11 +5281,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);Signature · 1 line
2.51.0
Thanks,
-Andrea
Tejun Heo
reviewerRe: [PATCH v2] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
Hello,
Quoted 13 lines · Andrea Righi
kernel/sched/ext.c static const char *scx_exit_reason(enum scx_exit_kind kind) · line 3879
Quoted 9 lines
kernel/sched/ext.c +9
-0+static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); + +call_rcu (&(*pseqs)->rcu, free_kick_pseqs_rcu); +RCU_INIT_POINTER (*pseqs,NULL );Is this safe? I think we should replace the pointer first and then schedule the free via
call_rcu(), like:
Quoted 3 lines
old = rcu_replace_pointer(*pseqs, NULL, true);
if (old)
call_rcu(&old->rcu, free_kick_pseqs_rcu);*pseqs is the deferenced per-cpu pointer and we're freeding the struct that's pointed by it. Whether the struct is freed or not doesn't really affect the pointer storage. Let's say the struct gets freed between call_rcu() and RCU_INIT_POINTER(). Then, *pseqs is just a dangling pointer inbetween until it gets cleared.
Quoted 10 lines
kernel/sched/ext.c static void scx_vexit(struct scx_sched *sch, · line 4398
Quoted 4 lines
kernel/sched/ext.c +4
-0+WARN_ON_ONCE (rcu_access_pointer (*pseqs)); + + new_pseqs =kvzalloc_node (sizeof (unsigned long ) * nr_cpu_ids, +GFP_KERNEL ,cpu_to_node (cpu));Don't we need to allocate the struct as well? This should be something like:
new_pseqs = kvzalloc_node(struct_size(new_pseqs, seqs, nr_cpu_ids), GFP_KERNEL, cpu_to_node(cpu));
Yes, will post the fixed version.
Thanks.
Signature · 1 line
tejun
Tejun Heo
reviewerRe: [PATCH v2] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
Hello, again.
Quoted 18 lines · Tejun Heo
Quoted 13 lines · Andrea Righi
kernel/sched/ext.c static const char *scx_exit_reason(enum scx_exit_kind kind) · line 3879
Quoted 9 lines
kernel/sched/ext.c +9
-0+static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); + +call_rcu (&(*pseqs)->rcu, free_kick_pseqs_rcu); +RCU_INIT_POINTER (*pseqs,NULL );Is this safe? I think we should replace the pointer first and then schedule the free via
call_rcu(), like:Quoted 3 lines
old = rcu_replace_pointer(*pseqs, NULL, true); if (old) call_rcu(&old->rcu, free_kick_pseqs_rcu);
Quoted 5 lines
*pseqs is the deferenced per-cpu pointer and we're freeding the struct that's pointed by it. Whether the struct is freed or not doesn't really affect the pointer storage. Let's say the struct gets freed between
call_rcu()andRCU_INIT_POINTER(). Then, *pseqs is just a dangling pointer inbetween until it gets cleared.
Wait, you're right. The problem is that the readers can end up fetching the pointer afterwards. Will fix it.
Thanks.
Signature · 1 line
tejun
Tejun Heo
reviewerPATCH v3 1/1 sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
In reply to Tejun HeoOn systems with >4096 CPUs, scx_kick_cpus_pnt_seqs allocation fails during boot because it exceeds the 32,768 byte percpu allocator limit.
Restructure to use DEFINE_PER_CPU() for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. Move allocation from boot time to scx_enable() and free in scx_disable(), so the O(nr_cpu_ids^2) memory is only consumed when sched_ext is active.
Use RCU to guard against racing with free. Arrays are freed via call_rcu() and kick_cpus_irq_workfn() uses rcu_dereference_bh() with a NULL check.
While at it, rename to scx_kick_pseqs for brevity and update comments to clarify these are pick_task sequence numbers.
v2: RCU protect scx_kick_seqs to manage kick_cpus_irq_workfn() racing
against disable as per Andrea.
v3: Fix bugs notcied by Andrea.
--- kernel/sched/ext.c | 89 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 10 deletions(-)
kernel/sched/ext.c +79 -10
@@ -67,8 +67,19 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of pick_task sequence +* numbers. The arrays are allocated with kvzalloc() as size can exceed percpu +* allocator limits on large machines. O(nr_cpu_ids^2) allocation, allocated +* lazily when enabling and freed when disabling to avoid waste when sched_ext +* isn't active. +*/ +struct scx_kick_pseqs { +struct rcu_head rcu; +unsigned long seqs[]; +}; + +static DEFINE_PER_CPU(struct scx_kick_pseqs __rcu *, scx_kick_pseqs);/* * Direct dispatch marker.
@@ -3850,6 +3861,27 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_pseqs_rcu (struct rcu_head *rcu) +{ + struct scx_kick_pseqs *pseqs = container_of(rcu, struct scx_kick_pseqs, rcu); + +kvfree (pseqs); +} + +static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *to_free; + + to_free =rcu_replace_pointer (*pseqs,NULL , true); +if (to_free) +call_rcu (&to_free->rcu, free_kick_pseqs_rcu); + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);
@@ -3986,6 +4018,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_pseqs ();mutex_unlock (&scx_enable_mutex);
@@ -4348,6 +4381,33 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_pseqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *new_pseqs; + +WARN_ON_ONCE (rcu_access_pointer (*pseqs)); + + new_pseqs =kvzalloc_node (struct_size (new_pseqs, seqs, nr_cpu_ids), +GFP_KERNEL ,cpu_to_node (cpu)); +if (!new_pseqs) { +free_kick_pseqs (); +return -ENOMEM ; + } + +rcu_assign_pointer (*pseqs, new_pseqs); + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;
@@ -4490,15 +4550,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_pseqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/*
@@ -4701,6 +4765,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_pseqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;
@@ -5082,10 +5148,18 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +struct scx_kick_pseqs __rcu *pseqs_pcpu =__this_cpu_read (scx_kick_pseqs);bool should_wait = false; +unsigned long *pseqs;s32 cpu; +if (unlikely (!pseqs_pcpu)) { +pr_warn_once ("kick_cpus_irq_workfn() called with NULL scx_kick_pseqs" ); +return ; + } + + pseqs =rcu_dereference_bh (pseqs_pcpu)->seqs; +for_each_cpu (cpu, this_scx->cpus_to_kick) { should_wait |=kick_one_cpu (cpu, this_rq, pseqs);cpumask_clear_cpu (cpu, this_scx->cpus_to_kick);
@@ -5208,11 +5282,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);
Signature · 1 line
2.51.0
Re: [PATCH v3] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
Hi Tejun,
Quoted 17 lines · Tejun Heo
On systems with >4096 CPUs,
scx_kick_cpus_pnt_seqsallocation fails during boot because it exceeds the 32,768 byte percpu allocator limit.Restructure to use
DEFINE_PER_CPU()for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. Move allocation from boot time toscx_enable()and free inscx_disable(), so theO(nr_cpu_ids^2)memory is only consumed whensched_extis active.Use RCU to guard against racing with free. Arrays are freed via
call_rcu()andkick_cpus_irq_workfn()usesrcu_dereference_bh()with a NULL check.While at it, rename to
scx_kick_pseqsfor brevity and update comments to clarify these arepick_tasksequence numbers.v2: RCU protect
scx_kick_seqsto managekick_cpus_irq_workfn()racing
against disable as per Andrea.
Quoted 6 lines
v3: Fix bugs notcied by Andrea.
Reported-by Phil Auld <[email protected]>Signed-off-by Tejun Heo <[email protected]>Cc Andrea Righi <[email protected]>
Looks good now!
Thanks,
-Andrea
Quoted 166 lines
--- kernel/sched/ext.c | 89 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 10 deletions(-)kernel/sched/ext.c +79
-10@@ -67,8 +67,19 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of pick_task sequence +* numbers. The arrays are allocated with kvzalloc() as size can exceed percpu +* allocator limits on large machines. O(nr_cpu_ids^2) allocation, allocated +* lazily when enabling and freed when disabling to avoid waste when sched_ext +* isn't active. +*/ +struct scx_kick_pseqs { +struct rcu_head rcu; +unsigned long seqs[]; +}; + +static DEFINE_PER_CPU(struct scx_kick_pseqs __rcu *, scx_kick_pseqs);/* * Direct dispatch marker. @@ -3850,6 +3861,27 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_pseqs_rcu (struct rcu_head *rcu) +{ + struct scx_kick_pseqs *pseqs = container_of(rcu, struct scx_kick_pseqs, rcu); + +kvfree (pseqs); +} + +static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *to_free; + + to_free =rcu_replace_pointer (*pseqs,NULL , true); +if (to_free) +call_rcu (&to_free->rcu, free_kick_pseqs_rcu); + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);@@ -3986,6 +4018,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_pseqs ();mutex_unlock (&scx_enable_mutex);@@ -4348,6 +4381,33 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_pseqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *new_pseqs; + +WARN_ON_ONCE (rcu_access_pointer (*pseqs)); + + new_pseqs =kvzalloc_node (struct_size (new_pseqs, seqs, nr_cpu_ids), +GFP_KERNEL ,cpu_to_node (cpu)); +if (!new_pseqs) { +free_kick_pseqs (); +return -ENOMEM ; + } + +rcu_assign_pointer (*pseqs, new_pseqs); + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;@@ -4490,15 +4550,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_pseqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/* @@ -4701,6 +4765,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_pseqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;@@ -5082,10 +5148,18 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +struct scx_kick_pseqs __rcu *pseqs_pcpu =__this_cpu_read (scx_kick_pseqs);bool should_wait = false; +unsigned long *pseqs;s32 cpu; +if (unlikely (!pseqs_pcpu)) { +pr_warn_once ("kick_cpus_irq_workfn() called with NULL scx_kick_pseqs" ); +return ; + } + + pseqs =rcu_dereference_bh (pseqs_pcpu)->seqs; +for_each_cpu (cpu, this_scx->cpus_to_kick) { should_wait |=kick_one_cpu (cpu, this_rq, pseqs);cpumask_clear_cpu (cpu, this_scx->cpus_to_kick);@@ -5208,11 +5282,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);Signature · 1 line
2.51.0
Re: [PATCH v3] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
In reply to Tejun HeoHi Tejun,
Quoted 11 lines · On Wed, Oct 08, 2025 at 01:43:26PM -1000 Tejun Heo wrote:
On systems with >4096 CPUs,
scx_kick_cpus_pnt_seqsallocation fails during boot because it exceeds the 32,768 byte percpu allocator limit.Restructure to use
DEFINE_PER_CPU()for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. Move allocation from boot time toscx_enable()and free inscx_disable(), so theO(nr_cpu_ids^2)memory is only consumed whensched_extis active.Use RCU to guard against racing with free. Arrays are freed via
call_rcu()andkick_cpus_irq_workfn()usesrcu_dereference_bh()with a NULL check.
Quoted 7 lines
While at it, rename to
scx_kick_pseqsfor brevity and update comments to clarify these arepick_tasksequence numbers.v2: RCU protect
scx_kick_seqsto managekick_cpus_irq_workfn()racing
against disable as per Andrea.v3: Fix bugs notcied by Andrea.
This looks good to me. The original patch of course did solve the boot issue. They aren't using scx so there were no other issues seen. I've kicked off a build with this version and will try to get that booted on the large system as well. It should solve that problem just as well as the original one obviously.
Thanks for the quick turn around.
I'm not going to offer a Tested tag since it was only boot tested (with no scx use) on the large system. And all that really tests is the 5 lines removed from init_sched_ext_class...
Cheers,
Phil
Quoted 171 lines
Reported-by Phil Auld <[email protected]>Signed-off-by Tejun Heo <[email protected]>Cc Andrea Righi <[email protected]>--- kernel/sched/ext.c | 89 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 10 deletions(-)kernel/sched/ext.c +79
-10@@ -67,8 +67,19 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of pick_task sequence +* numbers. The arrays are allocated with kvzalloc() as size can exceed percpu +* allocator limits on large machines. O(nr_cpu_ids^2) allocation, allocated +* lazily when enabling and freed when disabling to avoid waste when sched_ext +* isn't active. +*/ +struct scx_kick_pseqs { +struct rcu_head rcu; +unsigned long seqs[]; +}; + +static DEFINE_PER_CPU(struct scx_kick_pseqs __rcu *, scx_kick_pseqs);/* * Direct dispatch marker. @@ -3850,6 +3861,27 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_pseqs_rcu (struct rcu_head *rcu) +{ + struct scx_kick_pseqs *pseqs = container_of(rcu, struct scx_kick_pseqs, rcu); + +kvfree (pseqs); +} + +static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *to_free; + + to_free =rcu_replace_pointer (*pseqs,NULL , true); +if (to_free) +call_rcu (&to_free->rcu, free_kick_pseqs_rcu); + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);@@ -3986,6 +4018,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_pseqs ();mutex_unlock (&scx_enable_mutex);@@ -4348,6 +4381,33 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_pseqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *new_pseqs; + +WARN_ON_ONCE (rcu_access_pointer (*pseqs)); + + new_pseqs =kvzalloc_node (struct_size (new_pseqs, seqs, nr_cpu_ids), +GFP_KERNEL ,cpu_to_node (cpu)); +if (!new_pseqs) { +free_kick_pseqs (); +return -ENOMEM ; + } + +rcu_assign_pointer (*pseqs, new_pseqs); + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;@@ -4490,15 +4550,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_pseqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/* @@ -4701,6 +4765,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_pseqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;@@ -5082,10 +5148,18 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +struct scx_kick_pseqs __rcu *pseqs_pcpu =__this_cpu_read (scx_kick_pseqs);bool should_wait = false; +unsigned long *pseqs;s32 cpu; +if (unlikely (!pseqs_pcpu)) { +pr_warn_once ("kick_cpus_irq_workfn() called with NULL scx_kick_pseqs" ); +return ; + } + + pseqs =rcu_dereference_bh (pseqs_pcpu)->seqs; +for_each_cpu (cpu, this_scx->cpus_to_kick) { should_wait |=kick_one_cpu (cpu, this_rq, pseqs);cpumask_clear_cpu (cpu, this_scx->cpus_to_kick);@@ -5208,11 +5282,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);Signature · 1 line
2.51.0
Re: [PATCH v3] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
Quoted 20 lines · On Thu, Oct 09, 2025 at 08:06:40AM -0400 Phil Auld wrote:
Hi Tejun,
Quoted 17 lines · On Wed, Oct 08, 2025 at 01:43:26PM -1000 Tejun Heo wrote:
On systems with >4096 CPUs,
scx_kick_cpus_pnt_seqsallocation fails during boot because it exceeds the 32,768 byte percpu allocator limit.Restructure to use
DEFINE_PER_CPU()for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. Move allocation from boot time toscx_enable()and free inscx_disable(), so theO(nr_cpu_ids^2)memory is only consumed whensched_extis active.Use RCU to guard against racing with free. Arrays are freed via
call_rcu()andkick_cpus_irq_workfn()usesrcu_dereference_bh()with a NULL check.While at it, rename to
scx_kick_pseqsfor brevity and update comments to clarify these arepick_tasksequence numbers.v2: RCU protect
scx_kick_seqsto managekick_cpus_irq_workfn()racing
against disable as per Andrea.
Quoted 8 lines
Quoted 1 line
v3: Fix bugs notcied by Andrea.
This looks good to me. The original patch of course did solve the boot issue. They aren't using scx so there were no other issues seen. I've kicked off a build with this version and will try to get that booted on the large system as well. It should solve that problem just as well as the original one obviously.
Just to follow up: as expected this also boots on the 5220 cpu system.
Cheers,
Phil
Quoted 185 lines
Thanks for the quick turn around.
I'm not going to offer a Tested tag since it was only boot tested (with no scx use) on the large system. And all that really tests is the 5 lines removed from
init_sched_ext_class...Reviewed-by Phil Auld <[email protected]>Cheers,
PhilQuoted 171 lines
Reported-by Phil Auld <[email protected]>Signed-off-by Tejun Heo <[email protected]>Cc Andrea Righi <[email protected]>--- kernel/sched/ext.c | 89 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 10 deletions(-)kernel/sched/ext.c +79
-10@@ -67,8 +67,19 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of pick_task sequence +* numbers. The arrays are allocated with kvzalloc() as size can exceed percpu +* allocator limits on large machines. O(nr_cpu_ids^2) allocation, allocated +* lazily when enabling and freed when disabling to avoid waste when sched_ext +* isn't active. +*/ +struct scx_kick_pseqs { +struct rcu_head rcu; +unsigned long seqs[]; +}; + +static DEFINE_PER_CPU(struct scx_kick_pseqs __rcu *, scx_kick_pseqs);/* * Direct dispatch marker. @@ -3850,6 +3861,27 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_pseqs_rcu (struct rcu_head *rcu) +{ + struct scx_kick_pseqs *pseqs = container_of(rcu, struct scx_kick_pseqs, rcu); + +kvfree (pseqs); +} + +static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *to_free; + + to_free =rcu_replace_pointer (*pseqs,NULL , true); +if (to_free) +call_rcu (&to_free->rcu, free_kick_pseqs_rcu); + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);@@ -3986,6 +4018,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_pseqs ();mutex_unlock (&scx_enable_mutex);@@ -4348,6 +4381,33 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_pseqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *new_pseqs; + +WARN_ON_ONCE (rcu_access_pointer (*pseqs)); + + new_pseqs =kvzalloc_node (struct_size (new_pseqs, seqs, nr_cpu_ids), +GFP_KERNEL ,cpu_to_node (cpu)); +if (!new_pseqs) { +free_kick_pseqs (); +return -ENOMEM ; + } + +rcu_assign_pointer (*pseqs, new_pseqs); + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;@@ -4490,15 +4550,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_pseqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/* @@ -4701,6 +4765,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_pseqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;@@ -5082,10 +5148,18 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +struct scx_kick_pseqs __rcu *pseqs_pcpu =__this_cpu_read (scx_kick_pseqs);bool should_wait = false; +unsigned long *pseqs;s32 cpu; +if (unlikely (!pseqs_pcpu)) { +pr_warn_once ("kick_cpus_irq_workfn() called with NULL scx_kick_pseqs" ); +return ; + } + + pseqs =rcu_dereference_bh (pseqs_pcpu)->seqs; +for_each_cpu (cpu, this_scx->cpus_to_kick) { should_wait |=kick_one_cpu (cpu, this_rq, pseqs);cpumask_clear_cpu (cpu, this_scx->cpus_to_kick);@@ -5208,11 +5282,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);Signature · 1 line
2.51.0
Re: [PATCH v3] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
In reply to Tejun HeoQuoted 18 lines · Tejun Heo
On systems with >4096 CPUs,
scx_kick_cpus_pnt_seqsallocation fails during boot because it exceeds the 32,768 byte percpu allocator limit.Restructure to use
DEFINE_PER_CPU()for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. Move allocation from boot time toscx_enable()and free inscx_disable(), so theO(nr_cpu_ids^2)memory is only consumed whensched_extis active.Use RCU to guard against racing with free. Arrays are freed via
call_rcu()andkick_cpus_irq_workfn()usesrcu_dereference_bh()with a NULL check.While at it, rename to
scx_kick_pseqsfor brevity and update comments to clarify these arepick_tasksequence numbers.v2: RCU protect
scx_kick_seqsto managekick_cpus_irq_workfn()racing
against disable as per Andrea.
Quoted 7 lines
v3: Fix bugs notcied by Andrea.
Reported-by Phil Auld <[email protected]>Signed-off-by Tejun Heo <[email protected]>Cc Andrea Righi <[email protected]>---
Quoted 166 lines
kernel/sched/ext.c | 89 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 10 deletions(-)kernel/sched/ext.c +79
-11@@ -67,8 +67,19 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of pick_task sequence +* numbers. The arrays are allocated with kvzalloc() as size can exceed percpu +* allocator limits on large machines. O(nr_cpu_ids^2) allocation, allocated +* lazily when enabling and freed when disabling to avoid waste when sched_ext +* isn't active. +*/ +struct scx_kick_pseqs { +struct rcu_head rcu; +unsigned long seqs[]; +}; + +static DEFINE_PER_CPU(struct scx_kick_pseqs __rcu *, scx_kick_pseqs);/* * Direct dispatch marker. @@ -3850,6 +3861,27 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_pseqs_rcu (struct rcu_head *rcu) +{ + struct scx_kick_pseqs *pseqs = container_of(rcu, struct scx_kick_pseqs, rcu); + +kvfree (pseqs); +} + +static void free_kick_pseqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *to_free; + + to_free =rcu_replace_pointer (*pseqs,NULL , true); +if (to_free) +call_rcu (&to_free->rcu, free_kick_pseqs_rcu); + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);@@ -3986,6 +4018,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_pseqs ();mutex_unlock (&scx_enable_mutex);@@ -4348,6 +4381,33 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_pseqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +struct scx_kick_pseqs **pseqs =per_cpu_ptr (&scx_kick_pseqs, cpu); +struct scx_kick_pseqs *new_pseqs; + +WARN_ON_ONCE (rcu_access_pointer (*pseqs)); + + new_pseqs =kvzalloc_node (struct_size (new_pseqs, seqs, nr_cpu_ids), +GFP_KERNEL ,cpu_to_node (cpu)); +if (!new_pseqs) { +free_kick_pseqs (); +return -ENOMEM ; + } + +rcu_assign_pointer (*pseqs, new_pseqs); + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;@@ -4490,15 +4550,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_pseqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/* @@ -4701,6 +4765,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_pseqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;@@ -5082,10 +5148,18 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +struct scx_kick_pseqs __rcu *pseqs_pcpu =__this_cpu_read (scx_kick_pseqs);bool should_wait = false; +unsigned long *pseqs;s32 cpu; +if (unlikely (!pseqs_pcpu)) { +pr_warn_once ("kick_cpus_irq_workfn() called with NULL scx_kick_pseqs" ); +return ; + } + + pseqs =rcu_dereference_bh (pseqs_pcpu)->seqs; +for_each_cpu (cpu, this_scx->cpus_to_kick) { should_wait |=kick_one_cpu (cpu, this_rq, pseqs);cpumask_clear_cpu (cpu, this_scx->cpus_to_kick);@@ -5208,11 +5282,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu); --2.51.0
Re: [PATCH v3] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
In reply to Tejun HeoQuoted 2 lines
Tejun Heo (1):
sched_ext: Allocatescx_kick_cpus_pnt_seqslazily usingkvzalloc()
Applied to sched_ext/for-6.18-fixes.
Thanks.
--
tejun
Andrea Righi
reviewerRe: [PATCH v3] sched_ext: Allocate scx_kick_cpus_pnt_seqs lazily using kvzalloc()
Quoted 4 lines · Tejun Heo
Quoted 2 lines
Tejun Heo (1):
sched_ext: Allocatescx_kick_cpus_pnt_seqslazily usingkvzalloc()Applied to sched_ext/for-6.18-fixes.
Meh, too late. I just noticed a bug with this. I'll send a follow-up patch.
-Andrea
Quoted 5 lines
Thanks.
--
tejun
Quoted 4 lines · On Tue, Oct 07, 2025 at 04:37:24PM -1000 Tejun Heo wrote:
Hello,
Can you please see whether the following patch resolves the problem?
Yes, thanks, I'll do that. It may take a little time since they have not given me such a machine. I'll built it and pass it on to the partner to test it. My take a few days to turn around.
Cheers,
Phil
Quoted 159 lines
Thanks.
--
tejun----- 8< -----
From4d7f7d24e90fba47bb08ddbeb8668123b4bbab1bMon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Tue, 7 Oct 2025 16:23:43 -1000
Subject: [PATCH]sched_ext: Allocatescx_kick_cpus_pnt_seqslazily using
kvzalloc()On systems with >4096 CPUs,
scx_kick_cpus_pnt_seqsallocation fails during boot because it exceeds the 32,768 byte percpu allocator limit. The allocation size is sizeof(unsigned long) *nr_cpu_ids, which becomes 33,792 bytes with 4224 CPUs.Restructure
scx_kick_cpus_pnt_seqsto useDEFINE_PER_CPU()for the per-CPU pointers, with each CPU pointing to its own kvzalloc'd array. This avoids percpu allocator size limits. Additionally, move allocation from boot time toscx_enable()and free inscx_disable(), so theO(nr_cpu_ids^2)memory is only consumed whensched_extis active.Reported-by Phil Auld <[email protected]>Signed-off-by Tejun Heo <[email protected]>--- kernel/sched/ext.c | 59 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-)kernel/sched/ext.c +49
-10@@ -67,8 +67,13 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;static struct delayed_work scx_watchdog_work; -/* for %SCX_KICK_WAIT */ -static unsigned long __percpu *scx_kick_cpus_pnt_seqs; +/* +* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of sequence numbers. +* The arrays are allocated with kvzalloc() as size can exceed percpu allocator +* limits on large machines. O(nr_cpu_ids^2) allocation, allocated lazily when +* enabling and freed when disabling to avoid waste when sched_ext isn't active. +*/ +static DEFINE_PER_CPU(unsigned long *, scx_kick_cpus_pnt_seqs);/* * Direct dispatch marker. @@ -3850,6 +3855,16 @@ static const char *scx_exit_reason(enum scx_exit_kind kind) } } +static void free_kick_cpus_pnt_seqs (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) { +kvfree (per_cpu (scx_kick_cpus_pnt_seqs, cpu)); +per_cpu (scx_kick_cpus_pnt_seqs, cpu) =NULL ; + } +} +static void scx_disable_workfn (struct kthread_work *work) { struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);@@ -3986,6 +4001,7 @@ static void scx_disable_workfn(struct kthread_work *work)free_percpu (scx_dsp_ctx); scx_dsp_ctx =NULL ; scx_dsp_max_batch =0 ; +free_kick_cpus_pnt_seqs ();mutex_unlock (&scx_enable_mutex);@@ -4348,6 +4364,28 @@ static void scx_vexit(struct scx_sched *sch,irq_work_queue (&sch->error_irq_work); } +static int alloc_kick_cpus_pnt_seqs (void ) +{ +int cpu; + +/* +* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size +* can exceed percpu allocator limits on large machines. +*/ +for_each_possible_cpu (cpu) { +WARN_ON_ONCE (per_cpu (scx_kick_cpus_pnt_seqs, cpu)); +per_cpu (scx_kick_cpus_pnt_seqs, cpu) = +kvzalloc_node (sizeof (unsigned long ) * nr_cpu_ids, +GFP_KERNEL ,cpu_to_node (cpu)); +if (!per_cpu (scx_kick_cpus_pnt_seqs, cpu)) { +free_kick_cpus_pnt_seqs (); +return -ENOMEM ; + } + } + +return 0 ; +} +static struct scx_sched *scx_alloc_and_add_sched (struct sched_ext_ops *ops) {struct scx_sched *sch;@@ -4490,15 +4528,19 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)mutex_lock (&scx_enable_mutex); + ret =alloc_kick_cpus_pnt_seqs (); +if (ret) + goto err_unlock; +if (scx_enable_state () !=SCX_DISABLED ) { ret = -EBUSY ; - goto err_unlock; + goto err_free_pseqs; } sch =scx_alloc_and_add_sched (ops);if (IS_ERR (sch)) { ret =PTR_ERR (sch); - goto err_unlock; + goto err_free_pseqs; }/* @@ -4701,6 +4743,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)return 0 ; +err_free_pseqs: +free_kick_cpus_pnt_seqs (); err_unlock:mutex_unlock (&scx_enable_mutex);return ret;@@ -5082,7 +5126,7 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) {struct rq *this_rq =this_rq ();struct scx_rq *this_scx = &this_rq->scx; -unsigned long *pseqs =this_cpu_ptr (scx_kick_cpus_pnt_seqs); +unsigned long *pseqs =__this_cpu_read (scx_kick_cpus_pnt_seqs);bool should_wait = false;s32 cpu;@@ -5208,11 +5252,6 @@ void __init init_sched_ext_class(void)scx_idle_init_masks (); - scx_kick_cpus_pnt_seqs = -__alloc_percpu (sizeof (scx_kick_cpus_pnt_seqs[0 ]) * nr_cpu_ids, - __alignof__(scx_kick_cpus_pnt_seqs [0 ])); -BUG_ON (!scx_kick_cpus_pnt_seqs); -for_each_possible_cpu (cpu) {struct rq *rq =cpu_rq (cpu);int n =cpu_to_node (cpu);Signature · 1 line
2.51.0