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); |
