diff options
Diffstat (limited to 'kernel/tracepoint.c')
-rw-r--r-- | kernel/tracepoint.c | 105 |
1 files changed, 63 insertions, 42 deletions
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index cc89be5bc0f8..e95ee7f31d43 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/err.h> | 25 | #include <linux/err.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
28 | #include <linux/jump_label.h> | ||
28 | 29 | ||
29 | extern struct tracepoint __start___tracepoints[]; | 30 | extern struct tracepoint __start___tracepoints[]; |
30 | extern struct tracepoint __stop___tracepoints[]; | 31 | extern struct tracepoint __stop___tracepoints[]; |
@@ -54,7 +55,7 @@ static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; | |||
54 | */ | 55 | */ |
55 | struct tracepoint_entry { | 56 | struct tracepoint_entry { |
56 | struct hlist_node hlist; | 57 | struct hlist_node hlist; |
57 | void **funcs; | 58 | struct tracepoint_func *funcs; |
58 | int refcount; /* Number of times armed. 0 if disarmed. */ | 59 | int refcount; /* Number of times armed. 0 if disarmed. */ |
59 | char name[0]; | 60 | char name[0]; |
60 | }; | 61 | }; |
@@ -64,12 +65,12 @@ struct tp_probes { | |||
64 | struct rcu_head rcu; | 65 | struct rcu_head rcu; |
65 | struct list_head list; | 66 | struct list_head list; |
66 | } u; | 67 | } u; |
67 | void *probes[0]; | 68 | struct tracepoint_func probes[0]; |
68 | }; | 69 | }; |
69 | 70 | ||
70 | static inline void *allocate_probes(int count) | 71 | static inline void *allocate_probes(int count) |
71 | { | 72 | { |
72 | struct tp_probes *p = kmalloc(count * sizeof(void *) | 73 | struct tp_probes *p = kmalloc(count * sizeof(struct tracepoint_func) |
73 | + sizeof(struct tp_probes), GFP_KERNEL); | 74 | + sizeof(struct tp_probes), GFP_KERNEL); |
74 | return p == NULL ? NULL : p->probes; | 75 | return p == NULL ? NULL : p->probes; |
75 | } | 76 | } |
@@ -79,7 +80,7 @@ static void rcu_free_old_probes(struct rcu_head *head) | |||
79 | kfree(container_of(head, struct tp_probes, u.rcu)); | 80 | kfree(container_of(head, struct tp_probes, u.rcu)); |
80 | } | 81 | } |
81 | 82 | ||
82 | static inline void release_probes(void *old) | 83 | static inline void release_probes(struct tracepoint_func *old) |
83 | { | 84 | { |
84 | if (old) { | 85 | if (old) { |
85 | struct tp_probes *tp_probes = container_of(old, | 86 | struct tp_probes *tp_probes = container_of(old, |
@@ -95,15 +96,16 @@ static void debug_print_probes(struct tracepoint_entry *entry) | |||
95 | if (!tracepoint_debug || !entry->funcs) | 96 | if (!tracepoint_debug || !entry->funcs) |
96 | return; | 97 | return; |
97 | 98 | ||
98 | for (i = 0; entry->funcs[i]; i++) | 99 | for (i = 0; entry->funcs[i].func; i++) |
99 | printk(KERN_DEBUG "Probe %d : %p\n", i, entry->funcs[i]); | 100 | printk(KERN_DEBUG "Probe %d : %p\n", i, entry->funcs[i].func); |
100 | } | 101 | } |
101 | 102 | ||
102 | static void * | 103 | static struct tracepoint_func * |
103 | tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe) | 104 | tracepoint_entry_add_probe(struct tracepoint_entry *entry, |
105 | void *probe, void *data) | ||
104 | { | 106 | { |
105 | int nr_probes = 0; | 107 | int nr_probes = 0; |
106 | void **old, **new; | 108 | struct tracepoint_func *old, *new; |
107 | 109 | ||
108 | WARN_ON(!probe); | 110 | WARN_ON(!probe); |
109 | 111 | ||
@@ -111,8 +113,9 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe) | |||
111 | old = entry->funcs; | 113 | old = entry->funcs; |
112 | if (old) { | 114 | if (old) { |
113 | /* (N -> N+1), (N != 0, 1) probes */ | 115 | /* (N -> N+1), (N != 0, 1) probes */ |
114 | for (nr_probes = 0; old[nr_probes]; nr_probes++) | 116 | for (nr_probes = 0; old[nr_probes].func; nr_probes++) |
115 | if (old[nr_probes] == probe) | 117 | if (old[nr_probes].func == probe && |
118 | old[nr_probes].data == data) | ||
116 | return ERR_PTR(-EEXIST); | 119 | return ERR_PTR(-EEXIST); |
117 | } | 120 | } |
118 | /* + 2 : one for new probe, one for NULL func */ | 121 | /* + 2 : one for new probe, one for NULL func */ |
@@ -120,9 +123,10 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe) | |||
120 | if (new == NULL) | 123 | if (new == NULL) |
121 | return ERR_PTR(-ENOMEM); | 124 | return ERR_PTR(-ENOMEM); |
122 | if (old) | 125 | if (old) |
123 | memcpy(new, old, nr_probes * sizeof(void *)); | 126 | memcpy(new, old, nr_probes * sizeof(struct tracepoint_func)); |
124 | new[nr_probes] = probe; | 127 | new[nr_probes].func = probe; |
125 | new[nr_probes + 1] = NULL; | 128 | new[nr_probes].data = data; |
129 | new[nr_probes + 1].func = NULL; | ||
126 | entry->refcount = nr_probes + 1; | 130 | entry->refcount = nr_probes + 1; |
127 | entry->funcs = new; | 131 | entry->funcs = new; |
128 | debug_print_probes(entry); | 132 | debug_print_probes(entry); |
@@ -130,10 +134,11 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe) | |||
130 | } | 134 | } |
131 | 135 | ||
132 | static void * | 136 | static void * |
133 | tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe) | 137 | tracepoint_entry_remove_probe(struct tracepoint_entry *entry, |
138 | void *probe, void *data) | ||
134 | { | 139 | { |
135 | int nr_probes = 0, nr_del = 0, i; | 140 | int nr_probes = 0, nr_del = 0, i; |
136 | void **old, **new; | 141 | struct tracepoint_func *old, *new; |
137 | 142 | ||
138 | old = entry->funcs; | 143 | old = entry->funcs; |
139 | 144 | ||
@@ -142,8 +147,10 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe) | |||
142 | 147 | ||
143 | debug_print_probes(entry); | 148 | debug_print_probes(entry); |
144 | /* (N -> M), (N > 1, M >= 0) probes */ | 149 | /* (N -> M), (N > 1, M >= 0) probes */ |
145 | for (nr_probes = 0; old[nr_probes]; nr_probes++) { | 150 | for (nr_probes = 0; old[nr_probes].func; nr_probes++) { |
146 | if ((!probe || old[nr_probes] == probe)) | 151 | if (!probe || |
152 | (old[nr_probes].func == probe && | ||
153 | old[nr_probes].data == data)) | ||
147 | nr_del++; | 154 | nr_del++; |
148 | } | 155 | } |
149 | 156 | ||
@@ -160,10 +167,11 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe) | |||
160 | new = allocate_probes(nr_probes - nr_del + 1); | 167 | new = allocate_probes(nr_probes - nr_del + 1); |
161 | if (new == NULL) | 168 | if (new == NULL) |
162 | return ERR_PTR(-ENOMEM); | 169 | return ERR_PTR(-ENOMEM); |
163 | for (i = 0; old[i]; i++) | 170 | for (i = 0; old[i].func; i++) |
164 | if ((probe && old[i] != probe)) | 171 | if (probe && |
172 | (old[i].func != probe || old[i].data != data)) | ||
165 | new[j++] = old[i]; | 173 | new[j++] = old[i]; |
166 | new[nr_probes - nr_del] = NULL; | 174 | new[nr_probes - nr_del].func = NULL; |
167 | entry->refcount = nr_probes - nr_del; | 175 | entry->refcount = nr_probes - nr_del; |
168 | entry->funcs = new; | 176 | entry->funcs = new; |
169 | } | 177 | } |
@@ -256,7 +264,13 @@ static void set_tracepoint(struct tracepoint_entry **entry, | |||
256 | * is used. | 264 | * is used. |
257 | */ | 265 | */ |
258 | rcu_assign_pointer(elem->funcs, (*entry)->funcs); | 266 | rcu_assign_pointer(elem->funcs, (*entry)->funcs); |
259 | elem->state = active; | 267 | if (!elem->state && active) { |
268 | jump_label_enable(&elem->state); | ||
269 | elem->state = active; | ||
270 | } else if (elem->state && !active) { | ||
271 | jump_label_disable(&elem->state); | ||
272 | elem->state = active; | ||
273 | } | ||
260 | } | 274 | } |
261 | 275 | ||
262 | /* | 276 | /* |
@@ -270,7 +284,10 @@ static void disable_tracepoint(struct tracepoint *elem) | |||
270 | if (elem->unregfunc && elem->state) | 284 | if (elem->unregfunc && elem->state) |
271 | elem->unregfunc(); | 285 | elem->unregfunc(); |
272 | 286 | ||
273 | elem->state = 0; | 287 | if (elem->state) { |
288 | jump_label_disable(&elem->state); | ||
289 | elem->state = 0; | ||
290 | } | ||
274 | rcu_assign_pointer(elem->funcs, NULL); | 291 | rcu_assign_pointer(elem->funcs, NULL); |
275 | } | 292 | } |
276 | 293 | ||
@@ -315,18 +332,19 @@ static void tracepoint_update_probes(void) | |||
315 | module_update_tracepoints(); | 332 | module_update_tracepoints(); |
316 | } | 333 | } |
317 | 334 | ||
318 | static void *tracepoint_add_probe(const char *name, void *probe) | 335 | static struct tracepoint_func * |
336 | tracepoint_add_probe(const char *name, void *probe, void *data) | ||
319 | { | 337 | { |
320 | struct tracepoint_entry *entry; | 338 | struct tracepoint_entry *entry; |
321 | void *old; | 339 | struct tracepoint_func *old; |
322 | 340 | ||
323 | entry = get_tracepoint(name); | 341 | entry = get_tracepoint(name); |
324 | if (!entry) { | 342 | if (!entry) { |
325 | entry = add_tracepoint(name); | 343 | entry = add_tracepoint(name); |
326 | if (IS_ERR(entry)) | 344 | if (IS_ERR(entry)) |
327 | return entry; | 345 | return (struct tracepoint_func *)entry; |
328 | } | 346 | } |
329 | old = tracepoint_entry_add_probe(entry, probe); | 347 | old = tracepoint_entry_add_probe(entry, probe, data); |
330 | if (IS_ERR(old) && !entry->refcount) | 348 | if (IS_ERR(old) && !entry->refcount) |
331 | remove_tracepoint(entry); | 349 | remove_tracepoint(entry); |
332 | return old; | 350 | return old; |
@@ -340,12 +358,12 @@ static void *tracepoint_add_probe(const char *name, void *probe) | |||
340 | * Returns 0 if ok, error value on error. | 358 | * Returns 0 if ok, error value on error. |
341 | * The probe address must at least be aligned on the architecture pointer size. | 359 | * The probe address must at least be aligned on the architecture pointer size. |
342 | */ | 360 | */ |
343 | int tracepoint_probe_register(const char *name, void *probe) | 361 | int tracepoint_probe_register(const char *name, void *probe, void *data) |
344 | { | 362 | { |
345 | void *old; | 363 | struct tracepoint_func *old; |
346 | 364 | ||
347 | mutex_lock(&tracepoints_mutex); | 365 | mutex_lock(&tracepoints_mutex); |
348 | old = tracepoint_add_probe(name, probe); | 366 | old = tracepoint_add_probe(name, probe, data); |
349 | mutex_unlock(&tracepoints_mutex); | 367 | mutex_unlock(&tracepoints_mutex); |
350 | if (IS_ERR(old)) | 368 | if (IS_ERR(old)) |
351 | return PTR_ERR(old); | 369 | return PTR_ERR(old); |
@@ -356,15 +374,16 @@ int tracepoint_probe_register(const char *name, void *probe) | |||
356 | } | 374 | } |
357 | EXPORT_SYMBOL_GPL(tracepoint_probe_register); | 375 | EXPORT_SYMBOL_GPL(tracepoint_probe_register); |
358 | 376 | ||
359 | static void *tracepoint_remove_probe(const char *name, void *probe) | 377 | static struct tracepoint_func * |
378 | tracepoint_remove_probe(const char *name, void *probe, void *data) | ||
360 | { | 379 | { |
361 | struct tracepoint_entry *entry; | 380 | struct tracepoint_entry *entry; |
362 | void *old; | 381 | struct tracepoint_func *old; |
363 | 382 | ||
364 | entry = get_tracepoint(name); | 383 | entry = get_tracepoint(name); |
365 | if (!entry) | 384 | if (!entry) |
366 | return ERR_PTR(-ENOENT); | 385 | return ERR_PTR(-ENOENT); |
367 | old = tracepoint_entry_remove_probe(entry, probe); | 386 | old = tracepoint_entry_remove_probe(entry, probe, data); |
368 | if (IS_ERR(old)) | 387 | if (IS_ERR(old)) |
369 | return old; | 388 | return old; |
370 | if (!entry->refcount) | 389 | if (!entry->refcount) |
@@ -382,12 +401,12 @@ static void *tracepoint_remove_probe(const char *name, void *probe) | |||
382 | * itself uses stop_machine(), which insures that every preempt disabled section | 401 | * itself uses stop_machine(), which insures that every preempt disabled section |
383 | * have finished. | 402 | * have finished. |
384 | */ | 403 | */ |
385 | int tracepoint_probe_unregister(const char *name, void *probe) | 404 | int tracepoint_probe_unregister(const char *name, void *probe, void *data) |
386 | { | 405 | { |
387 | void *old; | 406 | struct tracepoint_func *old; |
388 | 407 | ||
389 | mutex_lock(&tracepoints_mutex); | 408 | mutex_lock(&tracepoints_mutex); |
390 | old = tracepoint_remove_probe(name, probe); | 409 | old = tracepoint_remove_probe(name, probe, data); |
391 | mutex_unlock(&tracepoints_mutex); | 410 | mutex_unlock(&tracepoints_mutex); |
392 | if (IS_ERR(old)) | 411 | if (IS_ERR(old)) |
393 | return PTR_ERR(old); | 412 | return PTR_ERR(old); |
@@ -418,12 +437,13 @@ static void tracepoint_add_old_probes(void *old) | |||
418 | * | 437 | * |
419 | * caller must call tracepoint_probe_update_all() | 438 | * caller must call tracepoint_probe_update_all() |
420 | */ | 439 | */ |
421 | int tracepoint_probe_register_noupdate(const char *name, void *probe) | 440 | int tracepoint_probe_register_noupdate(const char *name, void *probe, |
441 | void *data) | ||
422 | { | 442 | { |
423 | void *old; | 443 | struct tracepoint_func *old; |
424 | 444 | ||
425 | mutex_lock(&tracepoints_mutex); | 445 | mutex_lock(&tracepoints_mutex); |
426 | old = tracepoint_add_probe(name, probe); | 446 | old = tracepoint_add_probe(name, probe, data); |
427 | if (IS_ERR(old)) { | 447 | if (IS_ERR(old)) { |
428 | mutex_unlock(&tracepoints_mutex); | 448 | mutex_unlock(&tracepoints_mutex); |
429 | return PTR_ERR(old); | 449 | return PTR_ERR(old); |
@@ -441,12 +461,13 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_register_noupdate); | |||
441 | * | 461 | * |
442 | * caller must call tracepoint_probe_update_all() | 462 | * caller must call tracepoint_probe_update_all() |
443 | */ | 463 | */ |
444 | int tracepoint_probe_unregister_noupdate(const char *name, void *probe) | 464 | int tracepoint_probe_unregister_noupdate(const char *name, void *probe, |
465 | void *data) | ||
445 | { | 466 | { |
446 | void *old; | 467 | struct tracepoint_func *old; |
447 | 468 | ||
448 | mutex_lock(&tracepoints_mutex); | 469 | mutex_lock(&tracepoints_mutex); |
449 | old = tracepoint_remove_probe(name, probe); | 470 | old = tracepoint_remove_probe(name, probe, data); |
450 | if (IS_ERR(old)) { | 471 | if (IS_ERR(old)) { |
451 | mutex_unlock(&tracepoints_mutex); | 472 | mutex_unlock(&tracepoints_mutex); |
452 | return PTR_ERR(old); | 473 | return PTR_ERR(old); |