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 | static int worker_thread(void *__worker); | 375 | static int worker_thread(void *__worker); |
| 376 | static void workqueue_sysfs_unregister(struct workqueue_struct *wq); | 376 | static void workqueue_sysfs_unregister(struct workqueue_struct *wq); |
| 377 | static void show_pwq(struct pool_workqueue *pwq); | 377 | static void show_pwq(struct pool_workqueue *pwq); |
| 378 | static void show_one_worker_pool(struct worker_pool *pool); | ||
| 378 | 379 | ||
| 379 | #define CREATE_TRACE_POINTS | 380 | #define CREATE_TRACE_POINTS |
| 380 | #include <trace/events/workqueue.h> | 381 | #include <trace/events/workqueue.h> |
| @@ -4447,7 +4448,7 @@ void destroy_workqueue(struct workqueue_struct *wq) · 4066 unmodified lines | |||
| 4447 | raw_spin_unlock_irq(&pwq->pool->lock); | 4448 | raw_spin_unlock_irq(&pwq->pool->lock); |
| 4448 | mutex_unlock(&wq->mutex); | 4449 | mutex_unlock(&wq->mutex); |
| 4449 | mutex_unlock(&wq_pool_mutex); | 4450 | mutex_unlock(&wq_pool_mutex); |
| 4450 | show_workqueue_state(); | 4451 | show_one_workqueue(wq); |
| 4451 | return; | 4452 | return; |
| 4452 | } | 4453 | } |
| 4453 | raw_spin_unlock_irq(&pwq->pool->lock); | 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 | * show_one_workqueue - dump state of specified workqueue |
| 4801 | * | 4802 | * @wq: workqueue whose state will be printed |
| 4802 | * Called from a sysrq handler or try_to_freeze_tasks() and prints out | ||
| 4803 | * all busy workqueues and pools. | ||
| 4804 | */ | 4803 | */ |
| 4805 | void show_workqueue_state(void) | 4804 | void show_one_workqueue(struct workqueue_struct *wq) |
| 4806 | { | 4805 | { |
| 4807 | struct workqueue_struct *wq; | 4806 | struct pool_workqueue *pwq; |
| 4808 | struct worker_pool *pool; | 4807 | bool idle = true; |
| 4809 | unsigned long flags; | 4808 | unsigned long flags; |
| 4810 | int pi; | ||
| 4811 | |||
| 4812 | rcu_read_lock(); | ||
| 4813 | 4809 | ||
| 4814 | pr_info("Showing busy workqueues and worker pools:\n"); | 4810 | for_each_pwq(pwq, wq) { |
| 4815 | 4811 | if (pwq->nr_active || !list_empty(&pwq->inactive_works)) { | |
| 4816 | list_for_each_entry_rcu(wq, &workqueues, list) { | 4812 | idle = false; |
| 4817 | struct pool_workqueue *pwq; | 4813 | break; |
| 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 | } | ||
| 4825 | } | 4814 | } |
| 4826 | if (idle) | 4815 | } |
| 4827 | continue; | 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) { | 4821 | for_each_pwq(pwq, wq) { |
| 4832 | raw_spin_lock_irqsave(&pwq->pool->lock, flags); | 4822 | raw_spin_lock_irqsave(&pwq->pool->lock, flags); |
| 4833 | if (pwq->nr_active || !list_empty(&pwq->inactive_works)) { | 4823 | 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); | ||
| 4844 | /* | 4824 | /* |
| 4845 | * We could be printing a lot from atomic context, e.g. | 4825 | * Defer printing to avoid deadlocks in console |
| 4846 | * sysrq-t -> show_workqueue_state(). Avoid triggering | 4826 | * drivers that queue work while holding locks |
| 4847 | * hard lockup. | 4827 | * also taken in their write paths. |
| 4848 | */ | 4828 | */ |
| 4849 | touch_nmi_watchdog(); | 4829 | printk_deferred_enter(); |
| 4850 | } | 4830 | show_pwq(pwq); |
| 4851 | } | 4831 | printk_deferred_exit(); |
| 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; | ||
| 4878 | } | 4832 | } |
| 4879 | pr_cont("\n"); | 4833 | raw_spin_unlock_irqrestore(&pwq->pool->lock, flags); |
| 4880 | printk_deferred_exit(); | ||
| 4881 | next_pool: | ||
| 4882 | raw_spin_unlock_irqrestore(&pool->lock, flags); | ||
| 4883 | /* | 4834 | /* |
| 4884 | * We could be printing a lot from atomic context, e.g. | 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 | * hard lockup. | 4837 | * hard lockup. |
| 4887 | */ | 4838 | */ |
| 4888 | touch_nmi_watchdog(); | 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 | rcu_read_unlock(); | 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 | rcu_read_unlock(); | 5896 | rcu_read_unlock(); |
| 5877 | 5897 | ||
| 5878 | if (lockup_detected) | 5898 | if (lockup_detected) |
| 5879 | show_workqueue_state(); | 5899 | show_all_workqueues(); |
| 5880 | 5900 | ||
| 5881 | wq_watchdog_reset_touched(); | 5901 | wq_watchdog_reset_touched(); |
| 5882 | mod_timer(&wq_watchdog_timer, jiffies + thresh); | 5902 | mod_timer(&wq_watchdog_timer, jiffies + thresh); |