diff options
author | Tejun Heo <tj@kernel.org> | 2013-04-01 14:23:31 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2013-04-01 14:23:31 -0400 |
commit | 13e2e556013a543eebd238d1c2759195e3c0c9fc (patch) | |
tree | ee1aefceeda27e8ede268ce74bd9faf53efbf95c /kernel/workqueue.c | |
parent | bc0caf099d9df4dd0fad24992b043b40541f4200 (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.c | 54 |
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; |
3307 | fail: | 3307 | fail: |
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 | */ | ||
3324 | static 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 */ |
3340 | static u32 wqattrs_hash(const struct workqueue_attrs *attrs) | 3320 | static 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, | |||
3652 | int apply_workqueue_attrs(struct workqueue_struct *wq, | 3633 | int 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 | |||
3673 | enomem: | ||
3674 | kmem_cache_free(pwq_cache, pwq); | ||
3675 | free_workqueue_attrs(new_attrs); | ||
3676 | return -ENOMEM; | ||
3684 | } | 3677 | } |
3685 | 3678 | ||
3686 | static int alloc_and_link_pwqs(struct workqueue_struct *wq) | 3679 | static 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 | ||