diff options
| author | Ingo Molnar <mingo@elte.hu> | 2008-08-14 06:19:59 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-08-14 06:19:59 -0400 |
| commit | 8d7ccaa545490cdffdfaff0842436a8dd85cf47b (patch) | |
| tree | 8129b5907161bc6ae26deb3645ce1e280c5e1f51 /kernel/kprobes.c | |
| parent | b2139aa0eec330c711c5a279db361e5ef1178e78 (diff) | |
| parent | 30a2f3c60a84092c8084dfe788b710f8d0768cd4 (diff) | |
Merge commit 'v2.6.27-rc3' into x86/prototypes
Conflicts:
include/asm-x86/dma-mapping.h
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/kprobes.c')
| -rw-r--r-- | kernel/kprobes.c | 132 |
1 files changed, 89 insertions, 43 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 1485ca8d0e00..75bc2cd9ebc6 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
| @@ -62,6 +62,7 @@ | |||
| 62 | addr = ((kprobe_opcode_t *)(kallsyms_lookup_name(name))) | 62 | addr = ((kprobe_opcode_t *)(kallsyms_lookup_name(name))) |
| 63 | #endif | 63 | #endif |
| 64 | 64 | ||
| 65 | static int kprobes_initialized; | ||
| 65 | static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; | 66 | static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; |
| 66 | static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; | 67 | static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; |
| 67 | 68 | ||
| @@ -69,8 +70,15 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; | |||
| 69 | static bool kprobe_enabled; | 70 | static bool kprobe_enabled; |
| 70 | 71 | ||
| 71 | DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ | 72 | DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ |
| 72 | DEFINE_SPINLOCK(kretprobe_lock); /* Protects kretprobe_inst_table */ | ||
| 73 | static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; | 73 | static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; |
| 74 | static struct { | ||
| 75 | spinlock_t lock ____cacheline_aligned; | ||
| 76 | } kretprobe_table_locks[KPROBE_TABLE_SIZE]; | ||
| 77 | |||
| 78 | static spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) | ||
| 79 | { | ||
| 80 | return &(kretprobe_table_locks[hash].lock); | ||
| 81 | } | ||
| 74 | 82 | ||
| 75 | /* | 83 | /* |
| 76 | * Normally, functions that we'd want to prohibit kprobes in, are marked | 84 | * Normally, functions that we'd want to prohibit kprobes in, are marked |
| @@ -368,26 +376,53 @@ void __kprobes kprobes_inc_nmissed_count(struct kprobe *p) | |||
| 368 | return; | 376 | return; |
| 369 | } | 377 | } |
| 370 | 378 | ||
| 371 | /* Called with kretprobe_lock held */ | ||
| 372 | void __kprobes recycle_rp_inst(struct kretprobe_instance *ri, | 379 | void __kprobes recycle_rp_inst(struct kretprobe_instance *ri, |
| 373 | struct hlist_head *head) | 380 | struct hlist_head *head) |
| 374 | { | 381 | { |
| 382 | struct kretprobe *rp = ri->rp; | ||
| 383 | |||
| 375 | /* remove rp inst off the rprobe_inst_table */ | 384 | /* remove rp inst off the rprobe_inst_table */ |
| 376 | hlist_del(&ri->hlist); | 385 | hlist_del(&ri->hlist); |
| 377 | if (ri->rp) { | 386 | INIT_HLIST_NODE(&ri->hlist); |
| 378 | /* remove rp inst off the used list */ | 387 | if (likely(rp)) { |
| 379 | hlist_del(&ri->uflist); | 388 | spin_lock(&rp->lock); |
| 380 | /* put rp inst back onto the free list */ | 389 | hlist_add_head(&ri->hlist, &rp->free_instances); |
| 381 | INIT_HLIST_NODE(&ri->uflist); | 390 | spin_unlock(&rp->lock); |
| 382 | hlist_add_head(&ri->uflist, &ri->rp->free_instances); | ||
| 383 | } else | 391 | } else |
| 384 | /* Unregistering */ | 392 | /* Unregistering */ |
| 385 | hlist_add_head(&ri->hlist, head); | 393 | hlist_add_head(&ri->hlist, head); |
| 386 | } | 394 | } |
| 387 | 395 | ||
| 388 | struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk) | 396 | void kretprobe_hash_lock(struct task_struct *tsk, |
| 397 | struct hlist_head **head, unsigned long *flags) | ||
| 389 | { | 398 | { |
| 390 | return &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]; | 399 | unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS); |
| 400 | spinlock_t *hlist_lock; | ||
| 401 | |||
| 402 | *head = &kretprobe_inst_table[hash]; | ||
| 403 | hlist_lock = kretprobe_table_lock_ptr(hash); | ||
| 404 | spin_lock_irqsave(hlist_lock, *flags); | ||
| 405 | } | ||
| 406 | |||
| 407 | void kretprobe_table_lock(unsigned long hash, unsigned long *flags) | ||
| 408 | { | ||
| 409 | spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash); | ||
| 410 | spin_lock_irqsave(hlist_lock, *flags); | ||
| 411 | } | ||
| 412 | |||
| 413 | void kretprobe_hash_unlock(struct task_struct *tsk, unsigned long *flags) | ||
| 414 | { | ||
| 415 | unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS); | ||
| 416 | spinlock_t *hlist_lock; | ||
| 417 | |||
| 418 | hlist_lock = kretprobe_table_lock_ptr(hash); | ||
| 419 | spin_unlock_irqrestore(hlist_lock, *flags); | ||
| 420 | } | ||
| 421 | |||
| 422 | void kretprobe_table_unlock(unsigned long hash, unsigned long *flags) | ||
| 423 | { | ||
| 424 | spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash); | ||
| 425 | spin_unlock_irqrestore(hlist_lock, *flags); | ||
| 391 | } | 426 | } |
| 392 | 427 | ||
| 393 | /* | 428 | /* |
| @@ -401,17 +436,21 @@ void __kprobes kprobe_flush_task(struct task_struct *tk) | |||
| 401 | struct kretprobe_instance *ri; | 436 | struct kretprobe_instance *ri; |
| 402 | struct hlist_head *head, empty_rp; | 437 | struct hlist_head *head, empty_rp; |
| 403 | struct hlist_node *node, *tmp; | 438 | struct hlist_node *node, *tmp; |
| 404 | unsigned long flags = 0; | 439 | unsigned long hash, flags = 0; |
| 405 | 440 | ||
| 406 | INIT_HLIST_HEAD(&empty_rp); | 441 | if (unlikely(!kprobes_initialized)) |
| 407 | spin_lock_irqsave(&kretprobe_lock, flags); | 442 | /* Early boot. kretprobe_table_locks not yet initialized. */ |
| 408 | head = kretprobe_inst_table_head(tk); | 443 | return; |
| 444 | |||
| 445 | hash = hash_ptr(tk, KPROBE_HASH_BITS); | ||
| 446 | head = &kretprobe_inst_table[hash]; | ||
| 447 | kretprobe_table_lock(hash, &flags); | ||
| 409 | hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { | 448 | hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { |
| 410 | if (ri->task == tk) | 449 | if (ri->task == tk) |
| 411 | recycle_rp_inst(ri, &empty_rp); | 450 | recycle_rp_inst(ri, &empty_rp); |
| 412 | } | 451 | } |
| 413 | spin_unlock_irqrestore(&kretprobe_lock, flags); | 452 | kretprobe_table_unlock(hash, &flags); |
| 414 | 453 | INIT_HLIST_HEAD(&empty_rp); | |
| 415 | hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { | 454 | hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { |
| 416 | hlist_del(&ri->hlist); | 455 | hlist_del(&ri->hlist); |
| 417 | kfree(ri); | 456 | kfree(ri); |
| @@ -423,24 +462,29 @@ static inline void free_rp_inst(struct kretprobe *rp) | |||
| 423 | struct kretprobe_instance *ri; | 462 | struct kretprobe_instance *ri; |
| 424 | struct hlist_node *pos, *next; | 463 | struct hlist_node *pos, *next; |
| 425 | 464 | ||
| 426 | hlist_for_each_entry_safe(ri, pos, next, &rp->free_instances, uflist) { | 465 | hlist_for_each_entry_safe(ri, pos, next, &rp->free_instances, hlist) { |
| 427 | hlist_del(&ri->uflist); | 466 | hlist_del(&ri->hlist); |
| 428 | kfree(ri); | 467 | kfree(ri); |
| 429 | } | 468 | } |
| 430 | } | 469 | } |
| 431 | 470 | ||
| 432 | static void __kprobes cleanup_rp_inst(struct kretprobe *rp) | 471 | static void __kprobes cleanup_rp_inst(struct kretprobe *rp) |
| 433 | { | 472 | { |
| 434 | unsigned long flags; | 473 | unsigned long flags, hash; |
| 435 | struct kretprobe_instance *ri; | 474 | struct kretprobe_instance *ri; |
| 436 | struct hlist_node *pos, *next; | 475 | struct hlist_node *pos, *next; |
| 476 | struct hlist_head *head; | ||
| 477 | |||
| 437 | /* No race here */ | 478 | /* No race here */ |
| 438 | spin_lock_irqsave(&kretprobe_lock, flags); | 479 | for (hash = 0; hash < KPROBE_TABLE_SIZE; hash++) { |
| 439 | hlist_for_each_entry_safe(ri, pos, next, &rp->used_instances, uflist) { | 480 | kretprobe_table_lock(hash, &flags); |
| 440 | ri->rp = NULL; | 481 | head = &kretprobe_inst_table[hash]; |
| 441 | hlist_del(&ri->uflist); | 482 | hlist_for_each_entry_safe(ri, pos, next, head, hlist) { |
| 483 | if (ri->rp == rp) | ||
| 484 | ri->rp = NULL; | ||
| 485 | } | ||
| 486 | kretprobe_table_unlock(hash, &flags); | ||
| 442 | } | 487 | } |
| 443 | spin_unlock_irqrestore(&kretprobe_lock, flags); | ||
| 444 | free_rp_inst(rp); | 488 | free_rp_inst(rp); |
| 445 | } | 489 | } |
| 446 | 490 | ||
| @@ -831,32 +875,37 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p, | |||
| 831 | struct pt_regs *regs) | 875 | struct pt_regs *regs) |
| 832 | { | 876 | { |
| 833 | struct kretprobe *rp = container_of(p, struct kretprobe, kp); | 877 | struct kretprobe *rp = container_of(p, struct kretprobe, kp); |
| 834 | unsigned long flags = 0; | 878 | unsigned long hash, flags = 0; |
| 879 | struct kretprobe_instance *ri; | ||
| 835 | 880 | ||
| 836 | /*TODO: consider to only swap the RA after the last pre_handler fired */ | 881 | /*TODO: consider to only swap the RA after the last pre_handler fired */ |
| 837 | spin_lock_irqsave(&kretprobe_lock, flags); | 882 | hash = hash_ptr(current, KPROBE_HASH_BITS); |
| 883 | spin_lock_irqsave(&rp->lock, flags); | ||
| 838 | if (!hlist_empty(&rp->free_instances)) { | 884 | if (!hlist_empty(&rp->free_instances)) { |
| 839 | struct kretprobe_instance *ri; | ||
| 840 | |||
| 841 | ri = hlist_entry(rp->free_instances.first, | 885 | ri = hlist_entry(rp->free_instances.first, |
| 842 | struct kretprobe_instance, uflist); | 886 | struct kretprobe_instance, hlist); |
| 887 | hlist_del(&ri->hlist); | ||
| 888 | spin_unlock_irqrestore(&rp->lock, flags); | ||
| 889 | |||
| 843 | ri->rp = rp; | 890 | ri->rp = rp; |
| 844 | ri->task = current; | 891 | ri->task = current; |
| 845 | 892 | ||
| 846 | if (rp->entry_handler && rp->entry_handler(ri, regs)) { | 893 | if (rp->entry_handler && rp->entry_handler(ri, regs)) { |
| 847 | spin_unlock_irqrestore(&kretprobe_lock, flags); | 894 | spin_unlock_irqrestore(&rp->lock, flags); |
| 848 | return 0; | 895 | return 0; |
| 849 | } | 896 | } |
| 850 | 897 | ||
| 851 | arch_prepare_kretprobe(ri, regs); | 898 | arch_prepare_kretprobe(ri, regs); |
| 852 | 899 | ||
| 853 | /* XXX(hch): why is there no hlist_move_head? */ | 900 | /* XXX(hch): why is there no hlist_move_head? */ |
| 854 | hlist_del(&ri->uflist); | 901 | INIT_HLIST_NODE(&ri->hlist); |
| 855 | hlist_add_head(&ri->uflist, &ri->rp->used_instances); | 902 | kretprobe_table_lock(hash, &flags); |
| 856 | hlist_add_head(&ri->hlist, kretprobe_inst_table_head(ri->task)); | 903 | hlist_add_head(&ri->hlist, &kretprobe_inst_table[hash]); |
| 857 | } else | 904 | kretprobe_table_unlock(hash, &flags); |
| 905 | } else { | ||
| 858 | rp->nmissed++; | 906 | rp->nmissed++; |
| 859 | spin_unlock_irqrestore(&kretprobe_lock, flags); | 907 | spin_unlock_irqrestore(&rp->lock, flags); |
| 908 | } | ||
| 860 | return 0; | 909 | return 0; |
| 861 | } | 910 | } |
| 862 | 911 | ||
| @@ -892,7 +941,7 @@ static int __kprobes __register_kretprobe(struct kretprobe *rp, | |||
| 892 | rp->maxactive = NR_CPUS; | 941 | rp->maxactive = NR_CPUS; |
| 893 | #endif | 942 | #endif |
| 894 | } | 943 | } |
| 895 | INIT_HLIST_HEAD(&rp->used_instances); | 944 | spin_lock_init(&rp->lock); |
| 896 | INIT_HLIST_HEAD(&rp->free_instances); | 945 | INIT_HLIST_HEAD(&rp->free_instances); |
| 897 | for (i = 0; i < rp->maxactive; i++) { | 946 | for (i = 0; i < rp->maxactive; i++) { |
| 898 | inst = kmalloc(sizeof(struct kretprobe_instance) + | 947 | inst = kmalloc(sizeof(struct kretprobe_instance) + |
| @@ -901,8 +950,8 @@ static int __kprobes __register_kretprobe(struct kretprobe *rp, | |||
| 901 | free_rp_inst(rp); | 950 | free_rp_inst(rp); |
| 902 | return -ENOMEM; | 951 | return -ENOMEM; |
| 903 | } | 952 | } |
| 904 | INIT_HLIST_NODE(&inst->uflist); | 953 | INIT_HLIST_NODE(&inst->hlist); |
| 905 | hlist_add_head(&inst->uflist, &rp->free_instances); | 954 | hlist_add_head(&inst->hlist, &rp->free_instances); |
| 906 | } | 955 | } |
| 907 | 956 | ||
| 908 | rp->nmissed = 0; | 957 | rp->nmissed = 0; |
| @@ -1009,6 +1058,7 @@ static int __init init_kprobes(void) | |||
| 1009 | for (i = 0; i < KPROBE_TABLE_SIZE; i++) { | 1058 | for (i = 0; i < KPROBE_TABLE_SIZE; i++) { |
| 1010 | INIT_HLIST_HEAD(&kprobe_table[i]); | 1059 | INIT_HLIST_HEAD(&kprobe_table[i]); |
| 1011 | INIT_HLIST_HEAD(&kretprobe_inst_table[i]); | 1060 | INIT_HLIST_HEAD(&kretprobe_inst_table[i]); |
| 1061 | spin_lock_init(&(kretprobe_table_locks[i].lock)); | ||
| 1012 | } | 1062 | } |
| 1013 | 1063 | ||
| 1014 | /* | 1064 | /* |
| @@ -1050,6 +1100,7 @@ static int __init init_kprobes(void) | |||
| 1050 | err = arch_init_kprobes(); | 1100 | err = arch_init_kprobes(); |
| 1051 | if (!err) | 1101 | if (!err) |
| 1052 | err = register_die_notifier(&kprobe_exceptions_nb); | 1102 | err = register_die_notifier(&kprobe_exceptions_nb); |
| 1103 | kprobes_initialized = (err == 0); | ||
| 1053 | 1104 | ||
| 1054 | if (!err) | 1105 | if (!err) |
| 1055 | init_test_probes(); | 1106 | init_test_probes(); |
| @@ -1286,13 +1337,8 @@ EXPORT_SYMBOL_GPL(register_jprobe); | |||
| 1286 | EXPORT_SYMBOL_GPL(unregister_jprobe); | 1337 | EXPORT_SYMBOL_GPL(unregister_jprobe); |
| 1287 | EXPORT_SYMBOL_GPL(register_jprobes); | 1338 | EXPORT_SYMBOL_GPL(register_jprobes); |
| 1288 | EXPORT_SYMBOL_GPL(unregister_jprobes); | 1339 | EXPORT_SYMBOL_GPL(unregister_jprobes); |
| 1289 | #ifdef CONFIG_KPROBES | ||
| 1290 | EXPORT_SYMBOL_GPL(jprobe_return); | 1340 | EXPORT_SYMBOL_GPL(jprobe_return); |
| 1291 | #endif | ||
| 1292 | |||
| 1293 | #ifdef CONFIG_KPROBES | ||
| 1294 | EXPORT_SYMBOL_GPL(register_kretprobe); | 1341 | EXPORT_SYMBOL_GPL(register_kretprobe); |
| 1295 | EXPORT_SYMBOL_GPL(unregister_kretprobe); | 1342 | EXPORT_SYMBOL_GPL(unregister_kretprobe); |
| 1296 | EXPORT_SYMBOL_GPL(register_kretprobes); | 1343 | EXPORT_SYMBOL_GPL(register_kretprobes); |
| 1297 | EXPORT_SYMBOL_GPL(unregister_kretprobes); | 1344 | EXPORT_SYMBOL_GPL(unregister_kretprobes); |
| 1298 | #endif | ||
