aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/tty/sysrq.c1
-rw-r--r--include/linux/workqueue.h1
-rw-r--r--kernel/workqueue.c160
3 files changed, 162 insertions, 0 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 259a4d5a4e8f..843f2cdc280b 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -275,6 +275,7 @@ static struct sysrq_key_op sysrq_showregs_op = {
275static void sysrq_handle_showstate(int key) 275static void sysrq_handle_showstate(int key)
276{ 276{
277 show_state(); 277 show_state();
278 show_workqueue_state();
278} 279}
279static struct sysrq_key_op sysrq_showstate_op = { 280static struct sysrq_key_op sysrq_showstate_op = {
280 .handler = sysrq_handle_showstate, 281 .handler = sysrq_handle_showstate,
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index f597846ff605..deee212af8e0 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -454,6 +454,7 @@ extern bool workqueue_congested(int cpu, struct workqueue_struct *wq);
454extern unsigned int work_busy(struct work_struct *work); 454extern unsigned int work_busy(struct work_struct *work);
455extern __printf(1, 2) void set_worker_desc(const char *fmt, ...); 455extern __printf(1, 2) void set_worker_desc(const char *fmt, ...);
456extern void print_worker_info(const char *log_lvl, struct task_struct *task); 456extern void print_worker_info(const char *log_lvl, struct task_struct *task);
457extern void show_workqueue_state(void);
457 458
458/** 459/**
459 * queue_work - queue work on a workqueue 460 * queue_work - queue work on a workqueue
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 0c329a6f0c51..1ca0b1d54e70 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -4457,6 +4457,166 @@ void print_worker_info(const char *log_lvl, struct task_struct *task)
4457 } 4457 }
4458} 4458}
4459 4459
4460static void pr_cont_pool_info(struct worker_pool *pool)
4461{
4462 pr_cont(" cpus=%*pbl", nr_cpumask_bits, pool->attrs->cpumask);
4463 if (pool->node != NUMA_NO_NODE)
4464 pr_cont(" node=%d", pool->node);
4465 pr_cont(" flags=0x%x nice=%d", pool->flags, pool->attrs->nice);
4466}
4467
4468static void pr_cont_work(bool comma, struct work_struct *work)
4469{
4470 if (work->func == wq_barrier_func) {
4471 struct wq_barrier *barr;
4472
4473 barr = container_of(work, struct wq_barrier, work);
4474
4475 pr_cont("%s BAR(%d)", comma ? "," : "",
4476 task_pid_nr(barr->task));
4477 } else {
4478 pr_cont("%s %pf", comma ? "," : "", work->func);
4479 }
4480}
4481
4482static void show_pwq(struct pool_workqueue *pwq)
4483{
4484 struct worker_pool *pool = pwq->pool;
4485 struct work_struct *work;
4486 struct worker *worker;
4487 bool has_in_flight = false, has_pending = false;
4488 int bkt;
4489
4490 pr_info(" pwq %d:", pool->id);
4491 pr_cont_pool_info(pool);
4492
4493 pr_cont(" active=%d/%d%s\n", pwq->nr_active, pwq->max_active,
4494 !list_empty(&pwq->mayday_node) ? " MAYDAY" : "");
4495
4496 hash_for_each(pool->busy_hash, bkt, worker, hentry) {
4497 if (worker->current_pwq == pwq) {
4498 has_in_flight = true;
4499 break;
4500 }
4501 }
4502 if (has_in_flight) {
4503 bool comma = false;
4504
4505 pr_info(" in-flight:");
4506 hash_for_each(pool->busy_hash, bkt, worker, hentry) {
4507 if (worker->current_pwq != pwq)
4508 continue;
4509
4510 pr_cont("%s %d%s:%pf", comma ? "," : "",
4511 task_pid_nr(worker->task),
4512 worker == pwq->wq->rescuer ? "(RESCUER)" : "",
4513 worker->current_func);
4514 list_for_each_entry(work, &worker->scheduled, entry)
4515 pr_cont_work(false, work);
4516 comma = true;
4517 }
4518 pr_cont("\n");
4519 }
4520
4521 list_for_each_entry(work, &pool->worklist, entry) {
4522 if (get_work_pwq(work) == pwq) {
4523 has_pending = true;
4524 break;
4525 }
4526 }
4527 if (has_pending) {
4528 bool comma = false;
4529
4530 pr_info(" pending:");
4531 list_for_each_entry(work, &pool->worklist, entry) {
4532 if (get_work_pwq(work) != pwq)
4533 continue;
4534
4535 pr_cont_work(comma, work);
4536 comma = !(*work_data_bits(work) & WORK_STRUCT_LINKED);
4537 }
4538 pr_cont("\n");
4539 }
4540
4541 if (!list_empty(&pwq->delayed_works)) {
4542 bool comma = false;
4543
4544 pr_info(" delayed:");
4545 list_for_each_entry(work, &pwq->delayed_works, entry) {
4546 pr_cont_work(comma, work);
4547 comma = !(*work_data_bits(work) & WORK_STRUCT_LINKED);
4548 }
4549 pr_cont("\n");
4550 }
4551}
4552
4553/**
4554 * show_workqueue_state - dump workqueue state
4555 *
4556 * Called from a sysrq handler and prints out all busy workqueues and
4557 * pools.
4558 */
4559void show_workqueue_state(void)
4560{
4561 struct workqueue_struct *wq;
4562 struct worker_pool *pool;
4563 unsigned long flags;
4564 int pi;
4565
4566 rcu_read_lock_sched();
4567
4568 pr_info("Showing busy workqueues and worker pools:\n");
4569
4570 list_for_each_entry_rcu(wq, &workqueues, list) {
4571 struct pool_workqueue *pwq;
4572 bool idle = true;
4573
4574 for_each_pwq(pwq, wq) {
4575 if (pwq->nr_active || !list_empty(&pwq->delayed_works)) {
4576 idle = false;
4577 break;
4578 }
4579 }
4580 if (idle)
4581 continue;
4582
4583 pr_info("workqueue %s: flags=0x%x\n", wq->name, wq->flags);
4584
4585 for_each_pwq(pwq, wq) {
4586 spin_lock_irqsave(&pwq->pool->lock, flags);
4587 if (pwq->nr_active || !list_empty(&pwq->delayed_works))
4588 show_pwq(pwq);
4589 spin_unlock_irqrestore(&pwq->pool->lock, flags);
4590 }
4591 }
4592
4593 for_each_pool(pool, pi) {
4594 struct worker *worker;
4595 bool first = true;
4596
4597 spin_lock_irqsave(&pool->lock, flags);
4598 if (pool->nr_workers == pool->nr_idle)
4599 goto next_pool;
4600
4601 pr_info("pool %d:", pool->id);
4602 pr_cont_pool_info(pool);
4603 pr_cont(" workers=%d", pool->nr_workers);
4604 if (pool->manager)
4605 pr_cont(" manager: %d",
4606 task_pid_nr(pool->manager->task));
4607 list_for_each_entry(worker, &pool->idle_list, entry) {
4608 pr_cont(" %s%d", first ? "idle: " : "",
4609 task_pid_nr(worker->task));
4610 first = false;
4611 }
4612 pr_cont("\n");
4613 next_pool:
4614 spin_unlock_irqrestore(&pool->lock, flags);
4615 }
4616
4617 rcu_read_unlock_sched();
4618}
4619
4460/* 4620/*
4461 * CPU hotplug. 4621 * CPU hotplug.
4462 * 4622 *