[PATCH] Return probe redesign: ia64 specific implementation

The following patch implements function return probes for ia64 using
the revised design. With this new design we no longer need to do some
of the odd hacks previous required on the last ia64 return probe port
that I sent out for comments.

Note that this new implementation still does not resolve the problem noted
by Keith Owens where backtrace data is lost after a return probe is hit.

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 9508dbfe ba8af12f

+125 -7
+101 -2
arch/ia64/kernel/kprobes.c
··· 290 current_kprobe = p; 291 } 292 293 int arch_prepare_kprobe(struct kprobe *p) 294 { 295 unsigned long addr = (unsigned long) p->addr; ··· 580 if (p->pre_handler && p->pre_handler(p, regs)) 581 /* 582 * Our pre-handler is specifically requesting that we just 583 - * do a return. This is handling the case where the 584 - * pre-handler is really our special jprobe pre-handler. 585 */ 586 return 1; 587 ··· 686 { 687 *regs = jprobe_saved_regs; 688 return 1; 689 }
··· 290 current_kprobe = p; 291 } 292 293 + static void kretprobe_trampoline(void) 294 + { 295 + } 296 + 297 + /* 298 + * At this point the target function has been tricked into 299 + * returning into our trampoline. Lookup the associated instance 300 + * and then: 301 + * - call the handler function 302 + * - cleanup by marking the instance as unused 303 + * - long jump back to the original return address 304 + */ 305 + int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) 306 + { 307 + struct kretprobe_instance *ri = NULL; 308 + struct hlist_head *head; 309 + struct hlist_node *node, *tmp; 310 + unsigned long orig_ret_address = 0; 311 + unsigned long trampoline_address = 312 + ((struct fnptr *)kretprobe_trampoline)->ip; 313 + 314 + head = kretprobe_inst_table_head(current); 315 + 316 + /* 317 + * It is possible to have multiple instances associated with a given 318 + * task either because an multiple functions in the call path 319 + * have a return probe installed on them, and/or more then one return 320 + * return probe was registered for a target function. 321 + * 322 + * We can handle this because: 323 + * - instances are always inserted at the head of the list 324 + * - when multiple return probes are registered for the same 325 + * function, the first instance's ret_addr will point to the 326 + * real return address, and all the rest will point to 327 + * kretprobe_trampoline 328 + */ 329 + hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { 330 + if (ri->task != current) 331 + /* another task is sharing our hash bucket */ 332 + continue; 333 + 334 + if (ri->rp && ri->rp->handler) 335 + ri->rp->handler(ri, regs); 336 + 337 + orig_ret_address = (unsigned long)ri->ret_addr; 338 + recycle_rp_inst(ri); 339 + 340 + if (orig_ret_address != trampoline_address) 341 + /* 342 + * This is the real return address. Any other 343 + * instances associated with this task are for 344 + * other calls deeper on the call stack 345 + */ 346 + break; 347 + } 348 + 349 + BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); 350 + regs->cr_iip = orig_ret_address; 351 + 352 + unlock_kprobes(); 353 + preempt_enable_no_resched(); 354 + 355 + /* 356 + * By returning a non-zero value, we are telling 357 + * kprobe_handler() that we have handled unlocking 358 + * and re-enabling preemption. 359 + */ 360 + return 1; 361 + } 362 + 363 + void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) 364 + { 365 + struct kretprobe_instance *ri; 366 + 367 + if ((ri = get_free_rp_inst(rp)) != NULL) { 368 + ri->rp = rp; 369 + ri->task = current; 370 + ri->ret_addr = (kprobe_opcode_t *)regs->b0; 371 + 372 + /* Replace the return addr with trampoline addr */ 373 + regs->b0 = ((struct fnptr *)kretprobe_trampoline)->ip; 374 + 375 + add_rp_inst(ri); 376 + } else { 377 + rp->nmissed++; 378 + } 379 + } 380 + 381 int arch_prepare_kprobe(struct kprobe *p) 382 { 383 unsigned long addr = (unsigned long) p->addr; ··· 492 if (p->pre_handler && p->pre_handler(p, regs)) 493 /* 494 * Our pre-handler is specifically requesting that we just 495 + * do a return. This is used for both the jprobe pre-handler 496 + * and the kretprobe trampoline 497 */ 498 return 1; 499 ··· 598 { 599 *regs = jprobe_saved_regs; 600 return 1; 601 + } 602 + 603 + static struct kprobe trampoline_p = { 604 + .pre_handler = trampoline_probe_handler 605 + }; 606 + 607 + int __init arch_init(void) 608 + { 609 + trampoline_p.addr = 610 + (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip; 611 + return register_kprobe(&trampoline_p); 612 }
+16
arch/ia64/kernel/process.c
··· 27 #include <linux/efi.h> 28 #include <linux/interrupt.h> 29 #include <linux/delay.h> 30 31 #include <asm/cpu.h> 32 #include <asm/delay.h> ··· 708 void 709 flush_thread (void) 710 { 711 /* drop floating-point and debug-register state if it exists: */ 712 current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID); 713 ia64_drop_fpu(current); ··· 729 void 730 exit_thread (void) 731 { 732 ia64_drop_fpu(current); 733 #ifdef CONFIG_PERFMON 734 /* if needed, stop monitoring and flush state to perfmon context */
··· 27 #include <linux/efi.h> 28 #include <linux/interrupt.h> 29 #include <linux/delay.h> 30 + #include <linux/kprobes.h> 31 32 #include <asm/cpu.h> 33 #include <asm/delay.h> ··· 707 void 708 flush_thread (void) 709 { 710 + /* 711 + * Remove function-return probe instances associated with this task 712 + * and put them back on the free list. Do not insert an exit probe for 713 + * this function, it will be disabled by kprobe_flush_task if you do. 714 + */ 715 + kprobe_flush_task(current); 716 + 717 /* drop floating-point and debug-register state if it exists: */ 718 current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID); 719 ia64_drop_fpu(current); ··· 721 void 722 exit_thread (void) 723 { 724 + 725 + /* 726 + * Remove function-return probe instances associated with this task 727 + * and put them back on the free list. Do not insert an exit probe for 728 + * this function, it will be disabled by kprobe_flush_task if you do. 729 + */ 730 + kprobe_flush_task(current); 731 + 732 ia64_drop_fpu(current); 733 #ifdef CONFIG_PERFMON 734 /* if needed, stop monitoring and flush state to perfmon context */
+8 -5
include/asm-ia64/kprobes.h
··· 64 65 #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry 66 67 #define SLOT0_OPCODE_SHIFT (37) 68 #define SLOT1_p1_OPCODE_SHIFT (37 - (64-46)) 69 #define SLOT2_OPCODE_SHIFT (37) ··· 97 }; 98 99 /* ia64 does not need this */ 100 - static inline void jprobe_return(void) 101 - { 102 - } 103 - 104 - /* ia64 does not need this */ 105 static inline void arch_copy_kprobe(struct kprobe *p) 106 { 107 } ··· 104 #ifdef CONFIG_KPROBES 105 extern int kprobe_exceptions_notify(struct notifier_block *self, 106 unsigned long val, void *data); 107 #else /* !CONFIG_KPROBES */ 108 static inline int kprobe_exceptions_notify(struct notifier_block *self, 109 unsigned long val, void *data)
··· 64 65 #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry 66 67 + #define ARCH_SUPPORTS_KRETPROBES 68 + 69 #define SLOT0_OPCODE_SHIFT (37) 70 #define SLOT1_p1_OPCODE_SHIFT (37 - (64-46)) 71 #define SLOT2_OPCODE_SHIFT (37) ··· 95 }; 96 97 /* ia64 does not need this */ 98 static inline void arch_copy_kprobe(struct kprobe *p) 99 { 100 } ··· 107 #ifdef CONFIG_KPROBES 108 extern int kprobe_exceptions_notify(struct notifier_block *self, 109 unsigned long val, void *data); 110 + 111 + /* ia64 does not need this */ 112 + static inline void jprobe_return(void) 113 + { 114 + } 115 + 116 #else /* !CONFIG_KPROBES */ 117 static inline int kprobe_exceptions_notify(struct notifier_block *self, 118 unsigned long val, void *data)