Skip to contentSkip to conversation

Kprobes for ppc64

PATCH In review Ananth N Mavinakayanahalli 2 participants 2 messages Oct 18, 2004 – Oct 23, 2004 kprobes

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Kprobes for ppc64

Hi,

Here is kprobes for ppc64. The patch applies on 2.6.9-rc4/2.6.9-final and provides the kprobes + jprobes functionality.

My earlier post did not reach the mailing lists, hence this resend.

Kprobes (Kernel dynamic probes) is a lightweight mechanism for kernel modules to insert probes into a running kernel, without the need to modify the underlying source. The probe handlers can then be coded to log relevent data at the probe point. More information on kprobes can be found at:

http://www-124.ibm.com/developerworks/oss/linux/projects/kprobes/

Jprobes (or jumper probes) is a small infrastructure to access function arguments. It can be used by defining a small stub with the same template as the routine in kernel, within which the required parameters can be logged.

The following pseudocode illustrates the usage of a jprobe, where the skbuff at tcp_v4_rcv() needs to be decoded:

............
struct jprobe jp;

jtcp_v4_rcv(struct skbuff *skb)

{
	/* decode and log skb related details as required */
	
	jprobe_return();
	return 0;
}

init_module

{
	jp.kp.addr = (kprobe_opcode_t *)<addr of tcp_v4_rcv>;
	jp.entry = JPROBE_ENTRY(jtcp_v4_rcv);
	register_jprobe(&jp);
	return 0;
}

cleanup_module

{
	unregister_jprobe(&jp);
}

............

NOTE:
1. The current implementation uses xmon's emulate_step() and hence requires xmon to be compiled in.
2. arch_prepare_kprobe() now returns an int. I have made the necessary changes to i386 and sparc64 kprobes files, but is untested.

Thanks,
Ananth

arch/i386/kernel/kprobes.c renamed from linux-2.6.9-rc4/arch/i386/kernel/kprobes.c +2 -1
diff -Naurp temp/linux-2.6.9-rc4/arch/i386/kernel/kprobes.c linux-2.6.9-rc4/arch/i386/kernel/kprobes.c
@@ -58,9 +58,10 @@ static inline int is_IF_modifier(kprobe_
 	return 0;
 }
 
-void arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
 {
 	memcpy(p->insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+	return 0;
 }
 
 static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
arch/ppc64/Kconfig.debug renamed from linux-2.6.9-rc4/arch/ppc64/Kconfig.debug +10 -0
diff -Naurp temp/linux-2.6.9-rc4/arch/ppc64/Kconfig.debug linux-2.6.9-rc4/arch/ppc64/Kconfig.debug
@@ -6,6 +6,16 @@ config DEBUG_STACKOVERFLOW
 	bool "Check for stack overflows"
 	depends on DEBUG_KERNEL
 
+config KPROBES
+        bool "Kprobes"
+	depends on DEBUG_KERNEL
+	help
+	  Kprobes allows you to trap at almost any kernel address and
+	  execute a callback function.  register_kprobe() establishes
+	  a probepoint and specifies the callback.  Kprobes is useful
+	  for kernel debugging, non-intrusive instrumentation and testing.
+	  If in doubt, say "N".
+
 config DEBUG_STACK_USAGE
 	bool "Stack utilization instrumentation"
 	depends on DEBUG_KERNEL
arch/ppc64/kernel/kprobes.c renamed from linux-2.6.9-rc4/arch/ppc64/kernel/kprobes.c +260 -0

260 lines not on this page · raw message

arch/ppc64/kernel/Makefile renamed from linux-2.6.9-rc4/arch/ppc64/kernel/Makefile +1 -0
diff -Naurp temp/linux-2.6.9-rc4/arch/ppc64/kernel/Makefile linux-2.6.9-rc4/arch/ppc64/kernel/Makefile
@@ -56,5 +56,6 @@ obj-$(CONFIG_PPC_PMAC)		+= pmac_smp.o sm
 endif
 
 obj-$(CONFIG_ALTIVEC)		+= vecemu.o vector.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o
 
 CFLAGS_ioctl32.o += -Ifs/
arch/ppc64/kernel/traps.c renamed from linux-2.6.9-rc4/arch/ppc64/kernel/traps.c +24 -0
diff -Naurp temp/linux-2.6.9-rc4/arch/ppc64/kernel/traps.c linux-2.6.9-rc4/arch/ppc64/kernel/traps.c
@@ -29,6 +29,7 @@
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/module.h>
+#include <asm/kdebug.h>
 
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
@@ -61,6 +62,20 @@ EXPORT_SYMBOL(__debugger_dabr_match);
 EXPORT_SYMBOL(__debugger_fault_handler);
 #endif
 
+struct notifier_block *ppc64_die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+	int err = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&die_notifier_lock, flags);
+	err = notifier_chain_register(&ppc64_die_chain, nb);
+	spin_unlock_irqrestore(&die_notifier_lock, flags);
+	return err;
+}
+
 /*
  * Trap & Exception support
  */
