Skip to content

Commit 1211f3b

Browse files
committed
workqueue: Preserve OFFQ bits in cancel[_sync] paths
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 <[email protected]> Reviewed-by: Lai Jiangshan <[email protected]>
1 parent 4cece76 commit 1211f3b

File tree

2 files changed

+32
-20
lines changed

2 files changed

+32
-20
lines changed

include/linux/workqueue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ enum wq_misc_consts {
9797

9898
/* Convenience constants - of type 'unsigned long', not 'enum'! */
9999
#define WORK_OFFQ_CANCELING (1ul << WORK_OFFQ_CANCELING_BIT)
100+
#define WORK_OFFQ_FLAG_MASK (((1ul << WORK_OFFQ_FLAG_BITS) - 1) << WORK_OFFQ_FLAG_SHIFT)
100101
#define WORK_OFFQ_POOL_NONE ((1ul << WORK_OFFQ_POOL_BITS) - 1)
101102
#define WORK_STRUCT_NO_POOL (WORK_OFFQ_POOL_NONE << WORK_OFFQ_POOL_SHIFT)
102103
#define WORK_STRUCT_PWQ_MASK (~((1ul << WORK_STRUCT_PWQ_SHIFT) - 1))

kernel/workqueue.c

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,11 @@ struct wq_pod_type {
392392
int *cpu_pod; /* cpu -> pod */
393393
};
394394

395+
struct work_offq_data {
396+
u32 pool_id;
397+
u32 flags;
398+
};
399+
395400
static const char *wq_affn_names[WQ_AFFN_NR_TYPES] = {
396401
[WQ_AFFN_DFL] = "default",
397402
[WQ_AFFN_CPU] = "cpu",
@@ -892,29 +897,23 @@ static struct worker_pool *get_work_pool(struct work_struct *work)
892897
return idr_find(&worker_pool_idr, pool_id);
893898
}
894899

895-
/**
896-
* get_work_pool_id - return the worker pool ID a given work is associated with
897-
* @work: the work item of interest
898-
*
899-
* Return: The worker_pool ID @work was last associated with.
900-
* %WORK_OFFQ_POOL_NONE if none.
901-
*/
902-
static int get_work_pool_id(struct work_struct *work)
900+
static unsigned long shift_and_mask(unsigned long v, u32 shift, u32 bits)
903901
{
904-
unsigned long data = atomic_long_read(&work->data);
902+
return (v >> shift) & ((1 << bits) - 1);
903+
}
905904

906-
if (data & WORK_STRUCT_PWQ)
907-
return work_struct_pwq(data)->pool->id;
905+
static void work_offqd_unpack(struct work_offq_data *offqd, unsigned long data)
906+
{
907+
WARN_ON_ONCE(data & WORK_STRUCT_PWQ);
908908

909-
return data >> WORK_OFFQ_POOL_SHIFT;
909+
offqd->pool_id = shift_and_mask(data, WORK_OFFQ_POOL_SHIFT,
910+
WORK_OFFQ_POOL_BITS);
911+
offqd->flags = data & WORK_OFFQ_FLAG_MASK;
910912
}
911913

912-
static void mark_work_canceling(struct work_struct *work)
914+
static unsigned long work_offqd_pack_flags(struct work_offq_data *offqd)
913915
{
914-
unsigned long pool_id = get_work_pool_id(work);
915-
916-
pool_id <<= WORK_OFFQ_POOL_SHIFT;
917-
set_work_data(work, pool_id | WORK_STRUCT_PENDING | WORK_OFFQ_CANCELING);
916+
return (unsigned long)offqd->flags;
918917
}
919918

920919
static bool work_is_canceling(struct work_struct *work)
@@ -4271,6 +4270,7 @@ EXPORT_SYMBOL(flush_rcu_work);
42714270

42724271
static bool __cancel_work(struct work_struct *work, u32 cflags)
42734272
{
4273+
struct work_offq_data offqd;
42744274
unsigned long irq_flags;
42754275
int ret;
42764276

@@ -4281,19 +4281,26 @@ static bool __cancel_work(struct work_struct *work, u32 cflags)
42814281
if (unlikely(ret < 0))
42824282
return false;
42834283

4284-
set_work_pool_and_clear_pending(work, get_work_pool_id(work), 0);
4284+
work_offqd_unpack(&offqd, *work_data_bits(work));
4285+
set_work_pool_and_clear_pending(work, offqd.pool_id,
4286+
work_offqd_pack_flags(&offqd));
42854287
local_irq_restore(irq_flags);
42864288
return ret;
42874289
}
42884290

42894291
static bool __cancel_work_sync(struct work_struct *work, u32 cflags)
42904292
{
4293+
struct work_offq_data offqd;
42914294
unsigned long irq_flags;
42924295
bool ret;
42934296

42944297
/* claim @work and tell other tasks trying to grab @work to back off */
42954298
ret = work_grab_pending(work, cflags, &irq_flags);
4296-
mark_work_canceling(work);
4299+
4300+
work_offqd_unpack(&offqd, *work_data_bits(work));
4301+
offqd.flags |= WORK_OFFQ_CANCELING;
4302+
set_work_pool_and_keep_pending(work, offqd.pool_id,
4303+
work_offqd_pack_flags(&offqd));
42974304
local_irq_restore(irq_flags);
42984305

42994306
/*
@@ -4303,12 +4310,16 @@ static bool __cancel_work_sync(struct work_struct *work, u32 cflags)
43034310
if (wq_online)
43044311
__flush_work(work, true);
43054312

4313+
work_offqd_unpack(&offqd, *work_data_bits(work));
4314+
43064315
/*
43074316
* smp_mb() at the end of set_work_pool_and_clear_pending() is paired
43084317
* with prepare_to_wait() above so that either waitqueue_active() is
43094318
* visible here or !work_is_canceling() is visible there.
43104319
*/
4311-
set_work_pool_and_clear_pending(work, WORK_OFFQ_POOL_NONE, 0);
4320+
offqd.flags &= ~WORK_OFFQ_CANCELING;
4321+
set_work_pool_and_clear_pending(work, WORK_OFFQ_POOL_NONE,
4322+
work_offqd_pack_flags(&offqd));
43124323

43134324
if (waitqueue_active(&wq_cancel_waitq))
43144325
__wake_up(&wq_cancel_waitq, TASK_NORMAL, 1, work);

0 commit comments

Comments
 (0)