aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/workqueue.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-07-22 08:14:25 -0400
committerTejun Heo <tj@kernel.org>2010-07-22 16:39:22 -0400
commite120153ddf8620fd0a194d301e9c5a8b28483bb5 (patch)
tree953ef1a61ca29d0486a6c8c3bb72dd8bbc080419 /kernel/workqueue.c
parentf2e005aaff4878a8ea93d5fb033a21389b72579a (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/workqueue.c')
-rw-r--r--kernel/workqueue.c36
1 files changed, 13 insertions, 23 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index c11edc9c9365..e5cb7faac58e 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
500static void set_work_cpu(struct work_struct *work, unsigned int cpu) 499static 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
510static 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
515static struct cpu_workqueue_struct *get_work_cwq(struct work_struct *work) 509static 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
522static struct global_cwq *get_work_gcwq(struct work_struct *work) 519static 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 */