@@ -287,6 +302,9 @@ UnknownException(struct pt_regs *regs)
 void
 InstructionBreakpointException(struct pt_regs *regs)
 {
+	if (notify_die(DIE_BPT, "iabr_match", regs, 5,
+					5, SIGTRAP) == NOTIFY_STOP)
+		return;
 	if (debugger_iabr_match(regs))
 		return;
 	_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
@@ -297,6 +315,9 @@ SingleStepException(struct pt_regs *regs
 {
 	regs->msr &= ~MSR_SE;  /* Turn off 'trace' bit */
 
+	if (notify_die(DIE_SSTEP, "single_step", regs, 5,
+					5, SIGTRAP) == NOTIFY_STOP)
+		return;
 	if (debugger_sstep(regs))
 		return;
 
@@ -470,6 +491,9 @@ ProgramCheckException(struct pt_regs *re
 	} else if (regs->msr & 0x20000) {
 		/* trap exception */
 
+		if (notify_die(DIE_BPT, "breakpoint", regs, 5,
+					5, SIGTRAP) == NOTIFY_STOP)
+			return;
 		if (debugger_bpt(regs))
 			return;
 
arch/ppc64/mm/fault.c renamed from linux-2.6.9-rc4/arch/ppc64/mm/fault.c +7 -0
diff -Naurp temp/linux-2.6.9-rc4/arch/ppc64/mm/fault.c linux-2.6.9-rc4/arch/ppc64/mm/fault.c
@@ -36,6 +36,7 @@
 #include <asm/mmu_context.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/kdebug.h>
 
 /*
  * Check whether the instruction at regs->nip is a store using
@@ -96,6 +97,9 @@ int do_page_fault(struct pt_regs *regs, 
 	BUG_ON((trap == 0x380) || (trap == 0x480));
 
 	if (trap == 0x300) {
+		if (notify_die(DIE_PAGE_FAULT, "page_fault", regs, error_code,
+					11, SIGSEGV) == NOTIFY_STOP)
+			return 0;
 		if (debugger_fault_handler(regs))
 			return 0;
 	}
@@ -105,6 +109,9 @@ int do_page_fault(struct pt_regs *regs, 
 		return SIGSEGV;
 
 	if (error_code & 0x00400000) {
+		if (notify_die(DIE_BPT, "dabr_match", regs, error_code,
+					11, SIGSEGV) == NOTIFY_STOP)
+			return 0;
 		if (debugger_dabr_match(regs))
 			return 0;
 	}
arch/ppc64/xmon/xmon.c renamed from linux-2.6.9-rc4/arch/ppc64/xmon/xmon.c +2 -2
diff -Naurp temp/linux-2.6.9-rc4/arch/ppc64/xmon/xmon.c linux-2.6.9-rc4/arch/ppc64/xmon/xmon.c
@@ -132,7 +132,7 @@ static void csum(void);
 static void bootcmds(void);
 void dump_segments(void);
 static void symbol_lookup(void);
-static int emulate_step(struct pt_regs *regs, unsigned int instr);
+int emulate_step(struct pt_regs *regs, unsigned int instr);
 static void xmon_print_symbol(unsigned long address, const char *mid,
 			      const char *after);
 static const char *getvecname(unsigned long vec);
@@ -781,7 +781,7 @@ static int branch_taken(unsigned int ins
  * or -1 if the instruction is one that should not be stepped,
  * such as an rfid, or a mtmsrd that would clear MSR_RI.
  */
-static int emulate_step(struct pt_regs *regs, unsigned int instr)
+int emulate_step(struct pt_regs *regs, unsigned int instr)
 {
 	unsigned int opcode, rd;
 	unsigned long int imm;
arch/sparc64/kernel/kprobes.c renamed from linux-2.6.9-rc4/arch/sparc64/kernel/kprobes.c +2 -1
diff -Naurp temp/linux-2.6.9-rc4/arch/sparc64/kernel/kprobes.c linux-2.6.9-rc4/arch/sparc64/kernel/kprobes.c
@@ -38,10 +38,11 @@
  * - Mark that we are no longer actively in a kprobe.
  */
 
-void arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
 {
 	p->insn[0] = *p->addr;
 	p->insn[1] = BREAKPOINT_INSTRUCTION_2;
+	return 0;
 }
 
 /* kprobe_status settings */
include/asm-i386/kprobes.h renamed from linux-2.6.9-rc4/include/asm-i386/kprobes.h +2 -0
diff -Naurp temp/linux-2.6.9-rc4/include/asm-i386/kprobes.h linux-2.6.9-rc4/include/asm-i386/kprobes.h
@@ -38,6 +38,8 @@ typedef u8 kprobe_opcode_t;
 	? (MAX_STACK_SIZE) \
 	: (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
 
+#define JPROBE_ENTRY(pentry)	(kprobe_opcode_t *)pentry
+
 /* trap3/1 are intr gates for kprobes.  So, restore the status of IF,
  * if necessary, before executing the original int3/1 (trap) handler.
  */
include/asm-ppc64/kdebug.h renamed from linux-2.6.9-rc4/include/asm-ppc64/kdebug.h +43 -0

43 lines not on this page · raw message

include/asm-ppc64/kprobes.h renamed from linux-2.6.9-rc4/include/asm-ppc64/kprobes.h +53 -0

53 lines not on this page · raw message

include/linux/kprobes.h renamed from linux-2.6.9-rc4/include/linux/kprobes.h +1 -1
diff -Naurp temp/linux-2.6.9-rc4/include/linux/kprobes.h linux-2.6.9-rc4/include/linux/kprobes.h
@@ -94,7 +94,7 @@ static inline int kprobe_running(void)
 	return kprobe_cpu == smp_processor_id();
 }
 
-extern void arch_prepare_kprobe(struct kprobe *p);
+extern int arch_prepare_kprobe(struct kprobe *p);
 extern void show_registers(struct pt_regs *regs);
 
 /* Get the kprobe at this addr (if any).  Must have called lock_kprobes */
kernel/kprobes.c renamed from linux-2.6.9-rc4/kernel/kprobes.c +9 -2
diff -Naurp temp/linux-2.6.9-rc4/kernel/kprobes.c linux-2.6.9-rc4/kernel/kprobes.c
@@ -27,6 +27,8 @@
  *		interface to access function arguments.
  * 2004-Sep	Prasanna S Panchamukhi <[email protected]> Changed Kprobes
  *		exceptions notifier to be first on the priority list.
+ * 2004-Oct	Ananth N Mavinakayanahalli <[email protected]> 
+ *		arch_prepare_kprobe now returns an int.
  */
 #include <linux/kprobes.h>
 #include <linux/spinlock.h>
@@ -87,12 +89,17 @@ int register_kprobe(struct kprobe *p)
 	hlist_add_head(&p->hlist,
 		       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
 
-	arch_prepare_kprobe(p);
+	ret = arch_prepare_kprobe(p);
+	if (ret) {
+		unregister_kprobe(p);
+		ret = -EINVAL;
+		goto out;
+	}
 	p->opcode = *p->addr;
 	*p->addr = BREAKPOINT_INSTRUCTION;
 	flush_icache_range((unsigned long) p->addr,
 			   (unsigned long) p->addr + sizeof(kprobe_opcode_t));
-      out:
+out:
 	spin_unlock_irqrestore(&kprobe_lock, flags);
 	return ret;
 }
#1 lore raw
Quoted 5 lines · Ananth N Mavinakayanahalli

Here is kprobes for ppc64. The patch applies on 2.6.9-rc4/2.6.9-final and provides the kprobes + jprobes functionality.

1. The current implementation uses xmon's emulate_step() and hence requires xmon to be compiled in.

We can move emulate_step out to arch/ppc64/lib/step.c (and take out the printfs).

Quoted 2 lines

2. arch_prepare_kprobe() now returns an int. I have made the necessary changes to i386 and sparc64 kprobes files, but is untested.

Are you going to send this upstream?

Quoted 4 lines

+ * Interrupts are disabled on entry as trap3 is an interrupt gate and they
+ * remain disabled thorough out this function.
+ */
+static inline int kprobe_handler(struct pt_regs *regs)

Comments about "trap3" and "interrupt gate" don't help me understand this function on ppc64. :) At present interrupts are enabled in a program check exception handler but disabled in a single-step handler. When does this function get called?

arch/ppc64/mm/fault.c int do_page_fault(struct pt_regs *regs, · line 102

Quoted 7 lines
arch/ppc64/mm/fault.c renamed from linux-2.6.9-rc4/arch/ppc64/mm/fault.c +3 -0
@@ -96,6 +97,9 @@ int do_page_fault(struct pt_regs *regs, 
 	BUG_ON((trap == 0x380) || (trap == 0x480));
 
 	if (trap == 0x300) {
+		if (notify_die(DIE_PAGE_FAULT, "page_fault", regs, error_code,
+					11, SIGSEGV) == NOTIFY_STOP)
+			return 0;

Hmmm, this seems a bit heavyweight for adding to the page fault path. Have you done any benchmarks with vs. without kprobes?

On the whole the patch looks OK. I haven't checked the kprobe_handler code to see if I think it's all SMP- and preempt-safe, but I assume you have done it similarly on x86 and checked it there.

Paul.