aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRusty Lynch <rusty.lynch@intel.com>2005-06-27 18:17:08 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-27 18:23:52 -0400
commit802eae7c800fb7f583e6c06afa363585af2bef00 (patch)
treefa30469a4dc1f1a20abdf9466ec3c68bddef6ac9
parent9ec4b1f356b3bad928ae8e2aa9caebfa737d52df (diff)
[PATCH] Return probe redesign: architecture independent changes
The following is the second version of the function return probe patches I sent out earlier this week. Changes since my last submission include: * Fix in ppc64 code removing an unneeded call to re-enable preemption * Fix a build problem in ia64 when kprobes was turned off * Added another BUG_ON check to each of the architecture trampoline handlers My initial patch description ==> From my experiences with adding return probes to x86_64 and ia64, and the feedback on LKML to those patches, I think we can simplify the design for return probes. The following patch tweaks the original design such that: * Instead of storing the stack address in the return probe instance, the task pointer is stored. This gives us all we need in order to: - find the correct return probe instance when we enter the trampoline (even if we are recursing) - find all left-over return probe instances when the task is going away This has the side effect of simplifying the implementation since more work can be done in kernel/kprobes.c since architecture specific knowledge of the stack layout is no longer required. Specifically, we no longer have: - arch_get_kprobe_task() - arch_kprobe_flush_task() - get_rp_inst_tsk() - get_rp_inst() - trampoline_post_handler() <see next bullet> * Instead of splitting the return probe handling and cleanup logic across the pre and post trampoline handlers, all the work is pushed into the pre function (trampoline_probe_handler), and then we skip single stepping the original function. In this case the original instruction to be single stepped was just a NOP, and we can do without the extra interruption. The new flow of events to having a return probe handler execute when a target function exits is: * At system initialization time, a kprobe is inserted at the beginning of kretprobe_trampoline. kernel/kprobes.c use to handle this on it's own, but ia64 needed to do this a little differently (i.e. a function pointer is really a pointer to a structure containing the instruction pointer and a global pointer), so I added the notion of arch_init(), so that kernel/kprobes.c:init_kprobes() now allows architecture specific initialization by calling arch_init() before exiting. Each architecture now registers a kprobe on it's own trampoline function. * register_kretprobe() will insert a kprobe at the beginning of the targeted function with the kprobe pre_handler set to arch_prepare_kretprobe (still no change) * When the target function is entered, the kprobe is fired, calling arch_prepare_kretprobe (still no change) * In arch_prepare_kretprobe() we try to get a free instance and if one is available then we fill out the instance with a pointer to the return probe, the original return address, and a pointer to the task structure (instead of the stack address.) Just like before we change the return address to the trampoline function and mark the instance as used. If multiple return probes are registered for a given target function, then arch_prepare_kretprobe() will get called multiple times for the same task (since our kprobe implementation is able to handle multiple kprobes at the same address.) Past the first call to arch_prepare_kretprobe, we end up with the original address stored in the return probe instance pointing to our trampoline function. (This is a significant difference from the original arch_prepare_kretprobe design.) * Target function executes like normal and then returns to kretprobe_trampoline. * kprobe inserted on the first instruction of kretprobe_trampoline is fired and calls trampoline_probe_handler() (no change here) * trampoline_probe_handler() consumes each of the instances associated with the current task by calling the registered handler function and marking the instance as unused until an instance is found that has a return address different then the trampoline function. (change similar to my previous ia64 RFC) * If the task is killed with some left-over return probe instances (meaning that a target function was entered, but never returned), then we just free any instances associated with the task. (Not much different other then we can handle this without calling architecture specific functions.) There is a known problem that this patch does not yet solve where registering a return probe flush_old_exec or flush_thread will put us in a bad state. Most likely the best way to handle this is to not allow registering return probes on these two functions. (Significant change) This patch series applies to the 2.6.12-rc6-mm1 kernel, and provides: * kernel/kprobes.c changes * i386 patch of existing return probes implementation * x86_64 patch of existing return probe implementation * ia64 implementation * ppc64 implementation (provided by Ananth) This patch implements the architecture independant changes for a reworking of the kprobes based function return probes design. Changes include: * Removing functions for querying a return probe instance off a stack address * Removing the stack_addr field from the kretprobe_instance definition, and adding a task pointer * Adding architecture specific initialization via arch_init() * Removing extern definitions for the architecture trampoline functions (this isn't needed anymore since the architecture handles the initialization of the kprobe in the return probe trampoline function.) 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>
-rw-r--r--include/linux/kprobes.h28
-rw-r--r--kernel/kprobes.c69
2 files changed, 22 insertions, 75 deletions
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index d304d4579856..b7a194c4362a 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -104,33 +104,12 @@ struct jprobe {
104}; 104};
105 105
106#ifdef ARCH_SUPPORTS_KRETPROBES 106#ifdef ARCH_SUPPORTS_KRETPROBES
107extern int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs);
108extern void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs,
109 unsigned long flags);
110extern struct task_struct *arch_get_kprobe_task(void *ptr);
111extern void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs); 107extern void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs);
112extern void arch_kprobe_flush_task(struct task_struct *tk);
113#else /* ARCH_SUPPORTS_KRETPROBES */ 108#else /* ARCH_SUPPORTS_KRETPROBES */
114static inline void kretprobe_trampoline(void)
115{
116}
117static inline int trampoline_probe_handler(struct kprobe *p,
118 struct pt_regs *regs)
119{
120 return 0;
121}
122static inline void trampoline_post_handler(struct kprobe *p,
123 struct pt_regs *regs, unsigned long flags)
124{
125}
126static inline void arch_prepare_kretprobe(struct kretprobe *rp, 109static inline void arch_prepare_kretprobe(struct kretprobe *rp,
127 struct pt_regs *regs) 110 struct pt_regs *regs)
128{ 111{
129} 112}
130static inline void arch_kprobe_flush_task(struct task_struct *tk)
131{
132}
133#define arch_get_kprobe_task(ptr) ((struct task_struct *)NULL)
134#endif /* ARCH_SUPPORTS_KRETPROBES */ 113#endif /* ARCH_SUPPORTS_KRETPROBES */
135/* 114/*
136 * Function-return probe - 115 * Function-return probe -
@@ -155,8 +134,8 @@ struct kretprobe_instance {
155 struct hlist_node uflist; /* either on free list or used list */ 134 struct hlist_node uflist; /* either on free list or used list */
156 struct hlist_node hlist; 135 struct hlist_node hlist;
157 struct kretprobe *rp; 136 struct kretprobe *rp;
158 void *ret_addr; 137 kprobe_opcode_t *ret_addr;
159 void *stack_addr; 138 struct task_struct *task;
160}; 139};
161 140
162#ifdef CONFIG_KPROBES 141#ifdef CONFIG_KPROBES
@@ -176,6 +155,7 @@ extern void arch_copy_kprobe(struct kprobe *p);
176extern void arch_arm_kprobe(struct kprobe *p); 155extern void arch_arm_kprobe(struct kprobe *p);
177extern void arch_disarm_kprobe(struct kprobe *p); 156extern void arch_disarm_kprobe(struct kprobe *p);
178extern void arch_remove_kprobe(struct kprobe *p); 157extern void arch_remove_kprobe(struct kprobe *p);
158extern int arch_init(void);
179extern void show_registers(struct pt_regs *regs); 159extern void show_registers(struct pt_regs *regs);
180extern kprobe_opcode_t *get_insn_slot(void); 160extern kprobe_opcode_t *get_insn_slot(void);
181extern void free_insn_slot(kprobe_opcode_t *slot); 161extern void free_insn_slot(kprobe_opcode_t *slot);
@@ -196,8 +176,6 @@ int register_kretprobe(struct kretprobe *rp);
196void unregister_kretprobe(struct kretprobe *rp); 176void unregister_kretprobe(struct kretprobe *rp);
197 177
198struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp); 178struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp);
199struct kretprobe_instance *get_rp_inst(void *sara);
200struct kretprobe_instance *get_rp_inst_tsk(struct task_struct *tk);
201void add_rp_inst(struct kretprobe_instance *ri); 179void add_rp_inst(struct kretprobe_instance *ri);
202void kprobe_flush_task(struct task_struct *tk); 180void kprobe_flush_task(struct task_struct *tk);
203void recycle_rp_inst(struct kretprobe_instance *ri); 181void recycle_rp_inst(struct kretprobe_instance *ri);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 65242529a75f..90c0e82b650c 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -240,12 +240,6 @@ static int aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
240 return 0; 240 return 0;
241} 241}
242 242
243struct kprobe trampoline_p = {
244 .addr = (kprobe_opcode_t *) &kretprobe_trampoline,
245 .pre_handler = trampoline_probe_handler,
246 .post_handler = trampoline_post_handler
247};
248
249struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp) 243struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp)
250{ 244{
251 struct hlist_node *node; 245 struct hlist_node *node;
@@ -264,35 +258,18 @@ static struct kretprobe_instance *get_used_rp_inst(struct kretprobe *rp)
264 return NULL; 258 return NULL;
265} 259}
266 260
267struct kretprobe_instance *get_rp_inst(void *sara)
268{
269 struct hlist_head *head;
270 struct hlist_node *node;
271 struct task_struct *tsk;
272 struct kretprobe_instance *ri;
273
274 tsk = arch_get_kprobe_task(sara);
275 head = &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)];
276 hlist_for_each_entry(ri, node, head, hlist) {
277 if (ri->stack_addr == sara)
278 return ri;
279 }
280 return NULL;
281}
282
283void add_rp_inst(struct kretprobe_instance *ri) 261void add_rp_inst(struct kretprobe_instance *ri)
284{ 262{
285 struct task_struct *tsk;
286 /* 263 /*
287 * Remove rp inst off the free list - 264 * Remove rp inst off the free list -
288 * Add it back when probed function returns 265 * Add it back when probed function returns
289 */ 266 */
290 hlist_del(&ri->uflist); 267 hlist_del(&ri->uflist);
291 tsk = arch_get_kprobe_task(ri->stack_addr); 268
292 /* Add rp inst onto table */ 269 /* Add rp inst onto table */
293 INIT_HLIST_NODE(&ri->hlist); 270 INIT_HLIST_NODE(&ri->hlist);
294 hlist_add_head(&ri->hlist, 271 hlist_add_head(&ri->hlist,
295 &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]); 272 &kretprobe_inst_table[hash_ptr(ri->task, KPROBE_HASH_BITS)]);
296 273
297 /* Also add this rp inst to the used list. */ 274 /* Also add this rp inst to the used list. */
298 INIT_HLIST_NODE(&ri->uflist); 275 INIT_HLIST_NODE(&ri->uflist);
@@ -319,34 +296,25 @@ struct hlist_head * kretprobe_inst_table_head(struct task_struct *tsk)
319 return &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]; 296 return &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)];
320} 297}
321 298
322struct kretprobe_instance *get_rp_inst_tsk(struct task_struct *tk)
323{
324 struct task_struct *tsk;
325 struct hlist_head *head;
326 struct hlist_node *node;
327 struct kretprobe_instance *ri;
328
329 head = &kretprobe_inst_table[hash_ptr(tk, KPROBE_HASH_BITS)];
330
331 hlist_for_each_entry(ri, node, head, hlist) {
332 tsk = arch_get_kprobe_task(ri->stack_addr);
333 if (tsk == tk)
334 return ri;
335 }
336 return NULL;
337}
338
339/* 299/*
340 * This function is called from do_exit or do_execv when task tk's stack is 300 * This function is called from exit_thread or flush_thread when task tk's
341 * about to be recycled. Recycle any function-return probe instances 301 * stack is being recycled so that we can recycle any function-return probe
342 * associated with this task. These represent probed functions that have 302 * instances associated with this task. These left over instances represent
343 * been called but may never return. 303 * probed functions that have been called but will never return.
344 */ 304 */
345void kprobe_flush_task(struct task_struct *tk) 305void kprobe_flush_task(struct task_struct *tk)
346{ 306{
307 struct kretprobe_instance *ri;
308 struct hlist_head *head;
309 struct hlist_node *node, *tmp;
347 unsigned long flags = 0; 310 unsigned long flags = 0;
311
348 spin_lock_irqsave(&kprobe_lock, flags); 312 spin_lock_irqsave(&kprobe_lock, flags);
349 arch_kprobe_flush_task(tk); 313 head = kretprobe_inst_table_head(current);
314 hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
315 if (ri->task == tk)
316 recycle_rp_inst(ri);
317 }
350 spin_unlock_irqrestore(&kprobe_lock, flags); 318 spin_unlock_irqrestore(&kprobe_lock, flags);
351} 319}
352 320
@@ -606,9 +574,10 @@ static int __init init_kprobes(void)
606 INIT_HLIST_HEAD(&kretprobe_inst_table[i]); 574 INIT_HLIST_HEAD(&kretprobe_inst_table[i]);
607 } 575 }
608 576
609 err = register_die_notifier(&kprobe_exceptions_nb); 577 err = arch_init();
610 /* Register the trampoline probe for return probe */ 578 if (!err)
611 register_kprobe(&trampoline_p); 579 err = register_die_notifier(&kprobe_exceptions_nb);
580
612 return err; 581 return err;
613} 582}
614 583