diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2009-01-06 17:41:50 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-06 18:59:20 -0500 |
commit | 129415607845d4daea11ddcba706005c69dcb942 (patch) | |
tree | 9046ea79a0b81c8823b9d42f00fd7c158861ed5c | |
parent | a06f6211ef9b1785922f9d0e8766d63ac4e66de1 (diff) |
kprobes: add kprobe_insn_mutex and cleanup arch_remove_kprobe()
Add kprobe_insn_mutex for protecting kprobe_insn_pages hlist, and remove
kprobe_mutex from architecture dependent code.
This allows us to call arch_remove_kprobe() (and free_insn_slot) while
holding kprobe_mutex.
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Acked-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | arch/arm/kernel/kprobes.c | 2 | ||||
-rw-r--r-- | arch/ia64/kernel/kprobes.c | 8 | ||||
-rw-r--r-- | arch/powerpc/kernel/kprobes.c | 7 | ||||
-rw-r--r-- | arch/s390/kernel/kprobes.c | 7 | ||||
-rw-r--r-- | arch/x86/kernel/kprobes.c | 7 | ||||
-rw-r--r-- | include/linux/kprobes.h | 1 | ||||
-rw-r--r-- | kernel/kprobes.c | 25 |
7 files changed, 38 insertions, 19 deletions
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c index 3f9abe0e9aff..f692efddd449 100644 --- a/arch/arm/kernel/kprobes.c +++ b/arch/arm/kernel/kprobes.c | |||
@@ -92,9 +92,7 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) | |||
92 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 92 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
93 | { | 93 | { |
94 | if (p->ainsn.insn) { | 94 | if (p->ainsn.insn) { |
95 | mutex_lock(&kprobe_mutex); | ||
96 | free_insn_slot(p->ainsn.insn, 0); | 95 | free_insn_slot(p->ainsn.insn, 0); |
97 | mutex_unlock(&kprobe_mutex); | ||
98 | p->ainsn.insn = NULL; | 96 | p->ainsn.insn = NULL; |
99 | } | 97 | } |
100 | } | 98 | } |
diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index f07688da947c..097b84d54e73 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c | |||
@@ -670,9 +670,11 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) | |||
670 | 670 | ||
671 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 671 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
672 | { | 672 | { |
673 | mutex_lock(&kprobe_mutex); | 673 | if (p->ainsn.insn) { |
674 | free_insn_slot(p->ainsn.insn, p->ainsn.inst_flag & INST_FLAG_BOOSTABLE); | 674 | free_insn_slot(p->ainsn.insn, |
675 | mutex_unlock(&kprobe_mutex); | 675 | p->ainsn.inst_flag & INST_FLAG_BOOSTABLE); |
676 | p->ainsn.insn = NULL; | ||
677 | } | ||
676 | } | 678 | } |
677 | /* | 679 | /* |
678 | * We are resuming execution after a single step fault, so the pt_regs | 680 | * We are resuming execution after a single step fault, so the pt_regs |
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index de79915452c8..989edcdf0297 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c | |||
@@ -96,9 +96,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) | |||
96 | 96 | ||
97 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 97 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
98 | { | 98 | { |
99 | mutex_lock(&kprobe_mutex); | 99 | if (p->ainsn.insn) { |
100 | free_insn_slot(p->ainsn.insn, 0); | 100 | free_insn_slot(p->ainsn.insn, 0); |
101 | mutex_unlock(&kprobe_mutex); | 101 | p->ainsn.insn = NULL; |
102 | } | ||
102 | } | 103 | } |
103 | 104 | ||
104 | static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 105 | static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) |
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 569079ec4ff0..9b92856632cf 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
@@ -218,9 +218,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) | |||
218 | 218 | ||
219 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 219 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
220 | { | 220 | { |
221 | mutex_lock(&kprobe_mutex); | 221 | if (p->ainsn.insn) { |
222 | free_insn_slot(p->ainsn.insn, 0); | 222 | free_insn_slot(p->ainsn.insn, 0); |
223 | mutex_unlock(&kprobe_mutex); | 223 | p->ainsn.insn = NULL; |
224 | } | ||
224 | } | 225 | } |
225 | 226 | ||
226 | static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 227 | static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) |
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 6c27679ec6aa..eead6f8f9218 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c | |||
@@ -376,9 +376,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) | |||
376 | 376 | ||
377 | void __kprobes arch_remove_kprobe(struct kprobe *p) | 377 | void __kprobes arch_remove_kprobe(struct kprobe *p) |
378 | { | 378 | { |
379 | mutex_lock(&kprobe_mutex); | 379 | if (p->ainsn.insn) { |
380 | free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1)); | 380 | free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1)); |
381 | mutex_unlock(&kprobe_mutex); | 381 | p->ainsn.insn = NULL; |
382 | } | ||
382 | } | 383 | } |
383 | 384 | ||
384 | static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) | 385 | static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) |
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 497b1d1f7a05..b93e44ce2284 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h | |||
@@ -201,7 +201,6 @@ static inline int init_test_probes(void) | |||
201 | } | 201 | } |
202 | #endif /* CONFIG_KPROBES_SANITY_TEST */ | 202 | #endif /* CONFIG_KPROBES_SANITY_TEST */ |
203 | 203 | ||
204 | extern struct mutex kprobe_mutex; | ||
205 | extern int arch_prepare_kprobe(struct kprobe *p); | 204 | extern int arch_prepare_kprobe(struct kprobe *p); |
206 | extern void arch_arm_kprobe(struct kprobe *p); | 205 | extern void arch_arm_kprobe(struct kprobe *p); |
207 | extern void arch_disarm_kprobe(struct kprobe *p); | 206 | extern void arch_disarm_kprobe(struct kprobe *p); |
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 3afd354c46f1..29e87921437d 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -69,7 +69,7 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; | |||
69 | /* NOTE: change this value only with kprobe_mutex held */ | 69 | /* NOTE: change this value only with kprobe_mutex held */ |
70 | static bool kprobe_enabled; | 70 | static bool kprobe_enabled; |
71 | 71 | ||
72 | DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ | 72 | static DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ |
73 | static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; | 73 | static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; |
74 | static struct { | 74 | static struct { |
75 | spinlock_t lock ____cacheline_aligned_in_smp; | 75 | spinlock_t lock ____cacheline_aligned_in_smp; |
@@ -115,6 +115,7 @@ enum kprobe_slot_state { | |||
115 | SLOT_USED = 2, | 115 | SLOT_USED = 2, |
116 | }; | 116 | }; |
117 | 117 | ||
118 | static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_pages */ | ||
118 | static struct hlist_head kprobe_insn_pages; | 119 | static struct hlist_head kprobe_insn_pages; |
119 | static int kprobe_garbage_slots; | 120 | static int kprobe_garbage_slots; |
120 | static int collect_garbage_slots(void); | 121 | static int collect_garbage_slots(void); |
@@ -144,10 +145,10 @@ loop_end: | |||
144 | } | 145 | } |
145 | 146 | ||
146 | /** | 147 | /** |
147 | * get_insn_slot() - Find a slot on an executable page for an instruction. | 148 | * __get_insn_slot() - Find a slot on an executable page for an instruction. |
148 | * We allocate an executable page if there's no room on existing ones. | 149 | * We allocate an executable page if there's no room on existing ones. |
149 | */ | 150 | */ |
150 | kprobe_opcode_t __kprobes *get_insn_slot(void) | 151 | static kprobe_opcode_t __kprobes *__get_insn_slot(void) |
151 | { | 152 | { |
152 | struct kprobe_insn_page *kip; | 153 | struct kprobe_insn_page *kip; |
153 | struct hlist_node *pos; | 154 | struct hlist_node *pos; |
@@ -196,6 +197,15 @@ kprobe_opcode_t __kprobes *get_insn_slot(void) | |||
196 | return kip->insns; | 197 | return kip->insns; |
197 | } | 198 | } |
198 | 199 | ||
200 | kprobe_opcode_t __kprobes *get_insn_slot(void) | ||
201 | { | ||
202 | kprobe_opcode_t *ret; | ||
203 | mutex_lock(&kprobe_insn_mutex); | ||
204 | ret = __get_insn_slot(); | ||
205 | mutex_unlock(&kprobe_insn_mutex); | ||
206 | return ret; | ||
207 | } | ||
208 | |||
199 | /* Return 1 if all garbages are collected, otherwise 0. */ | 209 | /* Return 1 if all garbages are collected, otherwise 0. */ |
200 | static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx) | 210 | static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx) |
201 | { | 211 | { |
@@ -226,9 +236,13 @@ static int __kprobes collect_garbage_slots(void) | |||
226 | { | 236 | { |
227 | struct kprobe_insn_page *kip; | 237 | struct kprobe_insn_page *kip; |
228 | struct hlist_node *pos, *next; | 238 | struct hlist_node *pos, *next; |
239 | int safety; | ||
229 | 240 | ||
230 | /* Ensure no-one is preepmted on the garbages */ | 241 | /* Ensure no-one is preepmted on the garbages */ |
231 | if (check_safety() != 0) | 242 | mutex_unlock(&kprobe_insn_mutex); |
243 | safety = check_safety(); | ||
244 | mutex_lock(&kprobe_insn_mutex); | ||
245 | if (safety != 0) | ||
232 | return -EAGAIN; | 246 | return -EAGAIN; |
233 | 247 | ||
234 | hlist_for_each_entry_safe(kip, pos, next, &kprobe_insn_pages, hlist) { | 248 | hlist_for_each_entry_safe(kip, pos, next, &kprobe_insn_pages, hlist) { |
@@ -251,6 +265,7 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty) | |||
251 | struct kprobe_insn_page *kip; | 265 | struct kprobe_insn_page *kip; |
252 | struct hlist_node *pos; | 266 | struct hlist_node *pos; |
253 | 267 | ||
268 | mutex_lock(&kprobe_insn_mutex); | ||
254 | hlist_for_each_entry(kip, pos, &kprobe_insn_pages, hlist) { | 269 | hlist_for_each_entry(kip, pos, &kprobe_insn_pages, hlist) { |
255 | if (kip->insns <= slot && | 270 | if (kip->insns <= slot && |
256 | slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) { | 271 | slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) { |
@@ -267,6 +282,8 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty) | |||
267 | 282 | ||
268 | if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE) | 283 | if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE) |
269 | collect_garbage_slots(); | 284 | collect_garbage_slots(); |
285 | |||
286 | mutex_unlock(&kprobe_insn_mutex); | ||
270 | } | 287 | } |
271 | #endif | 288 | #endif |
272 | 289 | ||