Message ID | 20230530145335.930262644@redhat.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp2243797vqr; Tue, 30 May 2023 08:00:54 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5A5/5/CN8u0BCNLPrxlBcV2OB2YcUUG90Pjhm7tBJLHKre3OCls6rtxofJykfstWlDj+o/ X-Received: by 2002:a17:902:d34b:b0:1a6:4a64:4d27 with SMTP id l11-20020a170902d34b00b001a64a644d27mr2423535plk.40.1685458853664; Tue, 30 May 2023 08:00:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1685458853; cv=none; d=google.com; s=arc-20160816; b=BwMQddcUFk9eNIoKRsYTU0KnohYNwoPQWChkcrbDdZC48gzLNKlJhMam6vjQc/OBbD ObF2+Z6u4qXiXf56pFjyqp1k65HBCBfp+u31mmjq4d59g9gpBlk3ssjTFSXdCPl1T2uu bJf+6mGJUnu3L7KmLKgd811lt0IDF0DKL+jz728yixI2P1w9YwCu9poXHZGvNviTlrB5 hZ323z9Iu52Y2mrQCswvDxqTwrlN7L/ui4Wns39J5cxLC/PqXkZG8+k/GnKtGHjOUYvR gbswI+wEr81Drn/INhfEtzcHA6a0Q9uJZlgdL3uL98q7EnEM89i+jJr9COMEH6OOHRww QBNg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:references:subject:cc:to:from:date :user-agent:message-id:dkim-signature; bh=zB34+gA9q6N2mJEfFmSt2POPWMX6epdGtnjhsa8vBnc=; b=yXHkebK+5Ghux5xlkNr2Uj23hygTfSRZU9ZaPVV1XS9tsc4zdNCsUf7JBs8sodwjMy bZ/Qw9O+y8QepMDR7fIH3Mgdfbf16dTGMnxfmvy5SHchI03rh9BHhdpOdz16pvumO9C4 0I0w5xSAvmIlggj1+huwn+ew/sEDpb44xONFoO8gVZZ7tjn4qCz07G75ev4J6aRqlL+T cYOGnz4F5g/C9daOhuO/eBrnmnQ3l/Q0MgprXRsf4+LbEMXaEsT5wyBqdqn67ceMcTcp S9OhjeiP9xrqhaBSjhW7ICdTUlrS+JvG3xVbpySE3QuUJxKnPlyCfNLFvnDPIx2gRkgr TPYw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=iBCAgAnm; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id z6-20020a170903018600b001aafc6a76e1si931515plg.69.2023.05.30.08.00.37; Tue, 30 May 2023 08:00:53 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=iBCAgAnm; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232907AbjE3O5u (ORCPT <rfc822;andrewvogler123@gmail.com> + 99 others); Tue, 30 May 2023 10:57:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34710 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232820AbjE3O5n (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Tue, 30 May 2023 10:57:43 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C4DBAB0 for <linux-kernel@vger.kernel.org>; Tue, 30 May 2023 07:56:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1685458617; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: references:references; bh=zB34+gA9q6N2mJEfFmSt2POPWMX6epdGtnjhsa8vBnc=; b=iBCAgAnmzLl7pJbLrXIGp0tLeTly6KSI2RePd+9IiCIYcxesDLCKtzWPOwWleHe7Y+hjRX +uv2ng/UFTsZZHw/OXcaPa7iNRPFDovreXw9GbSHAr7Gxmtb0iWFS7vd7Q2OfiAEnYnr7p 9/TR1DCoYyKW+DhKlnRIDK4YlWCGcpw= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-471-M5R71YDnOC6zHMxN_4ZF3Q-1; Tue, 30 May 2023 10:56:51 -0400 X-MC-Unique: M5R71YDnOC6zHMxN_4ZF3Q-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 25935101AA68; Tue, 30 May 2023 14:56:51 +0000 (UTC) Received: from tpad.localdomain (ovpn-112-2.gru2.redhat.com [10.97.112.2]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E9F62C154D3; Tue, 30 May 2023 14:56:50 +0000 (UTC) Received: by tpad.localdomain (Postfix, from userid 1000) id 57A59401E150C; Tue, 30 May 2023 11:56:33 -0300 (-03) Message-ID: <20230530145335.930262644@redhat.com> User-Agent: quilt/0.67 Date: Tue, 30 May 2023 11:52:37 -0300 From: Marcelo Tosatti <mtosatti@redhat.com> To: Christoph Lameter <cl@linux.com> Cc: Aaron Tomlin <atomlin@atomlin.com>, Frederic Weisbecker <frederic@kernel.org>, Andrew Morton <akpm@linux-foundation.org>, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Vlastimil Babka <vbabka@suse.cz>, Michal Hocko <mhocko@suse.com>, Marcelo Tosatti <mtosatti@redhat.com> Subject: [PATCH 3/4] workqueue: add schedule_on_each_cpumask helper References: <20230530145234.968927611@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Spam-Status: No, score=-2.3 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1767331702793351536?= X-GMAIL-MSGID: =?utf-8?q?1767331702793351536?= |
Series | vmstat bug fixes for nohz_full CPUs | |
Commit Message
Marcelo Tosatti
May 30, 2023, 2:52 p.m. UTC
Add a schedule_on_each_cpumask function, equivalent to
schedule_on_each_cpu but accepting a cpumask to operate.
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
---
Comments
On Tue, 30 May 2023 11:52:37 -0300 Marcelo Tosatti <mtosatti@redhat.com> wrote: > Add a schedule_on_each_cpumask function, equivalent to > schedule_on_each_cpu but accepting a cpumask to operate. > > Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> > > --- > > Index: linux-vmstat-remote/kernel/workqueue.c > =================================================================== > --- linux-vmstat-remote.orig/kernel/workqueue.c > +++ linux-vmstat-remote/kernel/workqueue.c > @@ -3455,6 +3455,56 @@ int schedule_on_each_cpu(work_func_t fun > return 0; > } > > + > +/** > + * schedule_on_each_cpumask - execute a function synchronously on each > + * CPU in "cpumask", for those which are online. > + * > + * @func: the function to call > + * @mask: the CPUs which to call function on > + * > + * schedule_on_each_cpu() executes @func on each specified CPU that is online, > + * using the system workqueue and blocks until all such CPUs have completed. > + * schedule_on_each_cpu() is very slow. > + * > + * Return: > + * 0 on success, -errno on failure. > + */ > +int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask) > +{ > + int cpu; > + struct work_struct __percpu *works; > + cpumask_var_t effmask; > + > + works = alloc_percpu(struct work_struct); > + if (!works) > + return -ENOMEM; > + > + if (!alloc_cpumask_var(&effmask, GFP_KERNEL)) { > + free_percpu(works); > + return -ENOMEM; > + } > + > + cpumask_and(effmask, cpumask, cpu_online_mask); > + > + cpus_read_lock(); > + > + for_each_cpu(cpu, effmask) { Should we check here that the cpu is still online? > + struct work_struct *work = per_cpu_ptr(works, cpu); > + > + INIT_WORK(work, func); > + schedule_work_on(cpu, work); > + } > + > + for_each_cpu(cpu, effmask) > + flush_work(per_cpu_ptr(works, cpu)); > + > + cpus_read_unlock(); > + free_percpu(works); > + free_cpumask_var(effmask); > + return 0; > +} > + > /** > * execute_in_process_context - reliably execute the routine with user context > * @fn: the function to execute > --- linux-vmstat-remote.orig/include/linux/workqueue.h > +++ linux-vmstat-remote/include/linux/workqueue.h > @@ -450,6 +450,7 @@ extern void __flush_workqueue(struct wor > extern void drain_workqueue(struct workqueue_struct *wq); > > extern int schedule_on_each_cpu(work_func_t func); > +extern int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask); May as well make schedule_on_each_cpu() call schedule_on_each_cpumask()? Save a bit of text, and they're hardly performance-critical to that extent.
On Tue, May 30, 2023 at 01:09:47PM -0700, Andrew Morton wrote: > On Tue, 30 May 2023 11:52:37 -0300 Marcelo Tosatti <mtosatti@redhat.com> wrote: > > > Add a schedule_on_each_cpumask function, equivalent to > > schedule_on_each_cpu but accepting a cpumask to operate. > > > > Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> > > > > --- > > > > Index: linux-vmstat-remote/kernel/workqueue.c > > =================================================================== > > --- linux-vmstat-remote.orig/kernel/workqueue.c > > +++ linux-vmstat-remote/kernel/workqueue.c > > @@ -3455,6 +3455,56 @@ int schedule_on_each_cpu(work_func_t fun > > return 0; > > } > > > > + > > +/** > > + * schedule_on_each_cpumask - execute a function synchronously on each > > + * CPU in "cpumask", for those which are online. > > + * > > + * @func: the function to call > > + * @mask: the CPUs which to call function on > > + * > > + * schedule_on_each_cpu() executes @func on each specified CPU that is online, > > + * using the system workqueue and blocks until all such CPUs have completed. > > + * schedule_on_each_cpu() is very slow. > > + * > > + * Return: > > + * 0 on success, -errno on failure. > > + */ > > +int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask) > > +{ > > + int cpu; > > + struct work_struct __percpu *works; > > + cpumask_var_t effmask; > > + > > + works = alloc_percpu(struct work_struct); > > + if (!works) > > + return -ENOMEM; > > + > > + if (!alloc_cpumask_var(&effmask, GFP_KERNEL)) { > > + free_percpu(works); > > + return -ENOMEM; > > + } > > + > > + cpumask_and(effmask, cpumask, cpu_online_mask); > > + > > + cpus_read_lock(); > > + > > + for_each_cpu(cpu, effmask) { > > Should we check here that the cpu is still online? > > > + struct work_struct *work = per_cpu_ptr(works, cpu); > > + > > + INIT_WORK(work, func); > > + schedule_work_on(cpu, work); > > + } > > + > > + for_each_cpu(cpu, effmask) > > + flush_work(per_cpu_ptr(works, cpu)); > > + > > + cpus_read_unlock(); > > + free_percpu(works); > > + free_cpumask_var(effmask); > > + return 0; > > +} > > + > > /** > > * execute_in_process_context - reliably execute the routine with user context > > * @fn: the function to execute > > --- linux-vmstat-remote.orig/include/linux/workqueue.h > > +++ linux-vmstat-remote/include/linux/workqueue.h > > @@ -450,6 +450,7 @@ extern void __flush_workqueue(struct wor > > extern void drain_workqueue(struct workqueue_struct *wq); > > > > extern int schedule_on_each_cpu(work_func_t func); > > +extern int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask); > > May as well make schedule_on_each_cpu() call > schedule_on_each_cpumask()? Save a bit of text, and they're hardly > performance-critical to that extent. Agree, will wait for Michal's review before resending -v2. workqueue: add schedule_on_each_cpumask helper Add a schedule_on_each_cpumask function, equivalent to schedule_on_each_cpu but accepting a cpumask to operate. Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> --- v2: - cpu_online_mask reference should happen with cpus_read_lock held (Andrew Morton) - have schedule_on_each_cpu call _cpumask variant (Andrew Morton) Index: linux-vmstat-remote/kernel/workqueue.c =================================================================== --- linux-vmstat-remote.orig/kernel/workqueue.c +++ linux-vmstat-remote/kernel/workqueue.c @@ -3431,27 +3431,56 @@ EXPORT_SYMBOL(cancel_delayed_work_sync); */ int schedule_on_each_cpu(work_func_t func) { + return schedule_on_each_cpumask(func, cpu_possible_mask); +} + + +/** + * schedule_on_each_cpumask - execute a function synchronously on each + * CPU in "cpumask", for those which are online. + * + * @func: the function to call + * @mask: the CPUs which to call function on + * + * schedule_on_each_cpu() executes @func on each specified CPU that is online, + * using the system workqueue and blocks until all such CPUs have completed. + * schedule_on_each_cpu() is very slow. + * + * Return: + * 0 on success, -errno on failure. + */ +int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask) +{ int cpu; struct work_struct __percpu *works; + cpumask_var_t effmask; works = alloc_percpu(struct work_struct); if (!works) return -ENOMEM; + if (!alloc_cpumask_var(&effmask, GFP_KERNEL)) { + free_percpu(works); + return -ENOMEM; + } + cpus_read_lock(); - for_each_online_cpu(cpu) { + cpumask_and(effmask, cpumask, cpu_online_mask); + + for_each_cpu(cpu, effmask) { struct work_struct *work = per_cpu_ptr(works, cpu); INIT_WORK(work, func); schedule_work_on(cpu, work); } - for_each_online_cpu(cpu) + for_each_cpu(cpu, effmask) flush_work(per_cpu_ptr(works, cpu)); cpus_read_unlock(); free_percpu(works); + free_cpumask_var(effmask); return 0; } Index: linux-vmstat-remote/include/linux/workqueue.h =================================================================== --- linux-vmstat-remote.orig/include/linux/workqueue.h +++ linux-vmstat-remote/include/linux/workqueue.h @@ -450,6 +450,7 @@ extern void __flush_workqueue(struct wor extern void drain_workqueue(struct workqueue_struct *wq); extern int schedule_on_each_cpu(work_func_t func); +extern int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask); int execute_in_process_context(work_func_t fn, struct execute_work *);
You should be CCing WQ maintainers on changes like this one (now added). On Tue 30-05-23 11:52:37, Marcelo Tosatti wrote: > Add a schedule_on_each_cpumask function, equivalent to > schedule_on_each_cpu but accepting a cpumask to operate. IMHO it is preferable to add a new function along with its user so that the usecase is more clear. > Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> > > --- > > Index: linux-vmstat-remote/kernel/workqueue.c > =================================================================== > --- linux-vmstat-remote.orig/kernel/workqueue.c > +++ linux-vmstat-remote/kernel/workqueue.c > @@ -3455,6 +3455,56 @@ int schedule_on_each_cpu(work_func_t fun > return 0; > } > > + > +/** > + * schedule_on_each_cpumask - execute a function synchronously on each > + * CPU in "cpumask", for those which are online. > + * > + * @func: the function to call > + * @mask: the CPUs which to call function on > + * > + * schedule_on_each_cpu() executes @func on each specified CPU that is online, > + * using the system workqueue and blocks until all such CPUs have completed. > + * schedule_on_each_cpu() is very slow. > + * > + * Return: > + * 0 on success, -errno on failure. > + */ > +int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask) > +{ > + int cpu; > + struct work_struct __percpu *works; > + cpumask_var_t effmask; > + > + works = alloc_percpu(struct work_struct); > + if (!works) > + return -ENOMEM; > + > + if (!alloc_cpumask_var(&effmask, GFP_KERNEL)) { > + free_percpu(works); > + return -ENOMEM; > + } > + > + cpumask_and(effmask, cpumask, cpu_online_mask); > + > + cpus_read_lock(); > + > + for_each_cpu(cpu, effmask) { Is the cpu_online_mask dance really necessary? Why cannot you simply do for_each_online_cpu here? flush_work on unqueued work item should just return, no? Also there is no synchronization with the cpu hotplug so cpu_online_mask can change under your feet so this construct seem unsafe to me. > + struct work_struct *work = per_cpu_ptr(works, cpu); > + > + INIT_WORK(work, func); > + schedule_work_on(cpu, work); > + } > + > + for_each_cpu(cpu, effmask) > + flush_work(per_cpu_ptr(works, cpu)); > + > + cpus_read_unlock(); > + free_percpu(works); > + free_cpumask_var(effmask); > + return 0; > +} > + > /** > * execute_in_process_context - reliably execute the routine with user context > * @fn: the function to execute > Index: linux-vmstat-remote/include/linux/workqueue.h > =================================================================== > --- linux-vmstat-remote.orig/include/linux/workqueue.h > +++ linux-vmstat-remote/include/linux/workqueue.h > @@ -450,6 +450,7 @@ extern void __flush_workqueue(struct wor > extern void drain_workqueue(struct workqueue_struct *wq); > > extern int schedule_on_each_cpu(work_func_t func); > +extern int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask); > > int execute_in_process_context(work_func_t fn, struct execute_work *); > >
On Fri, Jun 02, 2023 at 12:48:23PM +0200, Michal Hocko wrote: > You should be CCing WQ maintainers on changes like this one (now added). > > On Tue 30-05-23 11:52:37, Marcelo Tosatti wrote: > > Add a schedule_on_each_cpumask function, equivalent to > > schedule_on_each_cpu but accepting a cpumask to operate. > > IMHO it is preferable to add a new function along with its user so that > the usecase is more clear. > > > Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> > > > > --- > > > > Index: linux-vmstat-remote/kernel/workqueue.c > > =================================================================== > > --- linux-vmstat-remote.orig/kernel/workqueue.c > > +++ linux-vmstat-remote/kernel/workqueue.c > > @@ -3455,6 +3455,56 @@ int schedule_on_each_cpu(work_func_t fun > > return 0; > > } > > > > + > > +/** > > + * schedule_on_each_cpumask - execute a function synchronously on each > > + * CPU in "cpumask", for those which are online. > > + * > > + * @func: the function to call > > + * @mask: the CPUs which to call function on > > + * > > + * schedule_on_each_cpu() executes @func on each specified CPU that is online, > > + * using the system workqueue and blocks until all such CPUs have completed. > > + * schedule_on_each_cpu() is very slow. > > + * > > + * Return: > > + * 0 on success, -errno on failure. > > + */ > > +int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask) > > +{ > > + int cpu; > > + struct work_struct __percpu *works; > > + cpumask_var_t effmask; > > + > > + works = alloc_percpu(struct work_struct); > > + if (!works) > > + return -ENOMEM; > > + > > + if (!alloc_cpumask_var(&effmask, GFP_KERNEL)) { > > + free_percpu(works); > > + return -ENOMEM; > > + } > > + > > + cpumask_and(effmask, cpumask, cpu_online_mask); > > + > > + cpus_read_lock(); > > + > > + for_each_cpu(cpu, effmask) { > > Is the cpu_online_mask dance really necessary? > Why cannot you simply do for_each_online_cpu here? Are you suggesting to do: for_each_online_cpu(cpu) { if cpu is not in cpumask continue; ... } This does not seem efficient. > flush_work on unqueued work item should just > return, no? Apparently not: commit 0e8d6a9336b487a1dd6f1991ff376e669d4c87c6 Author: Thomas Gleixner <tglx@linutronix.de> Date: Wed Apr 12 22:07:28 2017 +0200 workqueue: Provide work_on_cpu_safe() work_on_cpu() is not protected against CPU hotplug. For code which requires to be either executed on an online CPU or to fail if the CPU is not available the callsite would have to protect against CPU hotplug. Provide a function which does get/put_online_cpus() around the call to work_on_cpu() and fails the call with -ENODEV if the target CPU is not online. > Also there is no synchronization with the cpu hotplug so cpu_online_mask > can change under your feet so this construct seem unsafe to me. Yes, fixed by patch in response to Andrew's comment.
On Fri 02-06-23 14:04:28, Marcelo Tosatti wrote: > On Fri, Jun 02, 2023 at 12:48:23PM +0200, Michal Hocko wrote: [...] > > > + if (!alloc_cpumask_var(&effmask, GFP_KERNEL)) { > > > + free_percpu(works); > > > + return -ENOMEM; > > > + } > > > + > > > + cpumask_and(effmask, cpumask, cpu_online_mask); > > > + > > > + cpus_read_lock(); > > > + > > > + for_each_cpu(cpu, effmask) { > > > > Is the cpu_online_mask dance really necessary? > > > Why cannot you simply do for_each_online_cpu here? > > Are you suggesting to do: > > for_each_online_cpu(cpu) { > if cpu is not in cpumask > continue; > ... > } > > This does not seem efficient. Are you sure this is less sufficient than a memory allocation?
Index: linux-vmstat-remote/kernel/workqueue.c =================================================================== --- linux-vmstat-remote.orig/kernel/workqueue.c +++ linux-vmstat-remote/kernel/workqueue.c @@ -3455,6 +3455,56 @@ int schedule_on_each_cpu(work_func_t fun return 0; } + +/** + * schedule_on_each_cpumask - execute a function synchronously on each + * CPU in "cpumask", for those which are online. + * + * @func: the function to call + * @mask: the CPUs which to call function on + * + * schedule_on_each_cpu() executes @func on each specified CPU that is online, + * using the system workqueue and blocks until all such CPUs have completed. + * schedule_on_each_cpu() is very slow. + * + * Return: + * 0 on success, -errno on failure. + */ +int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask) +{ + int cpu; + struct work_struct __percpu *works; + cpumask_var_t effmask; + + works = alloc_percpu(struct work_struct); + if (!works) + return -ENOMEM; + + if (!alloc_cpumask_var(&effmask, GFP_KERNEL)) { + free_percpu(works); + return -ENOMEM; + } + + cpumask_and(effmask, cpumask, cpu_online_mask); + + cpus_read_lock(); + + for_each_cpu(cpu, effmask) { + struct work_struct *work = per_cpu_ptr(works, cpu); + + INIT_WORK(work, func); + schedule_work_on(cpu, work); + } + + for_each_cpu(cpu, effmask) + flush_work(per_cpu_ptr(works, cpu)); + + cpus_read_unlock(); + free_percpu(works); + free_cpumask_var(effmask); + return 0; +} + /** * execute_in_process_context - reliably execute the routine with user context * @fn: the function to execute Index: linux-vmstat-remote/include/linux/workqueue.h =================================================================== --- linux-vmstat-remote.orig/include/linux/workqueue.h +++ linux-vmstat-remote/include/linux/workqueue.h @@ -450,6 +450,7 @@ extern void __flush_workqueue(struct wor extern void drain_workqueue(struct workqueue_struct *wq); extern int schedule_on_each_cpu(work_func_t func); +extern int schedule_on_each_cpumask(work_func_t func, cpumask_t *cpumask); int execute_in_process_context(work_func_t fn, struct execute_work *);