diff options
author | Oleg Nesterov <oleg@redhat.com> | 2012-11-24 11:29:40 -0500 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2013-02-08 11:47:03 -0500 |
commit | e591c8d78e49e6206935cf31c4d2b603bbb29166 (patch) | |
tree | fd454634604829933828e06849550dfbcfc37542 /kernel | |
parent | 9a98e03cc145c994da824dac7602334f50feb670 (diff) |
uprobes: Introduce uprobe->register_rwsem
Introduce uprobe->register_rwsem. It is taken for writing around
__uprobe_register/unregister.
Change handler_chain() to use this sem rather than consumer_rwsem.
The main reason for this change is that we have the nasty problem
with mmap_sem/consumer_rwsem dependency. filter_chain() needs to
protect uprobe->consumers like handler_chain(), but they can not
use the same lock. filter_chain() can be called under ->mmap_sem
(currently this is always true), but we want to allow ->handler()
to play with the probed task's memory, and this needs ->mmap_sem.
Alternatively we could use srcu, but synchronize_srcu() is very
slow and ->register_rwsem allows us to do more. In particular, we
can teach handler_chain() to do remove_breakpoint() if this bp is
"nacked" by all consumers, we know that we can't race with the
new consumer which does uprobe_register().
See also the next patches. uprobes_mutex[] is almost ready to die.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/events/uprobes.c | 10 |
1 files changed, 8 insertions, 2 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index d1d1394bca8b..61d0fa6b5012 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
@@ -91,6 +91,7 @@ static atomic_t uprobe_events = ATOMIC_INIT(0); | |||
91 | struct uprobe { | 91 | struct uprobe { |
92 | struct rb_node rb_node; /* node in the rb tree */ | 92 | struct rb_node rb_node; /* node in the rb tree */ |
93 | atomic_t ref; | 93 | atomic_t ref; |
94 | struct rw_semaphore register_rwsem; | ||
94 | struct rw_semaphore consumer_rwsem; | 95 | struct rw_semaphore consumer_rwsem; |
95 | struct mutex copy_mutex; /* TODO: kill me and UPROBE_COPY_INSN */ | 96 | struct mutex copy_mutex; /* TODO: kill me and UPROBE_COPY_INSN */ |
96 | struct list_head pending_list; | 97 | struct list_head pending_list; |
@@ -449,6 +450,7 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) | |||
449 | 450 | ||
450 | uprobe->inode = igrab(inode); | 451 | uprobe->inode = igrab(inode); |
451 | uprobe->offset = offset; | 452 | uprobe->offset = offset; |
453 | init_rwsem(&uprobe->register_rwsem); | ||
452 | init_rwsem(&uprobe->consumer_rwsem); | 454 | init_rwsem(&uprobe->consumer_rwsem); |
453 | mutex_init(&uprobe->copy_mutex); | 455 | mutex_init(&uprobe->copy_mutex); |
454 | /* For now assume that the instruction need not be single-stepped */ | 456 | /* For now assume that the instruction need not be single-stepped */ |
@@ -476,10 +478,10 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) | |||
476 | if (!test_bit(UPROBE_RUN_HANDLER, &uprobe->flags)) | 478 | if (!test_bit(UPROBE_RUN_HANDLER, &uprobe->flags)) |
477 | return; | 479 | return; |
478 | 480 | ||
479 | down_read(&uprobe->consumer_rwsem); | 481 | down_read(&uprobe->register_rwsem); |
480 | for (uc = uprobe->consumers; uc; uc = uc->next) | 482 | for (uc = uprobe->consumers; uc; uc = uc->next) |
481 | uc->handler(uc, regs); | 483 | uc->handler(uc, regs); |
482 | up_read(&uprobe->consumer_rwsem); | 484 | up_read(&uprobe->register_rwsem); |
483 | } | 485 | } |
484 | 486 | ||
485 | static void consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc) | 487 | static void consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc) |
@@ -873,9 +875,11 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer * | |||
873 | mutex_lock(uprobes_hash(inode)); | 875 | mutex_lock(uprobes_hash(inode)); |
874 | uprobe = alloc_uprobe(inode, offset); | 876 | uprobe = alloc_uprobe(inode, offset); |
875 | if (uprobe) { | 877 | if (uprobe) { |
878 | down_write(&uprobe->register_rwsem); | ||
876 | ret = __uprobe_register(uprobe, uc); | 879 | ret = __uprobe_register(uprobe, uc); |
877 | if (ret) | 880 | if (ret) |
878 | __uprobe_unregister(uprobe, uc); | 881 | __uprobe_unregister(uprobe, uc); |
882 | up_write(&uprobe->register_rwsem); | ||
879 | } | 883 | } |
880 | mutex_unlock(uprobes_hash(inode)); | 884 | mutex_unlock(uprobes_hash(inode)); |
881 | if (uprobe) | 885 | if (uprobe) |
@@ -899,7 +903,9 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume | |||
899 | return; | 903 | return; |
900 | 904 | ||
901 | mutex_lock(uprobes_hash(inode)); | 905 | mutex_lock(uprobes_hash(inode)); |
906 | down_write(&uprobe->register_rwsem); | ||
902 | __uprobe_unregister(uprobe, uc); | 907 | __uprobe_unregister(uprobe, uc); |
908 | up_write(&uprobe->register_rwsem); | ||
903 | mutex_unlock(uprobes_hash(inode)); | 909 | mutex_unlock(uprobes_hash(inode)); |
904 | put_uprobe(uprobe); | 910 | put_uprobe(uprobe); |
905 | } | 911 | } |