Currently show_workqueue_state shows the state of all workqueues and of all worker pools. In certain cases we may need to dump state of only a specific workqueue or worker pool. For example in destroy_workqueue we only need to show state of the workqueue which is getting destroyed. So rename show_workqueue_state to show_all_workqueues(to signify it dumps state of all busy workqueues) and divide it into more granular functions (show_one_workqueue and show_one_worker_pool), that would show states of individual workqueues and worker pools and can be used in cases such as the one mentioned above. Also, as mentioned earlier, make destroy_workqueue dump data pertaining to only the workqueue that is being destroyed and make user(s) of earlier interface(show_workqueue_state), use new interface (show_all_workqueues).
Showing the changes to kernel/workqueue.c (1 of 4 files). Show all
Unified · Split
| @@ -375,6 +375,7 @@ EXPORT_SYMBOL_GPL(system_freezable_power_efficient_wq); · 374 unmodified lines | ||
| 375 | 375 | static int worker_thread(void *__worker); |
| 376 | 376 | static void workqueue_sysfs_unregister(struct workqueue_struct *wq); |
| 377 | 377 | static void show_pwq(struct pool_workqueue *pwq); |
| 378 | +static void show_one_worker_pool(struct worker_pool *pool); | |
| 378 | 379 | |
| 379 | 380 | #define CREATE_TRACE_POINTS |
| 380 | 381 | #include <trace/events/workqueue.h> |
| @@ -4447,7 +4448,7 @@ void destroy_workqueue(struct workqueue_struct *wq) · 4066 unmodified lines | ||
| 4447 | 4448 | raw_spin_unlock_irq(&pwq->pool->lock); |
| 4448 | 4449 | mutex_unlock(&wq->mutex); |
| 4449 | 4450 | mutex_unlock(&wq_pool_mutex); |
| 4450 | − show_workqueue_state(); | |
| 4451 | + show_one_workqueue(wq); | |
| 4451 | 4452 | return; |
| 4452 | 4453 | } |
| 4453 | 4454 | raw_spin_unlock_irq(&pwq->pool->lock); |
| @@ -4797,97 +4798,116 @@ static void show_pwq(struct pool_workqueue *pwq) · 343 unmodified lines | ||
| 4797 | 4798 | } |
| 4798 | 4799 | |
| 4799 | 4800 | /** |
| 4800 | − * show_workqueue_state - dump workqueue state | |
| 4801 | − * | |
| 4802 | − * Called from a sysrq handler or try_to_freeze_tasks() and prints out | |
| 4803 | − * all busy workqueues and pools. | |
| 4801 | + * show_one_workqueue - dump state of specified workqueue | |
| 4802 | + * @wq: workqueue whose state will be printed | |
| 4804 | 4803 | */ |
| 4805 | −void show_workqueue_state(void) | |
| 4804 | +void show_one_workqueue(struct workqueue_struct *wq) | |
| 4806 | 4805 | { |
| 4807 | − struct workqueue_struct *wq; | |
| 4808 | − struct worker_pool *pool; | |
| 4806 | + struct pool_workqueue *pwq; | |
| 4807 | + bool idle = true; | |
| 4809 | 4808 | unsigned long flags; |
| 4810 | − int pi; | |
| 4811 | − | |
| 4812 | − rcu_read_lock(); | |
| 4813 | 4809 | |
| 4814 | − pr_info("Showing busy workqueues and worker pools:\n"); | |
| 4815 | − | |
| 4816 | − list_for_each_entry_rcu(wq, &workqueues, list) { | |
| 4817 | − struct pool_workqueue *pwq; | |
| 4818 | − bool idle = true; | |
| 4819 | − | |
| 4820 | − for_each_pwq(pwq, wq) { | |
| 4821 | − if (pwq->nr_active || !list_empty(&pwq->inactive_works)) { | |
| 4822 | − idle = false; | |
| 4823 | − break; | |
| 4824 | − } | |
| 4810 | + for_each_pwq(pwq, wq) { | |
| 4811 | + if (pwq->nr_active || !list_empty(&pwq->inactive_works)) { | |
| 4812 | + idle = false; | |
| 4813 | + break; | |
| 4825 | 4814 | } |
| 4826 | − if (idle) | |
| 4827 | − continue; | |
| 4815 | + } | |
| 4816 | + if (idle) /* Nothing to print for idle workqueue */ | |
| 4817 | + return; | |
| 4828 | 4818 | |
| 4829 | − pr_info("workqueue %s: flags=0x%x\n", wq->name, wq->flags); | |
| 4819 | + pr_info("workqueue %s: flags=0x%x\n", wq->name, wq->flags); | |
| 4830 | 4820 | |
| 4831 | − for_each_pwq(pwq, wq) { | |
| 4832 | − raw_spin_lock_irqsave(&pwq->pool->lock, flags); | |
| 4833 | − if (pwq->nr_active || !list_empty(&pwq->inactive_works)) { | |
| 4834 | − /* | |
| 4835 | − * Defer printing to avoid deadlocks in console | |
| 4836 | − * drivers that queue work while holding locks | |
| 4837 | − * also taken in their write paths. | |
| 4838 | − */ | |
| 4839 | − printk_deferred_enter(); | |
| 4840 | − show_pwq(pwq); | |
| 4841 | − printk_deferred_exit(); | |
| 4842 | − } | |
| 4843 | − raw_spin_unlock_irqrestore(&pwq->pool->lock, flags); | |
| 4821 | + for_each_pwq(pwq, wq) { | |
| 4822 | + raw_spin_lock_irqsave(&pwq->pool->lock, flags); | |
| 4823 | + if (pwq->nr_active || !list_empty(&pwq->inactive_works)) { | |
| 4844 | 4824 | /* |
| 4845 | − * We could be printing a lot from atomic context, e.g. | |
| 4846 | − * sysrq-t -> show_workqueue_state(). Avoid triggering | |
| 4847 | − * hard lockup. | |
| 4825 | + * Defer printing to avoid deadlocks in console | |
| 4826 | + * drivers that queue work while holding locks | |
| 4827 | + * also taken in their write paths. | |
| 4848 | 4828 | */ |
| 4849 | − touch_nmi_watchdog(); | |
| 4850 | − } | |
| 4851 | − } | |
| 4852 | − | |
| 4853 | − for_each_pool(pool, pi) { | |
| 4854 | − struct worker *worker; | |
| 4855 | − bool first = true; | |
| 4856 | − | |
| 4857 | − raw_spin_lock_irqsave(&pool->lock, flags); | |
| 4858 | − if (pool->nr_workers == pool->nr_idle) | |
| 4859 | − goto next_pool; | |
| 4860 | − /* | |
| 4861 | − * Defer printing to avoid deadlocks in console drivers that | |
| 4862 | − * queue work while holding locks also taken in their write | |
| 4863 | − * paths. | |
| 4864 | − */ | |
| 4865 | − printk_deferred_enter(); | |
| 4866 | − pr_info("pool %d:", pool->id); | |
| 4867 | − pr_cont_pool_info(pool); | |
| 4868 | − pr_cont(" hung=%us workers=%d", | |
| 4869 | − jiffies_to_msecs(jiffies - pool->watchdog_ts) / 1000, | |
| 4870 | − pool->nr_workers); | |
| 4871 | − if (pool->manager) | |
| 4872 | − pr_cont(" manager: %d", | |
| 4873 | − task_pid_nr(pool->manager->task)); | |
| 4874 | − list_for_each_entry(worker, &pool->idle_list, entry) { | |
| 4875 | − pr_cont(" %s%d", first ? "idle: " : "", | |
| 4876 | − task_pid_nr(worker->task)); | |
| 4877 | − first = false; | |
| 4829 | + printk_deferred_enter(); | |
| 4830 | + show_pwq(pwq); | |
| 4831 | + printk_deferred_exit(); | |
| 4878 | 4832 | } |
| 4879 | − pr_cont("\n"); | |
| 4880 | − printk_deferred_exit(); | |
| 4881 | − next_pool: | |
| 4882 | − raw_spin_unlock_irqrestore(&pool->lock, flags); | |
| 4833 | + raw_spin_unlock_irqrestore(&pwq->pool->lock, flags); | |
| 4883 | 4834 | /* |
| 4884 | 4835 | * We could be printing a lot from atomic context, e.g. |
| 4885 | − * sysrq-t -> show_workqueue_state(). Avoid triggering | |
| 4836 | + * sysrq-t -> show_all_workqueues(). Avoid triggering | |
| 4886 | 4837 | * hard lockup. |
| 4887 | 4838 | */ |
| 4888 | 4839 | touch_nmi_watchdog(); |
| 4889 | 4840 | } |
| 4890 | 4841 | |
| 4842 | +} | |
| 4843 | + | |
| 4844 | +/** | |
| 4845 | + * show_one_worker_pool - dump state of specified worker pool | |
| 4846 | + * @pool: worker pool whose state will be printed | |
| 4847 | + */ | |
| 4848 | +static void show_one_worker_pool(struct worker_pool *pool) | |
| 4849 | +{ | |
| 4850 | + struct worker *worker; | |
| 4851 | + bool first = true; | |
| 4852 | + unsigned long flags; | |
| 4853 | + | |
| 4854 | + raw_spin_lock_irqsave(&pool->lock, flags); | |
| 4855 | + if (pool->nr_workers == pool->nr_idle) | |
| 4856 | + goto next_pool; | |
| 4857 | + /* | |
| 4858 | + * Defer printing to avoid deadlocks in console drivers that | |
| 4859 | + * queue work while holding locks also taken in their write | |
| 4860 | + * paths. | |
| 4861 | + */ | |
| 4862 | + printk_deferred_enter(); | |
| 4863 | + pr_info("pool %d:", pool->id); | |
| 4864 | + pr_cont_pool_info(pool); | |
| 4865 | + pr_cont(" hung=%us workers=%d", | |
| 4866 | + jiffies_to_msecs(jiffies - pool->watchdog_ts) / 1000, | |
| 4867 | + pool->nr_workers); | |
| 4868 | + if (pool->manager) | |
| 4869 | + pr_cont(" manager: %d", | |
| 4870 | + task_pid_nr(pool->manager->task)); | |
| 4871 | + list_for_each_entry(worker, &pool->idle_list, entry) { | |
| 4872 | + pr_cont(" %s%d", first ? "idle: " : "", | |
| 4873 | + task_pid_nr(worker->task)); | |
| 4874 | + first = false; | |
| 4875 | + } | |
| 4876 | + pr_cont("\n"); | |
| 4877 | + printk_deferred_exit(); | |
| 4878 | +next_pool: | |
| 4879 | + raw_spin_unlock_irqrestore(&pool->lock, flags); | |
| 4880 | + /* | |
| 4881 | + * We could be printing a lot from atomic context, e.g. | |
| 4882 | + * sysrq-t -> show_all_workqueues(). Avoid triggering | |
| 4883 | + * hard lockup. | |
| 4884 | + */ | |
| 4885 | + touch_nmi_watchdog(); | |
| 4886 | + | |
| 4887 | +} | |
| 4888 | + | |
| 4889 | +/** | |
| 4890 | + * show_all_workqueues - dump workqueue state | |
| 4891 | + * | |
| 4892 | + * Called from a sysrq handler or try_to_freeze_tasks() and prints out | |
| 4893 | + * all busy workqueues and pools. | |
| 4894 | + */ | |
| 4895 | +void show_all_workqueues(void) | |
| 4896 | +{ | |
| 4897 | + struct workqueue_struct *wq; | |
| 4898 | + struct worker_pool *pool; | |
| 4899 | + int pi; | |
| 4900 | + | |
| 4901 | + rcu_read_lock(); | |
| 4902 | + | |
| 4903 | + pr_info("Showing busy workqueues and worker pools:\n"); | |
| 4904 | + | |
| 4905 | + list_for_each_entry_rcu(wq, &workqueues, list) | |
| 4906 | + show_one_workqueue(wq); | |
| 4907 | + | |
| 4908 | + for_each_pool(pool, pi) | |
| 4909 | + show_one_worker_pool(pool); | |
| 4910 | + | |
| 4891 | 4911 | rcu_read_unlock(); |
| 4892 | 4912 | } |
| 4893 | 4913 | |
| @@ -5876,7 +5896,7 @@ static void wq_watchdog_timer_fn(struct timer_list *unused) · 982 unmodified lines | ||
| 5876 | 5896 | rcu_read_unlock(); |
| 5877 | 5897 | |
| 5878 | 5898 | if (lockup_detected) |
| 5879 | − show_workqueue_state(); | |
| 5899 | + show_all_workqueues(); | |
| 5880 | 5900 | |
| 5881 | 5901 | wq_watchdog_reset_touched(); |
| 5882 | 5902 | mod_timer(&wq_watchdog_timer, jiffies + thresh); |