aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/workqueue.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-04-01 14:23:31 -0400
committerTejun Heo <tj@kernel.org>2013-04-01 14:23:31 -0400
commit13e2e556013a543eebd238d1c2759195e3c0c9fc (patch)
treeee1aefceeda27e8ede268ce74bd9faf53efbf95c /kernel/workqueue.c
parentbc0caf099d9df4dd0fad24992b043b40541f4200 (diff)
workqueue: fix unbound workqueue attrs hashing / comparison
29c91e9912b ("workqueue: implement attribute-based unbound worker_pool management") implemented attrs based worker_pool matching. It tried to avoid false negative when comparing cpumasks with custom hash function; unfortunately, the hash and comparison functions fail to ignore CPUs which are not possible. It incorrectly assumed that bitmap_copy() skips leftover bits in the last word of bitmap and cpumask_equal() ignores impossible CPUs. This patch updates attrs->cpumask handling such that impossible CPUs are properly ignored. * Hash and copy functions no longer do anything special. They expect their callers to clear impossible CPUs. * alloc_workqueue_attrs() initializes the cpumask to cpu_possible_mask instead of setting all bits and explicit cpumask_setall() for unbound_std_wq_attrs[] in init_workqueues() is dropped. * apply_workqueue_attrs() is now responsible for ignoring impossible CPUs. It makes a copy of @attrs and clears impossible CPUs before doing anything else. Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'kernel/workqueue.c')
-rw-r--r--kernel/workqueue.c54
1 files changed, 22 insertions, 32 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 4d344326ae97..abe1f0da4600 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3302,7 +3302,7 @@ struct workqueue_attrs *alloc_workqueue_attrs(gfp_t gfp_mask)
3302 if (!alloc_cpumask_var(&attrs->cpumask, gfp_mask)) 3302 if (!alloc_cpumask_var(&attrs->cpumask, gfp_mask))
3303 goto fail; 3303 goto fail;
3304 3304
3305 cpumask_setall(attrs->cpumask); 3305 cpumask_copy(attrs->cpumask, cpu_possible_mask);
3306 return attrs; 3306 return attrs;
3307fail: 3307fail:
3308 free_workqueue_attrs(attrs); 3308 free_workqueue_attrs(attrs);
@@ -3316,33 +3316,14 @@ static void copy_workqueue_attrs(struct workqueue_attrs *to,
3316 cpumask_copy(to->cpumask, from->cpumask); 3316 cpumask_copy(to->cpumask, from->cpumask);
3317} 3317}
3318 3318
3319/*
3320 * Hacky implementation of jhash of bitmaps which only considers the
3321 * specified number of bits. We probably want a proper implementation in
3322 * include/linux/jhash.h.
3323 */
3324static u32 jhash_bitmap(const unsigned long *bitmap, int bits, u32 hash)
3325{
3326 int nr_longs = bits / BITS_PER_LONG;
3327 int nr_leftover = bits % BITS_PER_LONG;
3328 unsigned long leftover = 0;
3329
3330 if (nr_longs)
3331 hash = jhash(bitmap, nr_longs * sizeof(long), hash);
3332 if (nr_leftover) {
3333 bitmap_copy(&leftover, bitmap + nr_longs, nr_leftover);
3334 hash = jhash(&leftover, sizeof(long), hash);
3335 }
3336 return hash;
3337}
3338
3339/* hash value of the content of @attr */ 3319/* hash value of the content of @attr */
3340static u32 wqattrs_hash(const struct workqueue_attrs *attrs) 3320static u32 wqattrs_hash(const struct workqueue_attrs *attrs)
3341{ 3321{
3342 u32 hash = 0; 3322 u32 hash = 0;
3343 3323
3344 hash = jhash_1word(attrs->nice, hash); 3324 hash = jhash_1word(attrs->nice, hash);
3345 hash = jhash_bitmap(cpumask_bits(attrs->cpumask), nr_cpu_ids, hash); 3325 hash = jhash(cpumask_bits(attrs->cpumask),
3326 BITS_TO_LONGS(nr_cpumask_bits) * sizeof(long), hash);
3346 return hash; 3327 return hash;
3347} 3328}
3348 3329
@@ -3652,7 +3633,8 @@ static void init_and_link_pwq(struct pool_workqueue *pwq,
3652int apply_workqueue_attrs(struct workqueue_struct *wq, 3633int apply_workqueue_attrs(struct workqueue_struct *wq,
3653 const struct workqueue_attrs *attrs) 3634 const struct workqueue_attrs *attrs)
3654{ 3635{
3655 struct pool_workqueue *pwq, *last_pwq; 3636 struct workqueue_attrs *new_attrs;
3637 struct pool_workqueue *pwq = NULL, *last_pwq;
3656 struct worker_pool *pool; 3638 struct worker_pool *pool;
3657 3639
3658 /* only unbound workqueues can change attributes */ 3640 /* only unbound workqueues can change attributes */
@@ -3663,15 +3645,21 @@ int apply_workqueue_attrs(struct workqueue_struct *wq,
3663 if (WARN_ON((wq->flags & __WQ_ORDERED) && !list_empty(&wq->pwqs))) 3645 if (WARN_ON((wq->flags & __WQ_ORDERED) && !list_empty(&wq->pwqs)))
3664 return -EINVAL; 3646 return -EINVAL;
3665 3647
3648 /* make a copy of @attrs and sanitize it */
3649 new_attrs = alloc_workqueue_attrs(GFP_KERNEL);
3650 if (!new_attrs)
3651 goto enomem;
3652
3653 copy_workqueue_attrs(new_attrs, attrs);
3654 cpumask_and(new_attrs->cpumask, new_attrs->cpumask, cpu_possible_mask);
3655
3666 pwq = kmem_cache_zalloc(pwq_cache, GFP_KERNEL); 3656 pwq = kmem_cache_zalloc(pwq_cache, GFP_KERNEL);
3667 if (!pwq) 3657 if (!pwq)
3668 return -ENOMEM; 3658 goto enomem;
3669 3659
3670 pool = get_unbound_pool(attrs); 3660 pool = get_unbound_pool(new_attrs);
3671 if (!pool) { 3661 if (!pool)
3672 kmem_cache_free(pwq_cache, pwq); 3662 goto enomem;
3673 return -ENOMEM;
3674 }
3675 3663
3676 init_and_link_pwq(pwq, wq, pool, &last_pwq); 3664 init_and_link_pwq(pwq, wq, pool, &last_pwq);
3677 if (last_pwq) { 3665 if (last_pwq) {
@@ -3681,6 +3669,11 @@ int apply_workqueue_attrs(struct workqueue_struct *wq,
3681 } 3669 }
3682 3670
3683 return 0; 3671 return 0;
3672
3673enomem:
3674 kmem_cache_free(pwq_cache, pwq);
3675 free_workqueue_attrs(new_attrs);
3676 return -ENOMEM;
3684} 3677}
3685 3678
3686static int alloc_and_link_pwqs(struct workqueue_struct *wq) 3679static int alloc_and_link_pwqs(struct workqueue_struct *wq)
@@ -4450,10 +4443,7 @@ static int __init init_workqueues(void)
4450 struct workqueue_attrs *attrs; 4443 struct workqueue_attrs *attrs;
4451 4444
4452 BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL))); 4445 BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL)));
4453
4454 attrs->nice = std_nice[i]; 4446 attrs->nice = std_nice[i];
4455 cpumask_setall(attrs->cpumask);
4456
4457 unbound_std_wq_attrs[i] = attrs; 4447 unbound_std_wq_attrs[i] = attrs;
4458 } 4448 }
4459 4449