[PATCH] Return probe redesign: ppc64 specific implementation

The following is a patch provided by Ananth Mavinakayanahalli that implements
the new PPC64 specific parts of the new function return probe design.

NOTE: Since getting Ananth's patch, I changed trampoline_probe_handler()
to consume each of the outstanding return probem instances (feedback
on my original RFC after Ananth cut a patch), and also added the
arch_init() function (adding arch specific initialization.) I have
cross compiled but have not testing this on a PPC64 machine.

Changes include:
* Addition of kretprobe_trampoline to act as a dummy function for instrumented
functions to return to, and for the return probe infrastructure to place
a kprobe on on, gaining control so that the return probe handler
can be called, and so that the instruction pointer can be moved back
to the original return address.
* Addition of arch_init(), allowing a kprobe to be registered on
kretprobe_trampoline
* Addition of trampoline_probe_handler() which is used as the pre_handler
for the kprobe inserted on kretprobe_implementation. This is the function
that handles the details for calling the return probe handler function
and returning control back at the original return address
* Addition of arch_prepare_kretprobe() which is setup as the pre_handler
for a kprobe registered at the beginning of the target function by
kernel/kprobes.c so that a return probe instance can be setup when
a caller enters the target function. (A return probe instance contains
all the needed information for trampoline_probe_handler to do it's job.)
* Hooks added to the exit path of a task so that we can cleanup any left-over
return probe instances (i.e. if a task dies while inside a targeted function
then the return probe instance was reserved at the beginning of the function
but the function never returns so we need to mark the instance as unused.)

Signed-off-by: Rusty Lynch <rusty.lynch@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Rusty Lynch and committed by Linus Torvalds 97f7943d 9508dbfe

+106
+99
arch/ppc64/kernel/kprobes.c
··· 122 kprobe_saved_msr = kprobe_saved_msr_prev; 123 } 124 125 static inline int kprobe_handler(struct pt_regs *regs) 126 { 127 struct kprobe *p; ··· 226 227 no_kprobe: 228 return ret; 229 } 230 231 /* ··· 437 */ 438 memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); 439 return 1; 440 }
··· 122 kprobe_saved_msr = kprobe_saved_msr_prev; 123 } 124 125 + void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) 126 + { 127 + struct kretprobe_instance *ri; 128 + 129 + if ((ri = get_free_rp_inst(rp)) != NULL) { 130 + ri->rp = rp; 131 + ri->task = current; 132 + ri->ret_addr = (kprobe_opcode_t *)regs->link; 133 + 134 + /* Replace the return addr with trampoline addr */ 135 + regs->link = (unsigned long)kretprobe_trampoline; 136 + add_rp_inst(ri); 137 + } else { 138 + rp->nmissed++; 139 + } 140 + } 141 + 142 static inline int kprobe_handler(struct pt_regs *regs) 143 { 144 struct kprobe *p; ··· 209 210 no_kprobe: 211 return ret; 212 + } 213 + 214 + /* 215 + * Function return probe trampoline: 216 + * - init_kprobes() establishes a probepoint here 217 + * - When the probed function returns, this probe 218 + * causes the handlers to fire 219 + */ 220 + void kretprobe_trampoline_holder(void) 221 + { 222 + asm volatile(".global kretprobe_trampoline\n" 223 + "kretprobe_trampoline:\n" 224 + "nop\n"); 225 + } 226 + 227 + /* 228 + * Called when the probe at kretprobe trampoline is hit 229 + */ 230 + int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) 231 + { 232 + struct kretprobe_instance *ri = NULL; 233 + struct hlist_head *head; 234 + struct hlist_node *node, *tmp; 235 + unsigned long orig_ret_address = 0; 236 + unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; 237 + 238 + head = kretprobe_inst_table_head(current); 239 + 240 + /* 241 + * It is possible to have multiple instances associated with a given 242 + * task either because an multiple functions in the call path 243 + * have a return probe installed on them, and/or more then one return 244 + * return probe was registered for a target function. 245 + * 246 + * We can handle this because: 247 + * - instances are always inserted at the head of the list 248 + * - when multiple return probes are registered for the same 249 + * function, the first instance's ret_addr will point to the 250 + * real return address, and all the rest will point to 251 + * kretprobe_trampoline 252 + */ 253 + hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { 254 + if (ri->task != current) 255 + /* another task is sharing our hash bucket */ 256 + continue; 257 + 258 + if (ri->rp && ri->rp->handler) 259 + ri->rp->handler(ri, regs); 260 + 261 + orig_ret_address = (unsigned long)ri->ret_addr; 262 + recycle_rp_inst(ri); 263 + 264 + if (orig_ret_address != trampoline_address) 265 + /* 266 + * This is the real return address. Any other 267 + * instances associated with this task are for 268 + * other calls deeper on the call stack 269 + */ 270 + break; 271 + } 272 + 273 + BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); 274 + regs->nip = orig_ret_address; 275 + 276 + unlock_kprobes(); 277 + 278 + /* 279 + * By returning a non-zero value, we are telling 280 + * kprobe_handler() that we have handled unlocking 281 + * and re-enabling preemption. 282 + */ 283 + return 1; 284 } 285 286 /* ··· 348 */ 349 memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); 350 return 1; 351 + } 352 + 353 + static struct kprobe trampoline_p = { 354 + .addr = (kprobe_opcode_t *) &kretprobe_trampoline, 355 + .pre_handler = trampoline_probe_handler 356 + }; 357 + 358 + int __init arch_init(void) 359 + { 360 + return register_kprobe(&trampoline_p); 361 }
+4
arch/ppc64/kernel/process.c
··· 36 #include <linux/kallsyms.h> 37 #include <linux/interrupt.h> 38 #include <linux/utsname.h> 39 40 #include <asm/pgtable.h> 41 #include <asm/uaccess.h> ··· 308 309 void exit_thread(void) 310 { 311 #ifndef CONFIG_SMP 312 if (last_task_used_math == current) 313 last_task_used_math = NULL; ··· 324 { 325 struct thread_info *t = current_thread_info(); 326 327 if (t->flags & _TIF_ABI_PENDING) 328 t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT); 329
··· 36 #include <linux/kallsyms.h> 37 #include <linux/interrupt.h> 38 #include <linux/utsname.h> 39 + #include <linux/kprobes.h> 40 41 #include <asm/pgtable.h> 42 #include <asm/uaccess.h> ··· 307 308 void exit_thread(void) 309 { 310 + kprobe_flush_task(current); 311 + 312 #ifndef CONFIG_SMP 313 if (last_task_used_math == current) 314 last_task_used_math = NULL; ··· 321 { 322 struct thread_info *t = current_thread_info(); 323 324 + kprobe_flush_task(current); 325 if (t->flags & _TIF_ABI_PENDING) 326 t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT); 327
+3
include/asm-ppc64/kprobes.h
··· 42 43 #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)((func_descr_t *)pentry) 44 45 /* Architecture specific copy of original instruction */ 46 struct arch_specific_insn { 47 /* copy of original instruction */
··· 42 43 #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)((func_descr_t *)pentry) 44 45 + #define ARCH_SUPPORTS_KRETPROBES 46 + void kretprobe_trampoline(void); 47 + 48 /* Architecture specific copy of original instruction */ 49 struct arch_specific_insn { 50 /* copy of original instruction */