diff options
author | Tejun Heo <tj@kernel.org> | 2010-07-22 08:14:25 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2010-07-22 16:39:22 -0400 |
commit | e120153ddf8620fd0a194d301e9c5a8b28483bb5 (patch) | |
tree | 953ef1a61ca29d0486a6c8c3bb72dd8bbc080419 /kernel | |
parent | f2e005aaff4878a8ea93d5fb033a21389b72579a (diff) |
workqueue: fix how cpu number is stored in work->data
Once a work starts execution, its data contains the cpu number it was
on instead of pointing to cwq. This is added by commit 7a22ad75
(workqueue: carry cpu number in work data once execution starts) to
reliably determine the work was last on even if the workqueue itself
was destroyed inbetween.
Whether data points to a cwq or contains a cpu number was
distinguished by comparing the value against PAGE_OFFSET. The
assumption was that a cpu number should be below PAGE_OFFSET while a
pointer to cwq should be above it. However, on architectures which
use separate address spaces for user and kernel spaces, this doesn't
hold as PAGE_OFFSET is zero.
Fix it by using an explicit flag, WORK_STRUCT_CWQ, to mark what the
data field contains. If the flag is set, it's pointing to a cwq;
otherwise, it contains a cpu number.
Reported on s390 and microblaze during linux-next testing.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Sachin Sant <sachinp@in.ibm.com>
Reported-by: Michal Simek <michal.simek@petalogix.com>
Reported-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Tested-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Tested-by: Michal Simek <monstr@monstr.eu>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/workqueue.c | 36 |
1 files changed, 13 insertions, 23 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index c11edc9c936..e5cb7faac58 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c | |||
@@ -468,10 +468,9 @@ static int work_next_color(int color) | |||
468 | } | 468 | } |
469 | 469 | ||
470 | /* | 470 | /* |
471 | * Work data points to the cwq while a work is on queue. Once | 471 | * A work's data points to the cwq with WORK_STRUCT_CWQ set while the |
472 | * execution starts, it points to the cpu the work was last on. This | 472 | * work is on queue. Once execution starts, WORK_STRUCT_CWQ is |
473 | * can be distinguished by comparing the data value against | 473 | * cleared and the work data contains the cpu number it was last on. |
474 | * PAGE_OFFSET. | ||
475 | * | 474 | * |
476 | * set_work_{cwq|cpu}() and clear_work_data() can be used to set the | 475 | * set_work_{cwq|cpu}() and clear_work_data() can be used to set the |
477 | * cwq, cpu or clear work->data. These functions should only be | 476 | * cwq, cpu or clear work->data. These functions should only be |
@@ -494,7 +493,7 @@ static void set_work_cwq(struct work_struct *work, | |||
494 | unsigned long extra_flags) | 493 | unsigned long extra_flags) |
495 | { | 494 | { |
496 | set_work_data(work, (unsigned long)cwq, | 495 | set_work_data(work, (unsigned long)cwq, |
497 | WORK_STRUCT_PENDING | extra_flags); | 496 | WORK_STRUCT_PENDING | WORK_STRUCT_CWQ | extra_flags); |
498 | } | 497 | } |
499 | 498 | ||
500 | static void set_work_cpu(struct work_struct *work, unsigned int cpu) | 499 | static void set_work_cpu(struct work_struct *work, unsigned int cpu) |
@@ -507,25 +506,24 @@ static void clear_work_data(struct work_struct *work) | |||
507 | set_work_data(work, WORK_STRUCT_NO_CPU, 0); | 506 | set_work_data(work, WORK_STRUCT_NO_CPU, 0); |
508 | } | 507 | } |
509 | 508 | ||
510 | static inline unsigned long get_work_data(struct work_struct *work) | ||
511 | { | ||
512 | return atomic_long_read(&work->data) & WORK_STRUCT_WQ_DATA_MASK; | ||
513 | } | ||
514 | |||
515 | static struct cpu_workqueue_struct *get_work_cwq(struct work_struct *work) | 509 | static struct cpu_workqueue_struct *get_work_cwq(struct work_struct *work) |
516 | { | 510 | { |
517 | unsigned long data = get_work_data(work); | 511 | unsigned long data = atomic_long_read(&work->data); |
518 | 512 | ||
519 | return data >= PAGE_OFFSET ? (void *)data : NULL; | 513 | if (data & WORK_STRUCT_CWQ) |
514 | return (void *)(data & WORK_STRUCT_WQ_DATA_MASK); | ||
515 | else | ||
516 | return NULL; | ||
520 | } | 517 | } |
521 | 518 | ||
522 | static struct global_cwq *get_work_gcwq(struct work_struct *work) | 519 | static struct global_cwq *get_work_gcwq(struct work_struct *work) |
523 | { | 520 | { |
524 | unsigned long data = get_work_data(work); | 521 | unsigned long data = atomic_long_read(&work->data); |
525 | unsigned int cpu; | 522 | unsigned int cpu; |
526 | 523 | ||
527 | if (data >= PAGE_OFFSET) | 524 | if (data & WORK_STRUCT_CWQ) |
528 | return ((struct cpu_workqueue_struct *)data)->gcwq; | 525 | return ((struct cpu_workqueue_struct *) |
526 | (data & WORK_STRUCT_WQ_DATA_MASK))->gcwq; | ||
529 | 527 | ||
530 | cpu = data >> WORK_STRUCT_FLAG_BITS; | 528 | cpu = data >> WORK_STRUCT_FLAG_BITS; |
531 | if (cpu == WORK_CPU_NONE) | 529 | if (cpu == WORK_CPU_NONE) |
@@ -3501,14 +3499,6 @@ void __init init_workqueues(void) | |||
3501 | unsigned int cpu; | 3499 | unsigned int cpu; |
3502 | int i; | 3500 | int i; |
3503 | 3501 | ||
3504 | /* | ||
3505 | * The pointer part of work->data is either pointing to the | ||
3506 | * cwq or contains the cpu number the work ran last on. Make | ||
3507 | * sure cpu number won't overflow into kernel pointer area so | ||
3508 | * that they can be distinguished. | ||
3509 | */ | ||
3510 | BUILD_BUG_ON(WORK_CPU_LAST << WORK_STRUCT_FLAG_BITS >= PAGE_OFFSET); | ||
3511 | |||
3512 | hotcpu_notifier(workqueue_cpu_callback, CPU_PRI_WORKQUEUE); | 3502 | hotcpu_notifier(workqueue_cpu_callback, CPU_PRI_WORKQUEUE); |
3513 | 3503 | ||
3514 | /* initialize gcwqs */ | 3504 | /* initialize gcwqs */ |