diff options
author | Ananth N Mavinakayanahalli <ananth@in.ibm.com> | 2005-11-07 04:00:10 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-07 10:53:45 -0500 |
commit | 0dc036c91ac11b2b76bb91b59d8c7af919aa4a8d (patch) | |
tree | 88318deadc78c7dd416b60f2fac4b8eef1f61a28 | |
parent | 8a5c4dc5e5d72b7802f5647082ccf3861a94f013 (diff) |
[PATCH] Kprobes: Track kprobe on a per_cpu basis - ppc64 changes
PPC64 changes to track kprobe execution on a per-cpu basis. We now track the
kprobe state machine independently on each cpu using an arch specific kprobe
control block.
Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Signed-off-by: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | arch/ppc64/kernel/kprobes.c | 94 | ||||
-rw-r--r-- | include/asm-powerpc/kprobes.h | 15 |
2 files changed, 69 insertions, 40 deletions
diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c index 6071ee99f5cb..3f89f3e5584a 100644 --- a/arch/ppc64/kernel/kprobes.c +++ b/arch/ppc64/kernel/kprobes.c | |||
@@ -37,12 +37,8 @@ | |||
37 | #include <asm/sstep.h> | 37 | #include <asm/sstep.h> |
38 | 38 | ||
39 | static DECLARE_MUTEX(kprobe_mutex); | 39 | static DECLARE_MUTEX(kprobe_mutex); |
40 | 40 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; | |
41 | static struct kprobe *current_kprobe; | 41 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
42 | static unsigned long kprobe_status, kprobe_saved_msr; | ||
43 | static struct kprobe *kprobe_prev; | ||
44 | static unsigned long kprobe_status_prev, kprobe_saved_msr_prev; | ||
45 | static struct pt_regs jprobe_saved_regs; | ||
46 | 42 | ||
47 | int __kprobes arch_prepare_kprobe(struct kprobe *p) | 43 | int __kprobes arch_prepare_kprobe(struct kprobe *p) |
48 | { | 44 | { |
@@ -108,18 +104,25 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | |||
108 | regs->nip = (unsigned long)p->ainsn.insn; | 104 | regs->nip = (unsigned long)p->ainsn.insn; |
109 | } | 105 | } |
110 | 106 | ||
111 | static inline void save_previous_kprobe(void) | 107 | static inline void save_previous_kprobe(struct kprobe_ctlblk *kcb) |
108 | { | ||
109 | kcb->prev_kprobe.kp = kprobe_running(); | ||
110 | kcb->prev_kprobe.status = kcb->kprobe_status; | ||
111 | kcb->prev_kprobe.saved_msr = kcb->kprobe_saved_msr; | ||
112 | } | ||
113 | |||
114 | static inline void restore_previous_kprobe(struct kprobe_ctlblk *kcb) | ||
112 | { | 115 | { |
113 | kprobe_prev = current_kprobe; | 116 | __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; |
114 | kprobe_status_prev = kprobe_status; | 117 | kcb->kprobe_status = kcb->prev_kprobe.status; |
115 | kprobe_saved_msr_prev = kprobe_saved_msr; | 118 | kcb->kprobe_saved_msr = kcb->prev_kprobe.saved_msr; |
116 | } | 119 | } |
117 | 120 | ||
118 | static inline void restore_previous_kprobe(void) | 121 | static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs, |
122 | struct kprobe_ctlblk *kcb) | ||
119 | { | 123 | { |
120 | current_kprobe = kprobe_prev; | 124 | __get_cpu_var(current_kprobe) = p; |
121 | kprobe_status = kprobe_status_prev; | 125 | kcb->kprobe_saved_msr = regs->msr; |
122 | kprobe_saved_msr = kprobe_saved_msr_prev; | ||
123 | } | 126 | } |
124 | 127 | ||
125 | void __kprobes arch_prepare_kretprobe(struct kretprobe *rp, | 128 | void __kprobes arch_prepare_kretprobe(struct kretprobe *rp, |
@@ -145,6 +148,7 @@ static inline int kprobe_handler(struct pt_regs *regs) | |||
145 | struct kprobe *p; | 148 | struct kprobe *p; |
146 | int ret = 0; | 149 | int ret = 0; |
147 | unsigned int *addr = (unsigned int *)regs->nip; | 150 | unsigned int *addr = (unsigned int *)regs->nip; |
151 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | ||
148 | 152 | ||
149 | /* Check we're not actually recursing */ | 153 | /* Check we're not actually recursing */ |
150 | if (kprobe_running()) { | 154 | if (kprobe_running()) { |
@@ -153,10 +157,10 @@ static inline int kprobe_handler(struct pt_regs *regs) | |||
153 | p = get_kprobe(addr); | 157 | p = get_kprobe(addr); |
154 | if (p) { | 158 | if (p) { |
155 | kprobe_opcode_t insn = *p->ainsn.insn; | 159 | kprobe_opcode_t insn = *p->ainsn.insn; |
156 | if (kprobe_status == KPROBE_HIT_SS && | 160 | if (kcb->kprobe_status == KPROBE_HIT_SS && |
157 | is_trap(insn)) { | 161 | is_trap(insn)) { |
158 | regs->msr &= ~MSR_SE; | 162 | regs->msr &= ~MSR_SE; |
159 | regs->msr |= kprobe_saved_msr; | 163 | regs->msr |= kcb->kprobe_saved_msr; |
160 | unlock_kprobes(); | 164 | unlock_kprobes(); |
161 | goto no_kprobe; | 165 | goto no_kprobe; |
162 | } | 166 | } |
@@ -166,15 +170,15 @@ static inline int kprobe_handler(struct pt_regs *regs) | |||
166 | * just single step on the instruction of the new probe | 170 | * just single step on the instruction of the new probe |
167 | * without calling any user handlers. | 171 | * without calling any user handlers. |
168 | */ | 172 | */ |
169 | save_previous_kprobe(); | 173 | save_previous_kprobe(kcb); |
170 | current_kprobe = p; | 174 | set_current_kprobe(p, regs, kcb); |
171 | kprobe_saved_msr = regs->msr; | 175 | kcb->kprobe_saved_msr = regs->msr; |
172 | p->nmissed++; | 176 | p->nmissed++; |
173 | prepare_singlestep(p, regs); | 177 | prepare_singlestep(p, regs); |
174 | kprobe_status = KPROBE_REENTER; | 178 | kcb->kprobe_status = KPROBE_REENTER; |
175 | return 1; | 179 | return 1; |
176 | } else { | 180 | } else { |
177 | p = current_kprobe; | 181 | p = __get_cpu_var(current_kprobe); |
178 | if (p->break_handler && p->break_handler(p, regs)) { | 182 | if (p->break_handler && p->break_handler(p, regs)) { |
179 | goto ss_probe; | 183 | goto ss_probe; |
180 | } | 184 | } |
@@ -214,16 +218,15 @@ static inline int kprobe_handler(struct pt_regs *regs) | |||
214 | * in post_kprobe_handler(). | 218 | * in post_kprobe_handler(). |
215 | */ | 219 | */ |
216 | preempt_disable(); | 220 | preempt_disable(); |
217 | kprobe_status = KPROBE_HIT_ACTIVE; | 221 | kcb->kprobe_status = KPROBE_HIT_ACTIVE; |
218 | current_kprobe = p; | 222 | set_current_kprobe(p, regs, kcb); |
219 | kprobe_saved_msr = regs->msr; | ||
220 | if (p->pre_handler && p->pre_handler(p, regs)) | 223 | if (p->pre_handler && p->pre_handler(p, regs)) |
221 | /* handler has already set things up, so skip ss setup */ | 224 | /* handler has already set things up, so skip ss setup */ |
222 | return 1; | 225 | return 1; |
223 | 226 | ||
224 | ss_probe: | 227 | ss_probe: |
225 | prepare_singlestep(p, regs); | 228 | prepare_singlestep(p, regs); |
226 | kprobe_status = KPROBE_HIT_SS; | 229 | kcb->kprobe_status = KPROBE_HIT_SS; |
227 | return 1; | 230 | return 1; |
228 | 231 | ||
229 | no_kprobe: | 232 | no_kprobe: |
@@ -292,6 +295,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | |||
292 | BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); | 295 | BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); |
293 | regs->nip = orig_ret_address; | 296 | regs->nip = orig_ret_address; |
294 | 297 | ||
298 | reset_current_kprobe(); | ||
295 | unlock_kprobes(); | 299 | unlock_kprobes(); |
296 | preempt_enable_no_resched(); | 300 | preempt_enable_no_resched(); |
297 | 301 | ||
@@ -324,22 +328,26 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) | |||
324 | 328 | ||
325 | static inline int post_kprobe_handler(struct pt_regs *regs) | 329 | static inline int post_kprobe_handler(struct pt_regs *regs) |
326 | { | 330 | { |
327 | if (!kprobe_running()) | 331 | struct kprobe *cur = kprobe_running(); |
332 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | ||
333 | |||
334 | if (!cur) | ||
328 | return 0; | 335 | return 0; |
329 | 336 | ||
330 | if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) { | 337 | if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) { |
331 | kprobe_status = KPROBE_HIT_SSDONE; | 338 | kcb->kprobe_status = KPROBE_HIT_SSDONE; |
332 | current_kprobe->post_handler(current_kprobe, regs, 0); | 339 | cur->post_handler(cur, regs, 0); |
333 | } | 340 | } |
334 | 341 | ||
335 | resume_execution(current_kprobe, regs); | 342 | resume_execution(cur, regs); |
336 | regs->msr |= kprobe_saved_msr; | 343 | regs->msr |= kcb->kprobe_saved_msr; |
337 | 344 | ||
338 | /*Restore back the original saved kprobes variables and continue. */ | 345 | /*Restore back the original saved kprobes variables and continue. */ |
339 | if (kprobe_status == KPROBE_REENTER) { | 346 | if (kcb->kprobe_status == KPROBE_REENTER) { |
340 | restore_previous_kprobe(); | 347 | restore_previous_kprobe(kcb); |
341 | goto out; | 348 | goto out; |
342 | } | 349 | } |
350 | reset_current_kprobe(); | ||
343 | unlock_kprobes(); | 351 | unlock_kprobes(); |
344 | out: | 352 | out: |
345 | preempt_enable_no_resched(); | 353 | preempt_enable_no_resched(); |
@@ -358,15 +366,18 @@ out: | |||
358 | /* Interrupts disabled, kprobe_lock held. */ | 366 | /* Interrupts disabled, kprobe_lock held. */ |
359 | static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) | 367 | static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) |
360 | { | 368 | { |
361 | if (current_kprobe->fault_handler | 369 | struct kprobe *cur = kprobe_running(); |
362 | && current_kprobe->fault_handler(current_kprobe, regs, trapnr)) | 370 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
371 | |||
372 | if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) | ||
363 | return 1; | 373 | return 1; |
364 | 374 | ||
365 | if (kprobe_status & KPROBE_HIT_SS) { | 375 | if (kcb->kprobe_status & KPROBE_HIT_SS) { |
366 | resume_execution(current_kprobe, regs); | 376 | resume_execution(cur, regs); |
367 | regs->msr &= ~MSR_SE; | 377 | regs->msr &= ~MSR_SE; |
368 | regs->msr |= kprobe_saved_msr; | 378 | regs->msr |= kcb->kprobe_saved_msr; |
369 | 379 | ||
380 | reset_current_kprobe(); | ||
370 | unlock_kprobes(); | 381 | unlock_kprobes(); |
371 | preempt_enable_no_resched(); | 382 | preempt_enable_no_resched(); |
372 | } | 383 | } |
@@ -411,8 +422,9 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, | |||
411 | int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) | 422 | int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) |
412 | { | 423 | { |
413 | struct jprobe *jp = container_of(p, struct jprobe, kp); | 424 | struct jprobe *jp = container_of(p, struct jprobe, kp); |
425 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | ||
414 | 426 | ||
415 | memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs)); | 427 | memcpy(&kcb->jprobe_saved_regs, regs, sizeof(struct pt_regs)); |
416 | 428 | ||
417 | /* setup return addr to the jprobe handler routine */ | 429 | /* setup return addr to the jprobe handler routine */ |
418 | regs->nip = (unsigned long)(((func_descr_t *)jp->entry)->entry); | 430 | regs->nip = (unsigned long)(((func_descr_t *)jp->entry)->entry); |
@@ -432,12 +444,14 @@ void __kprobes jprobe_return_end(void) | |||
432 | 444 | ||
433 | int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | 445 | int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) |
434 | { | 446 | { |
447 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | ||
448 | |||
435 | /* | 449 | /* |
436 | * FIXME - we should ideally be validating that we got here 'cos | 450 | * FIXME - we should ideally be validating that we got here 'cos |
437 | * of the "trap" in jprobe_return() above, before restoring the | 451 | * of the "trap" in jprobe_return() above, before restoring the |
438 | * saved regs... | 452 | * saved regs... |
439 | */ | 453 | */ |
440 | memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); | 454 | memcpy(regs, &kcb->jprobe_saved_regs, sizeof(struct pt_regs)); |
441 | return 1; | 455 | return 1; |
442 | } | 456 | } |
443 | 457 | ||
diff --git a/include/asm-powerpc/kprobes.h b/include/asm-powerpc/kprobes.h index b2f09f17fbe0..6cd0a3bfa280 100644 --- a/include/asm-powerpc/kprobes.h +++ b/include/asm-powerpc/kprobes.h | |||
@@ -27,6 +27,7 @@ | |||
27 | */ | 27 | */ |
28 | #include <linux/types.h> | 28 | #include <linux/types.h> |
29 | #include <linux/ptrace.h> | 29 | #include <linux/ptrace.h> |
30 | #include <linux/percpu.h> | ||
30 | 31 | ||
31 | struct pt_regs; | 32 | struct pt_regs; |
32 | 33 | ||
@@ -53,6 +54,20 @@ struct arch_specific_insn { | |||
53 | kprobe_opcode_t *insn; | 54 | kprobe_opcode_t *insn; |
54 | }; | 55 | }; |
55 | 56 | ||
57 | struct prev_kprobe { | ||
58 | struct kprobe *kp; | ||
59 | unsigned long status; | ||
60 | unsigned long saved_msr; | ||
61 | }; | ||
62 | |||
63 | /* per-cpu kprobe control block */ | ||
64 | struct kprobe_ctlblk { | ||
65 | unsigned long kprobe_status; | ||
66 | unsigned long kprobe_saved_msr; | ||
67 | struct pt_regs jprobe_saved_regs; | ||
68 | struct prev_kprobe prev_kprobe; | ||
69 | }; | ||
70 | |||
56 | #ifdef CONFIG_KPROBES | 71 | #ifdef CONFIG_KPROBES |
57 | extern int kprobe_exceptions_notify(struct notifier_block *self, | 72 | extern int kprobe_exceptions_notify(struct notifier_block *self, |
58 | unsigned long val, void *data); | 73 | unsigned long val, void *data); |