1/* 2 * Workqueue statistical tracer. 3 * 4 * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com> 5 * 6 */ 7 8 9#include <trace/events/workqueue.h> 10#include <linux/list.h> 11#include <linux/percpu.h> 12#include <linux/slab.h> 13#include <linux/kref.h> 14#include "trace_stat.h" 15#include "trace.h" 16 17 18/* A cpu workqueue thread */ 19struct cpu_workqueue_stats { 20 struct list_head list; 21 struct kref kref; 22 int cpu; 23 pid_t pid; 24/* Can be inserted from interrupt or user context, need to be atomic */ 25 atomic_t inserted; 26/* 27 * Don't need to be atomic, works are serialized in a single workqueue thread 28 * on a single CPU. 29 */ 30 unsigned int executed; 31}; 32 33/* List of workqueue threads on one cpu */ 34struct workqueue_global_stats { 35 struct list_head list; 36 spinlock_t lock; 37}; 38 39/* Don't need a global lock because allocated before the workqueues, and 40 * never freed. 41 */ 42static DEFINE_PER_CPU(struct workqueue_global_stats, all_workqueue_stat); 43#define workqueue_cpu_stat(cpu) (&per_cpu(all_workqueue_stat, cpu)) 44 45static void cpu_workqueue_stat_free(struct kref *kref) 46{ 47 kfree(container_of(kref, struct cpu_workqueue_stats, kref)); 48} 49 50/* Insertion of a work */ 51static void 52probe_workqueue_insertion(void *ignore, 53 struct task_struct *wq_thread, 54 struct work_struct *work) 55{ 56 int cpu = cpumask_first(&wq_thread->cpus_allowed); 57 struct cpu_workqueue_stats *node; 58 unsigned long flags; 59 60 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); 61 list_for_each_entry(node, &workqueue_cpu_stat(cpu)->list, list) { 62 if (node->pid == wq_thread->pid) { 63 atomic_inc(&node->inserted); 64 goto found; 65 } 66 } 67 pr_debug("trace_workqueue: entry not found\n"); 68found: 69 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); 70} 71 72/* Execution of a work */ 73static void 74probe_workqueue_execution(void *ignore, 75 struct task_struct *wq_thread, 76 struct work_struct *work) 77{ 78 int cpu = cpumask_first(&wq_thread->cpus_allowed); 79 struct cpu_workqueue_stats *node; 80 unsigned long flags; 81 82 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); 83 list_for_each_entry(node, &workqueue_cpu_stat(cpu)->list, list) { 84 if (node->pid == wq_thread->pid) { 85 node->executed++; 86 goto found; 87 } 88 } 89 pr_debug("trace_workqueue: entry not found\n"); 90found: 91 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); 92} 93 94/* Creation of a cpu workqueue thread */ 95static void probe_workqueue_creation(void *ignore, 96 struct task_struct *wq_thread, int cpu) 97{ 98 struct cpu_workqueue_stats *cws; 99 unsigned long flags; 100 101 WARN_ON(cpu < 0); 102 103 /* Workqueues are sometimes created in atomic context */ 104 cws = kzalloc(sizeof(struct cpu_workqueue_stats), GFP_ATOMIC); 105 if (!cws) { 106 pr_warning("trace_workqueue: not enough memory\n"); 107 return; 108 } 109 INIT_LIST_HEAD(&cws->list); 110 kref_init(&cws->kref); 111 cws->cpu = cpu; 112 cws->pid = wq_thread->pid; 113 114 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); 115 list_add_tail(&cws->list, &workqueue_cpu_stat(cpu)->list); 116 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); 117} 118 119/* Destruction of a cpu workqueue thread */ 120static void 121probe_workqueue_destruction(void *ignore, struct task_struct *wq_thread) 122{ 123 /* Workqueue only execute on one cpu */ 124 int cpu = cpumask_first(&wq_thread->cpus_allowed); 125 struct cpu_workqueue_stats *node, *next; 126 unsigned long flags; 127 128 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); 129 list_for_each_entry_safe(node, next, &workqueue_cpu_stat(cpu)->list, 130 list) { 131 if (node->pid == wq_thread->pid) { 132 list_del(&node->list); 133 kref_put(&node->kref, cpu_workqueue_stat_free); 134 goto found; 135 } 136 } 137 138 pr_debug("trace_workqueue: don't find workqueue to destroy\n"); 139found: 140 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); 141 142} 143 144static struct cpu_workqueue_stats *workqueue_stat_start_cpu(int cpu) 145{ 146 unsigned long flags; 147 struct cpu_workqueue_stats *ret = NULL; 148 149 150 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); 151 152 if (!list_empty(&workqueue_cpu_stat(cpu)->list)) { 153 ret = list_entry(workqueue_cpu_stat(cpu)->list.next, 154 struct cpu_workqueue_stats, list); 155 kref_get(&ret->kref); 156 } 157 158 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); 159 160 return ret; 161} 162 163static void *workqueue_stat_start(struct tracer_stat *trace) 164{ 165 int cpu; 166 void *ret = NULL; 167 168 for_each_possible_cpu(cpu) { 169 ret = workqueue_stat_start_cpu(cpu); 170 if (ret) 171 return ret; 172 } 173 return NULL; 174} 175 176static void *workqueue_stat_next(void *prev, int idx) 177{ 178 struct cpu_workqueue_stats *prev_cws = prev; 179 struct cpu_workqueue_stats *ret; 180 int cpu = prev_cws->cpu; 181 unsigned long flags; 182 183 spin_lock_irqsave(&workqueue_cpu_stat(cpu)->lock, flags); 184 if (list_is_last(&prev_cws->list, &workqueue_cpu_stat(cpu)->list)) { 185 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); 186 do { 187 cpu = cpumask_next(cpu, cpu_possible_mask); 188 if (cpu >= nr_cpu_ids) 189 return NULL; 190 } while (!(ret = workqueue_stat_start_cpu(cpu))); 191 return ret; 192 } else { 193 ret = list_entry(prev_cws->list.next, 194 struct cpu_workqueue_stats, list); 195 kref_get(&ret->kref); 196 } 197 spin_unlock_irqrestore(&workqueue_cpu_stat(cpu)->lock, flags); 198 199 return ret; 200} 201 202static int workqueue_stat_show(struct seq_file *s, void *p) 203{ 204 struct cpu_workqueue_stats *cws = p; 205 struct pid *pid; 206 struct task_struct *tsk; 207 208 pid = find_get_pid(cws->pid); 209 if (pid) { 210 tsk = get_pid_task(pid, PIDTYPE_PID); 211 if (tsk) { 212 seq_printf(s, "%3d %6d %6u %s\n", cws->cpu, 213 atomic_read(&cws->inserted), cws->executed, 214 tsk->comm); 215 put_task_struct(tsk); 216 } 217 put_pid(pid); 218 } 219 220 return 0; 221} 222 223static void workqueue_stat_release(void *stat) 224{ 225 struct cpu_workqueue_stats *node = stat; 226 227 kref_put(&node->kref, cpu_workqueue_stat_free); 228} 229 230static int workqueue_stat_headers(struct seq_file *s) 231{ 232 seq_printf(s, "# CPU INSERTED EXECUTED NAME\n"); 233 seq_printf(s, "# | | | |\n"); 234 return 0; 235} 236 237struct tracer_stat workqueue_stats __read_mostly = { 238 .name = "workqueues", 239 .stat_start = workqueue_stat_start, 240 .stat_next = workqueue_stat_next, 241 .stat_show = workqueue_stat_show, 242 .stat_release = workqueue_stat_release, 243 .stat_headers = workqueue_stat_headers 244}; 245 246 247int __init stat_workqueue_init(void) 248{ 249 if (register_stat_tracer(&workqueue_stats)) { 250 pr_warning("Unable to register workqueue stat tracer\n"); 251 return 1; 252 } 253 254 return 0; 255} 256fs_initcall(stat_workqueue_init); 257 258/* 259 * Workqueues are created very early, just after pre-smp initcalls. 260 * So we must register our tracepoints at this stage. 261 */ 262int __init trace_workqueue_early_init(void) 263{ 264 int ret, cpu; 265 266 ret = register_trace_workqueue_insertion(probe_workqueue_insertion, NULL); 267 if (ret) 268 goto out; 269 270 ret = register_trace_workqueue_execution(probe_workqueue_execution, NULL); 271 if (ret) 272 goto no_insertion; 273 274 ret = register_trace_workqueue_creation(probe_workqueue_creation, NULL); 275 if (ret) 276 goto no_execution; 277 278 ret = register_trace_workqueue_destruction(probe_workqueue_destruction, NULL); 279 if (ret) 280 goto no_creation; 281 282 for_each_possible_cpu(cpu) { 283 spin_lock_init(&workqueue_cpu_stat(cpu)->lock); 284 INIT_LIST_HEAD(&workqueue_cpu_stat(cpu)->list); 285 } 286 287 return 0; 288 289no_creation: 290 unregister_trace_workqueue_creation(probe_workqueue_creation, NULL); 291no_execution: 292 unregister_trace_workqueue_execution(probe_workqueue_execution, NULL); 293no_insertion: 294 unregister_trace_workqueue_insertion(probe_workqueue_insertion, NULL); 295out: 296 pr_warning("trace_workqueue: unable to trace workqueues\n"); 297 298 return 1; 299} 300early_initcall(trace_workqueue_early_init); 301