public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Tejun Heo <tj@kernel.org>
To: jiangshanlai@gmail.com
Cc: torvalds@linux-foundation.org, linux-kernel@vger.kernel.org,
	allen.lkml@gmail.com, kernel-team@meta.com,
	Tejun Heo <tj@kernel.org>
Subject: [PATCH 11/17] workqueue: Preserve OFFQ bits in cancel[_sync] paths
Date: Fri, 16 Feb 2024 08:04:52 -1000	[thread overview]
Message-ID: <20240216180559.208276-12-tj@kernel.org> (raw)
In-Reply-To: <20240216180559.208276-1-tj@kernel.org>

The cancel[_sync] paths acquire and release WORK_STRUCT_PENDING, and
manipulate WORK_OFFQ_CANCELING. However, they assume that all the OFFQ bit
values except for the pool ID are statically known and don't preserve them,
which is not wrong in the current code as the pool ID and CANCELING are the
only information carried. However, the planned disable/enable support will
add more fields and need them to be preserved.

This patch updates work data handling so that only the bits which need
updating are updated.

- struct work_offq_data is added along with work_offqd_unpack() and
  work_offqd_pack_flags() to help manipulating multiple fields contained in
  work->data. Note that the helpers look a bit silly right now as there
  isn't that much to pack. The next patch will add more.

- mark_work_canceling() which is used only by __cancel_work_sync() is
  replaced by open-coded usage of work_offq_data and
  set_work_pool_and_keep_pending() in __cancel_work_sync().

- __cancel_work[_sync]() uses offq_data helpers to preserve other OFFQ bits
  when clearing WORK_STRUCT_PENDING and WORK_OFFQ_CANCELING at the end.

- This removes all users of get_work_pool_id() which is dropped. Note that
  get_work_pool_id() could handle both WORK_STRUCT_PWQ and !WORK_STRUCT_PWQ
  cases; however, it was only being called after try_to_grab_pending()
  succeeded, in which case WORK_STRUCT_PWQ is never set and thus it's safe
  to use work_offqd_unpack() instead.

No behavior changes intended.

