From: Andrew Morton schedule_on_each_cpu() presently does a large kmalloc - 96 kbytes on 1024 CPU 64-bit. Rework it so that we do one 8192-byte allocation and then a pile of tiny ones, which has a much higher chance of success (100% in the current VM). Cc: Christoph Lameter Cc: Andi Kleen Signed-off-by: Andrew Morton --- kernel/workqueue.c | 45 +++++++++++++++++++++++++++++++++---------- 1 files changed, 35 insertions(+), 10 deletions(-) diff -puN kernel/workqueue.c~schedule_on_each_cpu-reduce-kmalloc-size kernel/workqueue.c --- devel/kernel/workqueue.c~schedule_on_each_cpu-reduce-kmalloc-size 2006-05-22 01:24:34.000000000 -0700 +++ devel-akpm/kernel/workqueue.c 2006-05-22 01:49:02.000000000 -0700 @@ -428,23 +428,48 @@ int schedule_delayed_work_on(int cpu, return ret; } -int schedule_on_each_cpu(void (*func) (void *info), void *info) +/** + * schedule_on_each_cpu - call a function on each online CPU from keventd + * @func: the function to call + * @info: a pointer to pass to func() + * + * Returns zero on success. + * Returns -ve errno on failure. + * + * Note that if schedule_on_each_cpu() failed, the callback function may have + * been called on some (but not all) of the presently-online CPUs. + * + * Appears to be racy against CPU hotplug. + * + * schedule_on_each_cpu() is very slow. + */ +int schedule_on_each_cpu(void (*func)(void *info), void *info) { int cpu; - struct work_struct *work; + struct work_struct **works; + int ret = 0; - work = kmalloc(NR_CPUS * sizeof(struct work_struct), GFP_KERNEL); + works = kzalloc(NR_CPUS * sizeof(*works), GFP_KERNEL); + if (!works) { + ret = -ENOMEM; + goto out; + } - if (!work) - return -ENOMEM; for_each_online_cpu(cpu) { - INIT_WORK(work + cpu, func, info); - __queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), - work + cpu); + works[cpu] = kmalloc(sizeof(*works[cpu]), GFP_KERNEL); + if (!works[cpu]) { + ret = -ENOMEM; + break; + } + INIT_WORK(works[cpu], func, info); + __queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), works[cpu]); } flush_workqueue(keventd_wq); - kfree(work); - return 0; + for (cpu = 0; cpu < NR_CPUS; cpu++) + kfree(works[cpu]); + kfree(works); +out: + return ret; } void flush_scheduled_work(void) _