workqueue: include workqueue info when printing debug dump of a worker task
[linux-2.6-block.git] / kernel / workqueue.c
index 154aa12af48e6a29313b6214bcb5c24fbf88e343..4aa9f5bc6b2dfdd9f152f5d20bf06c15f070aa21 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/rculist.h>
 #include <linux/nodemask.h>
 #include <linux/moduleparam.h>
+#include <linux/uaccess.h>
 
 #include "workqueue_internal.h"
 
@@ -2197,6 +2198,7 @@ __acquires(&pool->lock)
        worker->current_work = NULL;
        worker->current_func = NULL;
        worker->current_pwq = NULL;
+       worker->desc_valid = false;
        pwq_dec_nr_in_flight(pwq, work_color);
 }
 
@@ -4365,6 +4367,83 @@ unsigned int work_busy(struct work_struct *work)
 }
 EXPORT_SYMBOL_GPL(work_busy);
 
+/**
+ * set_worker_desc - set description for the current work item
+ * @fmt: printf-style format string
+ * @...: arguments for the format string
+ *
+ * This function can be called by a running work function to describe what
+ * the work item is about.  If the worker task gets dumped, this
+ * information will be printed out together to help debugging.  The
+ * description can be at most WORKER_DESC_LEN including the trailing '\0'.
+ */
+void set_worker_desc(const char *fmt, ...)
+{
+       struct worker *worker = current_wq_worker();
+       va_list args;
+
+       if (worker) {
+               va_start(args, fmt);
+               vsnprintf(worker->desc, sizeof(worker->desc), fmt, args);
+               va_end(args);
+               worker->desc_valid = true;
+       }
+}
+
+/**
+ * print_worker_info - print out worker information and description
+ * @log_lvl: the log level to use when printing
+ * @task: target task
+ *
+ * If @task is a worker and currently executing a work item, print out the
+ * name of the workqueue being serviced and worker description set with
+ * set_worker_desc() by the currently executing work item.
+ *
+ * This function can be safely called on any task as long as the
+ * task_struct itself is accessible.  While safe, this function isn't
+ * synchronized and may print out mixups or garbages of limited length.
+ */
+void print_worker_info(const char *log_lvl, struct task_struct *task)
+{
+       work_func_t *fn = NULL;
+       char name[WQ_NAME_LEN] = { };
+       char desc[WORKER_DESC_LEN] = { };
+       struct pool_workqueue *pwq = NULL;
+       struct workqueue_struct *wq = NULL;
+       bool desc_valid = false;
+       struct worker *worker;
+
+       if (!(task->flags & PF_WQ_WORKER))
+               return;
+
+       /*
+        * This function is called without any synchronization and @task
+        * could be in any state.  Be careful with dereferences.
+        */
+       worker = probe_kthread_data(task);
+
+       /*
+        * Carefully copy the associated workqueue's workfn and name.  Keep
+        * the original last '\0' in case the original contains garbage.
+        */
+       probe_kernel_read(&fn, &worker->current_func, sizeof(fn));
+       probe_kernel_read(&pwq, &worker->current_pwq, sizeof(pwq));
+       probe_kernel_read(&wq, &pwq->wq, sizeof(wq));
+       probe_kernel_read(name, wq->name, sizeof(name) - 1);
+
+       /* copy worker description */
+       probe_kernel_read(&desc_valid, &worker->desc_valid, sizeof(desc_valid));
+       if (desc_valid)
+               probe_kernel_read(desc, worker->desc, sizeof(desc) - 1);
+
+       if (fn || name[0] || desc[0]) {
+               printk("%sWorkqueue: %s %pf", log_lvl, name, fn);
+               if (desc[0])
+                       pr_cont(" (%s)", desc);
+               pr_cont("\n");
+       }
+}
+
 /*
  * CPU hotplug.
  *