Signed-off-by: Tejun Heo <tj@kernel.org>
---
 include/linux/workqueue.h |  1 +
 kernel/workqueue.c        | 51 ++++++++++++++++++++++++---------------
 2 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 0ad534fe6673..e15fc77bf2e2 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -97,6 +97,7 @@ enum wq_misc_consts {
 
 /* Convenience constants - of type 'unsigned long', not 'enum'! */
 #define WORK_OFFQ_CANCELING	(1ul << WORK_OFFQ_CANCELING_BIT)
+#define WORK_OFFQ_FLAG_MASK	(((1ul << WORK_OFFQ_FLAG_BITS) - 1) << WORK_OFFQ_FLAG_SHIFT)
 #define WORK_OFFQ_POOL_NONE	((1ul << WORK_OFFQ_POOL_BITS) - 1)
 #define WORK_STRUCT_NO_POOL	(WORK_OFFQ_POOL_NONE << WORK_OFFQ_POOL_SHIFT)
 #define WORK_STRUCT_PWQ_MASK	(~((1ul << WORK_STRUCT_PWQ_SHIFT) - 1))
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 7aa53a2e41af..e8f310aa16d6 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -391,6 +391,11 @@ struct wq_pod_type {
 	int			*cpu_pod;	/* cpu -> pod */
 };
 
+struct work_offq_data {
+	u32			pool_id;
+	u32			flags;
+};
+
 static const char *wq_affn_names[WQ_AFFN_NR_TYPES] = {
 	[WQ_AFFN_DFL]		= "default",
 	[WQ_AFFN_CPU]		= "cpu",
@@ -887,29 +892,23 @@ static struct worker_pool *get_work_pool(struct work_struct *work)
 	return idr_find(&worker_pool_idr, pool_id);
 }
 
-/**
- * get_work_pool_id - return the worker pool ID a given work is associated with
- * @work: the work item of interest
- *
- * Return: The worker_pool ID @work was last associated with.
- * %WORK_OFFQ_POOL_NONE if none.
- */
-static int get_work_pool_id(struct work_struct *work)
+static unsigned long shift_and_mask(unsigned long v, u32 shift, u32 bits)
 {
-	unsigned long data = atomic_long_read(&work->data);
+	return (v >> shift) & ((1 << bits) - 1);
+}
 
-	if (data & WORK_STRUCT_PWQ)
-		return work_struct_pwq(data)->pool->id;
+static void work_offqd_unpack(struct work_offq_data *offqd, unsigned long data)
+{
+	WARN_ON_ONCE(data & WORK_STRUCT_PWQ);
 
-	return data >> WORK_OFFQ_POOL_SHIFT;
+	offqd->pool_id = shift_and_mask(data, WORK_OFFQ_POOL_SHIFT,
+					WORK_OFFQ_POOL_BITS);
+	offqd->flags = data & WORK_OFFQ_FLAG_MASK;
 }
 
-static void mark_work_canceling(struct work_struct *work)
+static unsigned long work_offqd_pack_flags(struct work_offq_data *offqd)
 {
-	unsigned long pool_id = get_work_pool_id(work);
-
-	pool_id <<= WORK_OFFQ_POOL_SHIFT;
-	set_work_data(work, pool_id | WORK_STRUCT_PENDING | WORK_OFFQ_CANCELING);
+	return (unsigned long)offqd->flags;
 }
 
 static bool work_is_canceling(struct work_struct *work)
@@ -4176,6 +4175,7 @@ EXPORT_SYMBOL(flush_rcu_work);
 
 static bool __cancel_work(struct work_struct *work, u32 cflags)
 {
+	struct work_offq_data offqd;
 	unsigned long irq_flags;
 	int ret;
 
@@ -4186,19 +4186,26 @@ static bool __cancel_work(struct work_struct *work, u32 cflags)
 	if (unlikely(ret < 0))
 		return false;
 
-	set_work_pool_and_clear_pending(work, get_work_pool_id(work), 0);
+	work_offqd_unpack(&offqd, *work_data_bits(work));
+	set_work_pool_and_clear_pending(work, offqd.pool_id,
+					work_offqd_pack_flags(&offqd));
 	local_irq_restore(irq_flags);
 	return ret;
 }
 
 static bool __cancel_work_sync(struct work_struct *work, u32 cflags)
 {
+	struct work_offq_data offqd;
 	unsigned long irq_flags;
 	bool ret;
 
 	/* claim @work and tell other tasks trying to grab @work to back off */
 	ret = work_grab_pending(work, cflags, &irq_flags);
-	mark_work_canceling(work);
+
+	work_offqd_unpack(&offqd, *work_data_bits(work));
+	offqd.flags |= WORK_OFFQ_CANCELING;
+	set_work_pool_and_keep_pending(work, offqd.pool_id,
+				       work_offqd_pack_flags(&offqd));
 	local_irq_restore(irq_flags);
 
 	/*
@@ -4208,12 +4215,16 @@ static bool __cancel_work_sync(struct work_struct *work, u32 cflags)
 	if (wq_online)
 		__flush_work(work, true);
 
+	work_offqd_unpack(&offqd, *work_data_bits(work));
+
 	/*
 	 * smp_mb() at the end of set_work_pool_and_clear_pending() is paired
 	 * with prepare_to_wait() above so that either waitqueue_active() is
 	 * visible here or !work_is_canceling() is visible there.
 	 */
-	set_work_pool_and_clear_pending(work, WORK_OFFQ_POOL_NONE, 0);
+	offqd.flags &= ~WORK_OFFQ_CANCELING;
+	set_work_pool_and_clear_pending(work, WORK_OFFQ_POOL_NONE,
+					work_offqd_pack_flags(&offqd));
 
 	if (waitqueue_active(&wq_cancel_waitq))
 		__wake_up(&wq_cancel_waitq, TASK_NORMAL, 1, work);
-- 
2.43.2


  parent reply	other threads:[~2024-02-16 18:06 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-16 18:04 [PATCHSET wq/for-6.9,6.10] workqueue: Implement disable/enable_work() Tejun Heo
2024-02-16 18:04 ` [PATCH 01/17] workqueue: Cosmetic changes Tejun Heo
2024-02-16 18:04 ` [PATCH 02/17] workqueue: Use rcu_read_lock_any_held() instead of rcu_read_lock_held() Tejun Heo
2024-02-16 18:04 ` [PATCH 03/17] workqueue: Rename __cancel_work_timer() to __cancel_timer_sync() Tejun Heo
2024-02-16 18:04 ` [PATCH 04/17] workqueue: Reorganize flush and cancel[_sync] functions Tejun Heo
2024-02-16 18:04 ` [PATCH 05/17] workqueue: Use variable name irq_flags for saving local irq flags Tejun Heo
2024-02-16 18:04 ` [PATCH 06/17] workqueue: Introduce work_cancel_flags Tejun Heo
2024-02-16 18:04 ` [PATCH 07/17] workqueue: Clean up enum work_bits and related constants Tejun Heo
2024-02-16 18:04 ` [PATCH 08/17] workqueue: Factor out work_grab_pending() from __cancel_work_sync() Tejun Heo
2024-02-16 18:04 ` [PATCH 09/17] workqueue: Remove clear_work_data() Tejun Heo
2024-02-16 18:04 ` [PATCH 10/17] workqueue: Make @flags handling consistent across set_work_data() and friends Tejun Heo
2024-02-16 18:04 ` Tejun Heo [this message]
2024-02-16 18:04 ` [PATCH 12/17] workqueue: Implement disable/enable for (delayed) work items Tejun Heo
2024-02-20  7:22   ` Lai Jiangshan
2024-02-20 18:38     ` Tejun Heo
2024-02-21  2:54       ` Lai Jiangshan
2024-02-21  5:47         ` Tejun Heo
2024-02-26  3:42   ` Boqun Feng
2024-02-26 18:30     ` Tejun Heo
2024-02-16 18:04 ` [PATCH 13/17] workqueue: Remove WORK_OFFQ_CANCELING Tejun Heo
2024-02-20  7:23   ` Lai Jiangshan
2024-02-20 18:53     ` Tejun Heo
2024-02-16 18:04 ` [PATCH 14/17] workqueue: Remember whether a work item was on a BH workqueue Tejun Heo
2024-02-16 18:04 ` [PATCH 15/17] workqueue: Update how start_flush_work() is called Tejun Heo
2024-02-16 18:04 ` [PATCH 16/17] workqueue: Allow cancel_work_sync() and disable_work() from atomic contexts on BH work items Tejun Heo
2024-02-20  7:33   ` Lai Jiangshan
2024-02-20 20:01     ` Tejun Heo
2024-02-16 18:04 ` [PATCH 17/17] r8152: Convert from tasklet to BH workqueue Tejun Heo
2024-02-20 17:33 ` [PATCHSET wq/for-6.9,6.10] workqueue: Implement disable/enable_work() Allen
2024-02-20 20:25 ` Tejun Heo
2024-02-20 21:38   ` Allen
2024-02-22 20:26     ` Allen
2024-02-26 18:38       ` Tejun Heo
2024-02-27 17:56         ` Allen
2024-02-21  2:39   ` Lai Jiangshan
2024-02-21  5:39     ` Tejun Heo
2024-02-21 17:03       ` Allen
2024-02-25 17:40         ` Kees Cook
2024-02-26 17:30           ` Allen
2024-02-26 18:30             ` Tejun Heo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240216180559.208276-12-tj@kernel.org \
    --to=tj@kernel.org \
    --cc=allen.lkml@gmail.com \
    --cc=jiangshanlai@gmail.com \
    --cc=kernel-team@meta.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox