* Move initialization and linking of pool_workqueues into init_and_link_pwq(). * Make the failure path use destroy_workqueue() once pool_workqueue initialization succeeds. These changes are to prepare for dynamic management of pool_workqueues and don't introduce any functional changes. While at it, convert list_del(&wq->list) to list_del_init() as a precaution as scheduled changes will make destruction more complex.
Showing the changes to kernel/workqueue.c (1 of 1 file). Show all
Unified · Split
| @@ -3329,6 +3329,23 @@ fail: · 3328 unmodified lines | ||
| 3329 | 3329 | return NULL; |
| 3330 | 3330 | } |
| 3331 | 3331 | |
| 3332 | +/* initialize @pwq which interfaces with @pool for @wq and link it in */ | |
| 3333 | +static void init_and_link_pwq(struct pool_workqueue *pwq, | |
| 3334 | + struct workqueue_struct *wq, | |
| 3335 | + struct worker_pool *pool) | |
| 3336 | +{ | |
| 3337 | + BUG_ON((unsigned long)pwq & WORK_STRUCT_FLAG_MASK); | |
| 3338 | + | |
| 3339 | + pwq->pool = pool; | |
| 3340 | + pwq->wq = wq; | |
| 3341 | + pwq->flush_color = -1; | |
| 3342 | + pwq->max_active = wq->saved_max_active; | |
| 3343 | + INIT_LIST_HEAD(&pwq->delayed_works); | |
| 3344 | + INIT_LIST_HEAD(&pwq->mayday_node); | |
| 3345 | + | |
| 3346 | + list_add_tail_rcu(&pwq->pwqs_node, &wq->pwqs); | |
| 3347 | +} | |
| 3348 | + | |
| 3332 | 3349 | static int alloc_and_link_pwqs(struct workqueue_struct *wq) |
| 3333 | 3350 | { |
| 3334 | 3351 | bool highpri = wq->flags & WQ_HIGHPRI; |
| @@ -3345,23 +3362,23 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) · 10 unmodified lines | ||
| 3345 | 3362 | struct worker_pool *cpu_pools = |
| 3346 | 3363 | per_cpu(cpu_worker_pools, cpu); |
| 3347 | 3364 | |
| 3348 | − pwq->pool = &cpu_pools[highpri]; | |
| 3349 | − list_add_tail_rcu(&pwq->pwqs_node, &wq->pwqs); | |
| 3365 | + init_and_link_pwq(pwq, wq, &cpu_pools[highpri]); | |
| 3350 | 3366 | } |
| 3351 | 3367 | } else { |
| 3352 | 3368 | struct pool_workqueue *pwq; |
| 3369 | + struct worker_pool *pool; | |
| 3353 | 3370 | |
| 3354 | 3371 | pwq = kmem_cache_zalloc(pwq_cache, GFP_KERNEL); |
| 3355 | 3372 | if (!pwq) |
| 3356 | 3373 | return -ENOMEM; |
| 3357 | 3374 | |
| 3358 | − pwq->pool = get_unbound_pool(unbound_std_wq_attrs[highpri]); | |
| 3359 | − if (!pwq->pool) { | |
| 3375 | + pool = get_unbound_pool(unbound_std_wq_attrs[highpri]); | |
| 3376 | + if (!pool) { | |
| 3360 | 3377 | kmem_cache_free(pwq_cache, pwq); |
| 3361 | 3378 | return -ENOMEM; |
| 3362 | 3379 | } |
| 3363 | 3380 | |
| 3364 | − list_add_tail_rcu(&pwq->pwqs_node, &wq->pwqs); | |
| 3381 | + init_and_link_pwq(pwq, wq, pool); | |
| 3365 | 3382 | } |
| 3366 | 3383 | |
| 3367 | 3384 | return 0; |
| @@ -3406,7 +3423,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, · 38 unmodified lines | ||
| 3406 | 3423 | |
| 3407 | 3424 | wq = kzalloc(sizeof(*wq) + namelen, GFP_KERNEL); |
| 3408 | 3425 | if (!wq) |
| 3409 | − goto err; | |
| 3426 | + return NULL; | |
| 3410 | 3427 | |
| 3411 | 3428 | vsnprintf(wq->name, namelen, fmt, args1); |
| 3412 | 3429 | va_end(args); |
| @@ -3429,18 +3446,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, · 16 unmodified lines | ||
| 3429 | 3446 | INIT_LIST_HEAD(&wq->list); |
| 3430 | 3447 | |
| 3431 | 3448 | if (alloc_and_link_pwqs(wq) < 0) |
| 3432 | − goto err; | |
| 3433 | − | |
| 3434 | − local_irq_disable(); | |
| 3435 | − for_each_pwq(pwq, wq) { | |
| 3436 | − BUG_ON((unsigned long)pwq & WORK_STRUCT_FLAG_MASK); | |
| 3437 | − pwq->wq = wq; | |
| 3438 | − pwq->flush_color = -1; | |
| 3439 | − pwq->max_active = max_active; | |
| 3440 | − INIT_LIST_HEAD(&pwq->delayed_works); | |
| 3441 | − INIT_LIST_HEAD(&pwq->mayday_node); | |
| 3442 | − } | |
| 3443 | − local_irq_enable(); | |
| 3449 | + goto err_free_wq; | |
| 3444 | 3450 | |
| 3445 | 3451 | /* |
| 3446 | 3452 | * Workqueues which may be used during memory reclaim should |
| @@ -3449,16 +3455,19 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, · 2 unmodified lines | ||
| 3449 | 3455 | if (flags & WQ_MEM_RECLAIM) { |
| 3450 | 3456 | struct worker *rescuer; |
| 3451 | 3457 | |
| 3452 | − wq->rescuer = rescuer = alloc_worker(); | |
| 3458 | + rescuer = alloc_worker(); | |
| 3453 | 3459 | if (!rescuer) |
| 3454 | − goto err; | |
| 3460 | + goto err_destroy; | |
| 3455 | 3461 | |
| 3456 | 3462 | rescuer->rescue_wq = wq; |
| 3457 | 3463 | rescuer->task = kthread_create(rescuer_thread, rescuer, "%s", |
| 3458 | 3464 | wq->name); |
| 3459 | − if (IS_ERR(rescuer->task)) | |
| 3460 | − goto err; | |
| 3465 | + if (IS_ERR(rescuer->task)) { | |
| 3466 | + kfree(rescuer); | |
| 3467 | + goto err_destroy; | |
| 3468 | + } | |
| 3461 | 3469 | |
| 3470 | + wq->rescuer = rescuer; | |
| 3462 | 3471 | rescuer->task->flags |= PF_THREAD_BOUND; |
| 3463 | 3472 | wake_up_process(rescuer->task); |
| 3464 | 3473 | } |
| @@ -3479,12 +3488,12 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, · 14 unmodified lines | ||
| 3479 | 3488 | spin_unlock_irq(&workqueue_lock); |
| 3480 | 3489 | |
| 3481 | 3490 | return wq; |
| 3482 | −err: | |
| 3483 | − if (wq) { | |
| 3484 | − free_pwqs(wq); | |
| 3485 | − kfree(wq->rescuer); | |
| 3486 | − kfree(wq); | |
| 3487 | − } | |
| 3491 | + | |
| 3492 | +err_free_wq: | |
| 3493 | + kfree(wq); | |
| 3494 | + return NULL; | |
| 3495 | +err_destroy: | |
| 3496 | + destroy_workqueue(wq); | |
| 3488 | 3497 | return NULL; |
| 3489 | 3498 | } |
| 3490 | 3499 | EXPORT_SYMBOL_GPL(__alloc_workqueue_key); |
| @@ -3526,7 +3535,7 @@ void destroy_workqueue(struct workqueue_struct *wq) · 35 unmodified lines | ||
| 3526 | 3535 | * wq list is used to freeze wq, remove from list after |
| 3527 | 3536 | * flushing is complete in case freeze races us. |
| 3528 | 3537 | */ |
| 3529 | − list_del(&wq->list); | |
| 3538 | + list_del_init(&wq->list); | |
| 3530 | 3539 | |
| 3531 | 3540 | spin_unlock_irq(&workqueue_lock); |
| 3532 | 3541 | |