This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[RFC PATCH 2/2] check safety in stop_machine_run
- From: Masami Hiramatsu <hiramatu at sdl dot hitachi dot co dot jp>
- To: Mathieu Desnoyers <compudj at krystal dot dyndns dot org>
- Cc: Ashok Raj <ashok dot raj at intel dot com>, systemtap at sources dot redhat dot com,"Keshavamurthy, Anil S" <anil dot s dot keshavamurthy at intel dot com>,Roland McGrath <roland at redhat dot com>,Richard J Moore <richardj_moore at uk dot ibm dot com>, Andi Kleen <ak at muc dot de>,michel dot dagenais at polymtl dot ca, "Frank Ch. Eigler" <fche at redhat dot com>,Karim Yaghmour <karim at opersys dot com>,Satoshi Oshima <soshima at redhat dot com>, Hideo Aoki <haoki at redhat dot com>,Yumiko Sugita <sugita at sdl dot hitachi dot co dot jp>
- Date: Fri, 05 Aug 2005 21:56:29 +0900
- Subject: [RFC PATCH 2/2] check safety in stop_machine_run
- References: <20050804175433.GA23544@Krystal> <20050804125933.A17107@unix-os.sc.intel.com> <20050805015549.GA8644@Krystal>
Hi,
This patch contains full set of a djprobe patch for linux-2.6.12 that
uses stop_machine_run() to check safety of all cpus. This method has
some performance issues.
- It must create threads for each CPU.
- It might sleep.
- If we want to insert a number of probes (this is usual situation in
the module initialization), we must wait completion of all insertion.
So, I think this is an optional method of check routine.
--
Masami HIRAMATSU
2nd Research Dept.
Hitachi, Ltd., Systems Development Laboratory
E-mail: hiramatu@sdl.hitachi.co.jp
diff -Narup linux-2.6.12/arch/i386/Kconfig.debug linux-2.6.12-djprobe.3/arch/i386/Kconfig.debug
--- linux-2.6.12/arch/i386/Kconfig.debug 2005-06-18 04:48:29.000000000 +0900
+++ linux-2.6.12-djprobe.3/arch/i386/Kconfig.debug 2005-08-04 16:58:55.000000000 +0900
@@ -29,6 +29,14 @@ config KPROBES
for kernel debugging, non-intrusive instrumentation and testing.
If in doubt, say "N".
+config DJPROBE
+ bool "Direct Jump Probe"
+ depends on KPROBES && !PREEMPT
+ help
+ Djprobe is ultra-light probing system. This uses a jmp opecode
+ instead of an int3 trap opecode. Djprobe is useful for probing
+ kernel tight timing problems.
+
config DEBUG_STACK_USAGE
bool "Stack utilization instrumentation"
depends on DEBUG_KERNEL
diff -Narup linux-2.6.12/arch/i386/kernel/Makefile linux-2.6.12-djprobe.3/arch/i386/kernel/Makefile
--- linux-2.6.12/arch/i386/kernel/Makefile 2005-06-18 04:48:29.000000000 +0900
+++ linux-2.6.12-djprobe.3/arch/i386/kernel/Makefile 2005-08-04 16:58:55.000000000 +0900
@@ -27,6 +27,7 @@ obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot
obj-$(CONFIG_X86_NUMAQ) += numaq.o
obj-$(CONFIG_X86_SUMMIT_NUMA) += summit.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_DJPROBE) += stub_djprobe.o
obj-$(CONFIG_MODULES) += module.o
obj-y += sysenter.o vsyscall.o
obj-$(CONFIG_ACPI_SRAT) += srat.o
diff -Narup linux-2.6.12/arch/i386/kernel/kprobes.c linux-2.6.12-djprobe.3/arch/i386/kernel/kprobes.c
--- linux-2.6.12/arch/i386/kernel/kprobes.c 2005-06-18 04:48:29.000000000 +0900
+++ linux-2.6.12-djprobe.3/arch/i386/kernel/kprobes.c 2005-08-05 20:32:12.000000000 +0900
@@ -32,6 +32,7 @@
#include <linux/preempt.h>
#include <asm/kdebug.h>
#include <asm/desc.h>
+#include <asm/cacheflush.h>
/* kprobe_status settings */
#define KPROBE_HIT_ACTIVE 0x00000001
@@ -390,3 +391,126 @@ int longjmp_break_handler(struct kprobe
}
return 0;
}
+
+#ifdef CONFIG_DJPROBE
+/*
+ * DJProbe (Direct Jump Probe): Insertion and Recovery
+ * Insertion:
+ * 1. Djprobe inserts a kprobe at the specified address.
+ * 2. It uses stop_machine_run() to check safety of the all cpus.
+ * 3. After it finish checking all cpus, it replace int3 to relative jump
+ * instruction.
+ * Recovery:
+ * 1. Djprobe recovers a kprobe's int3 and original code except the first byte
+ * at the specified address.
+ * 2. It uses stop_machine_run() to check safety of the all cpus.
+ * 3. After it finish checking all cpus, it uninstalls the kprobe.
+ */
+
+/* jmp code manipulators */
+struct __arch_jmp_op {
+ char op;
+ long raddr;
+} __attribute__((packed));
+/* insert jmp code */
+static inline void __set_jmp_op(void *from, void *to)
+{
+ struct __arch_jmp_op *jop;
+ jop = (struct __arch_jmp_op *)from;
+ jop->raddr=(long)(to) - ((long)(from) + 5);
+ smp_flush_icache_all();
+ jop->op = RELATIVEJUMP_INSTRUCTION;
+}
+/* switch back to the kprobe */
+static inline void __set_breakpoint_op(void *dest, void *orig)
+{
+ struct __arch_jmp_op *jop = (struct __arch_jmp_op *)dest,
+ *jop2 = (struct __arch_jmp_op *)orig;
+
+ jop->op = BREAKPOINT_INSTRUCTION;
+ smp_flush_icache_all();
+ jop->raddr = jop2->raddr;
+}
+/*
+ * djprobe call back function: called from stub code.
+ */
+static void asmlinkage djprobe_callback(struct djprobe_instance * djpi,
+ struct pt_regs *regs)
+{
+ /*TODO: use list*/
+ if (djpi->djp && djpi->djp->handler)
+ djpi->djp->handler(djpi->djp, regs);
+}
+
+/*
+ * Copy post processing instructions
+ * Target instructions MUST be relocatable.
+ */
+int arch_prepare_djprobe_instance(struct djprobe_instance *djpi,
+ unsigned long size)
+{
+ kprobe_opcode_t *stub;
+ stub = djpi->stub.insn;
+
+ /* copy arch-dep-instance from template */
+ memcpy((void*)stub, (void*)&arch_tmpl_stub_entry, ARCH_STUB_SIZE);
+
+ /* set probe information */
+ *((long*)(stub + ARCH_STUB_VAL_IDX)) = (long)djpi;
+ /* set probe function */
+ *((long*)(stub + ARCH_STUB_CALL_IDX)) = (long)djprobe_callback;
+
+ /* copy instructions into the middle of axporbe instance */
+ memcpy((void*)(stub + ARCH_STUB_INST_IDX),
+ (void*)djpi->kp.addr, size);
+ djpi->stub.size = size;
+
+ /* set returning jmp instruction at the tail of axporbe instance*/
+ __set_jmp_op(stub + ARCH_STUB_END_IDX,
+ (void*)((long)djpi->kp.addr + size));
+
+ return 0;
+}
+
+/* Insert "jmp" instruction into the probing point. */
+void arch_install_djprobe_instance(struct djprobe_instance *djpi)
+{
+ kprobe_opcode_t *stub;
+ stub = djpi->stub.insn;
+ __set_jmp_op((void*)djpi->kp.addr, (void*)stub);
+}
+/* Write back original instructions & kprobe */
+void arch_uninstall_djprobe_instance(struct djprobe_instance *djpi)
+{
+ kprobe_opcode_t *stub;
+ stub = &djpi->stub.insn[ARCH_STUB_INST_IDX];
+ __set_breakpoint_op((void*)djpi->kp.addr, (void*)stub);
+}
+
+/*
+ * safety check handler
+ */
+int djprobe_bypass_handler(struct kprobe * kp, struct pt_regs * regs)
+{
+ struct djprobe_instance *djpi =
+ container_of(kp,struct djprobe_instance, kp);
+ kprobe_opcode_t *stub = djpi->stub.insn;
+
+ if (DJPI_EMPTY(djpi)) {
+ /* fixup dummy instruction */
+ kp->ainsn.insn[0] = djpi->stub.insn[ARCH_STUB_INST_IDX];
+ return 0;
+ } else {
+ regs->eip = (unsigned long)stub;
+ regs->eflags |= TF_MASK;
+ regs->eflags &= ~IF_MASK;
+ /*
+ * dummy return code :
+ * This code is to avoid to be changed eip value by
+ * resume_execute() of kprobes
+ */
+ kp->ainsn.insn[0] = RETURN_INSTRUCTION;
+ return 1; /* already prepared */
+ }
+}
+#endif /*DJPROBE*/
diff -Narup linux-2.6.12/arch/i386/kernel/stub_djprobe.S linux-2.6.12-djprobe.3/arch/i386/kernel/stub_djprobe.S
--- linux-2.6.12/arch/i386/kernel/stub_djprobe.S 1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.12-djprobe.3/arch/i386/kernel/stub_djprobe.S 2005-08-04 16:58:55.000000000 +0900
@@ -0,0 +1,78 @@
+/*
+ * linux/arch/i386/stub_djprobe.S
+ *
+ * Copyright (C) HITACHI,LTD. 2005
+ * Created by Masami Hiramatsu <hiramatu@sdl.hitachi.co.jp>
+ */
+
+#include <linux/config.h>
+
+# jmp into this function from other functions.
+.global arch_tmpl_stub_entry
+arch_tmpl_stub_entry:
+ nop
+ subl $8, %esp #skip segment registers.
+ pushf
+ subl $20, %esp #skip segment registers.
+ pushl %eax
+ pushl %ebp
+ pushl %edi
+ pushl %esi
+ pushl %edx
+ pushl %ecx
+ pushl %ebx
+
+ movl %esp, %eax
+ pushl %eax
+ addl $60, %eax
+ movl %eax, 56(%esp)
+.global arch_tmpl_stub_val
+arch_tmpl_stub_val:
+ movl $0xffffffff, %eax
+ pushl %eax
+.global arch_tmpl_stub_call
+arch_tmpl_stub_call:
+ movl $0xffffffff, %eax
+ call *%eax
+ addl $8, %esp
+
+ popl %ebx
+ popl %ecx
+ popl %edx
+ popl %esi
+ popl %edi
+ popl %ebp
+ popl %eax
+ addl $20, %esp
+ popf
+ addl $8, %esp
+.global arch_tmpl_stub_inst
+arch_tmpl_stub_inst:
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+.global arch_tmpl_stub_end
+arch_tmpl_stub_end:
+ nop
+ nop
+ nop
+ nop
+ nop
diff -Narup linux-2.6.12/arch/i386/mm/pageattr.c linux-2.6.12-djprobe.3/arch/i386/mm/pageattr.c
--- linux-2.6.12/arch/i386/mm/pageattr.c 2005-06-18 04:48:29.000000000 +0900
+++ linux-2.6.12-djprobe.3/arch/i386/mm/pageattr.c 2005-08-04 16:58:55.000000000 +0900
@@ -217,5 +217,15 @@ void kernel_map_pages(struct page *page,
}
#endif
+static void local_flush_icache(void * info)
+{
+ cpuid_eax(0);
+}
+
+void smp_flush_icache_all(void)
+{
+ on_each_cpu(local_flush_icache, NULL, 1,1);
+}
+
EXPORT_SYMBOL(change_page_attr);
EXPORT_SYMBOL(global_flush_tlb);
diff -Narup linux-2.6.12/include/asm-i386/cacheflush.h linux-2.6.12-djprobe.3/include/asm-i386/cacheflush.h
--- linux-2.6.12/include/asm-i386/cacheflush.h 2005-06-18 04:48:29.000000000 +0900
+++ linux-2.6.12-djprobe.3/include/asm-i386/cacheflush.h 2005-08-04 16:58:55.000000000 +0900
@@ -31,4 +31,6 @@ int change_page_attr(struct page *page,
void kernel_map_pages(struct page *page, int numpages, int enable);
#endif
+void smp_flush_icache_all(void);
+
#endif /* _I386_CACHEFLUSH_H */
diff -Narup linux-2.6.12/include/asm-i386/kprobes.h linux-2.6.12-djprobe.3/include/asm-i386/kprobes.h
--- linux-2.6.12/include/asm-i386/kprobes.h 2005-06-18 04:48:29.000000000 +0900
+++ linux-2.6.12-djprobe.3/include/asm-i386/kprobes.h 2005-08-04 16:58:55.000000000 +0900
@@ -31,6 +31,8 @@ struct pt_regs;
typedef u8 kprobe_opcode_t;
#define BREAKPOINT_INSTRUCTION 0xcc
+#define RELATIVEJUMP_INSTRUCTION 0xe9
+#define RETURN_INSTRUCTION 0xc3
#define MAX_INSN_SIZE 16
#define MAX_STACK_SIZE 64
#define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \
@@ -46,6 +48,31 @@ struct arch_specific_insn {
kprobe_opcode_t insn[MAX_INSN_SIZE];
};
+#ifdef CONFIG_DJPROBE
+/* stub template code */
+extern long arch_tmpl_stub_entry;
+extern long arch_tmpl_stub_val;
+extern long arch_tmpl_stub_call;
+extern long arch_tmpl_stub_inst;
+extern long arch_tmpl_stub_end;
+
+#define ARCH_STUB_VAL_IDX ((long)&arch_tmpl_stub_val - (long)&arch_tmpl_stub_entry + 1)
+#define ARCH_STUB_CALL_IDX ((long)&arch_tmpl_stub_call - (long)&arch_tmpl_stub_entry + 1)
+#define ARCH_STUB_INST_IDX ((long)&arch_tmpl_stub_inst - (long)&arch_tmpl_stub_entry)
+#define ARCH_STUB_END_IDX ((long)&arch_tmpl_stub_end - (long)&arch_tmpl_stub_entry)
+#define ARCH_STUB_SIZE ((long)&arch_tmpl_stub_end - (long)&arch_tmpl_stub_entry + 5)
+#endif
+
+#define ARCH_STUB_INSN_SIZE 80
+#define ARCH_STUB_INSN_MAX 20
+#define ARCH_STUB_INSN_MIN 5
+
+struct arch_djprobe_stub {
+ kprobe_opcode_t insn[ARCH_STUB_INSN_SIZE];
+ int size;
+};
+
+#define arch_serialize_cache() cpuid_eax(0)
/* trap3/1 are intr gates for kprobes. So, restore the status of IF,
* if necessary, before executing the original int3/1 (trap) handler.
diff -Narup linux-2.6.12/include/linux/kprobes.h linux-2.6.12-djprobe.3/include/linux/kprobes.h
--- linux-2.6.12/include/linux/kprobes.h 2005-06-18 04:48:29.000000000 +0900
+++ linux-2.6.12-djprobe.3/include/linux/kprobes.h 2005-08-05 00:37:03.000000000 +0900
@@ -25,6 +25,8 @@
* Rusty Russell).
* 2004-July Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
* interface to access function arguments.
+ * 2005-June Masami HIRAMATSU <hiramatu@sdl.hitachi.co.jp> added direct
+ * jump probe (djprobe) interface to reduce overhead.
*/
#include <linux/config.h>
#include <linux/list.h>
@@ -85,6 +87,40 @@ struct jprobe {
kprobe_opcode_t *entry; /* probe handling code to jump to */
};
+/* djprobe's instance (internal use)*/
+struct djprobe_instance {
+ struct djprobe *djp;
+ struct arch_djprobe_stub stub;
+
+ struct kprobe kp;
+ struct list_head list; /* list of djprobe_instances */
+ cpumask_t checked_cpus;
+};
+#define DJPI_EMPTY(djpi) (djpi->djp==NULL)
+#define DJPI_CHECKED(djpi) (cpus_equal(djpi->checked_cpus, cpu_online_map))
+
+struct djprobe;
+typedef void (*djprobe_handler_t)(struct djprobe *, struct pt_regs *);
+/*
+ * Direct Jump probe interface structure
+ */
+struct djprobe {
+#ifndef CONFIG_DJPROBE
+ struct kprobe kp;
+#endif
+ /* location of the probe point */
+ void * addr;
+
+ /* sum of length of the replacing codes */
+ int size;
+
+ /* probing handler (pre-executed) */
+ djprobe_handler_t handler;
+
+ /* pointer for instance */
+ struct djprobe_instance * inst;
+};
+
#ifdef CONFIG_KPROBES
/* Locks kprobe: irq must be disabled */
void lock_kprobes(void);
@@ -113,6 +149,31 @@ int register_jprobe(struct jprobe *p);
void unregister_jprobe(struct jprobe *p);
void jprobe_return(void);
+#ifdef CONFIG_DJPROBE
+extern int arch_prepare_djprobe_instance(struct djprobe_instance *djpi,
+ unsigned long size);
+extern int djprobe_bypass_handler(struct kprobe * kp, struct pt_regs * regs);
+extern void arch_install_djprobe_instance(struct djprobe_instance *djpi);
+extern void arch_uninstall_djprobe_instance(struct djprobe_instance *djpi);
+
+int register_djprobe(struct djprobe *p);
+void unregister_djprobe(struct djprobe *p);
+#else
+static inline int register_djprobe(struct djprobe *p)
+{
+ if (p!=NULL) {
+ p->kp.addr = p->addr;
+ p->kp.pre_handler = (kprobe_pre_handler_t)p->handler;
+ return register_kprobe(&p->kp);
+ }
+ return -EINVAL;
+}
+static inline void unregister_djprobe(struct djprobe *p)
+{
+ unregister_kprobe(&p->kp);
+}
+#endif
+
#else
static inline int kprobe_running(void)
{
@@ -135,5 +196,14 @@ static inline void unregister_jprobe(str
static inline void jprobe_return(void)
{
}
+static inline int register_djprobe(struct djprobe *p)
+{
+ return -ENOSYS;
+}
+static inline void unregister_djprobe(struct djprobe *p)
+{
+}
#endif
+
+
#endif /* _LINUX_KPROBES_H */
diff -Narup linux-2.6.12/kernel/kprobes.c linux-2.6.12-djprobe.3/kernel/kprobes.c
--- linux-2.6.12/kernel/kprobes.c 2005-06-18 04:48:29.000000000 +0900
+++ linux-2.6.12-djprobe.3/kernel/kprobes.c 2005-08-05 16:29:17.000000000 +0900
@@ -27,12 +27,16 @@
* interface to access function arguments.
* 2004-Sep Prasanna S Panchamukhi <prasanna@in.ibm.com> Changed Kprobes
* exceptions notifier to be first on the priority list.
+ * 2005-June Masami HIRAMATSU <hiramatu@sdl.hitachi.co.jp> added direct
+ * jump probe (djprobe) interface to reduce overhead.
*/
#include <linux/kprobes.h>
#include <linux/spinlock.h>
#include <linux/hash.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/stop_machine.h>
+#include <linux/hardirq.h>
#include <asm/cacheflush.h>
#include <asm/errno.h>
#include <asm/kdebug.h>
@@ -257,6 +261,147 @@ void unregister_jprobe(struct jprobe *jp
unregister_kprobe(&jp->kp);
}
+#ifdef CONFIG_DJPROBE
+/*
+ * The djprobe do not refer it's list when probe function called.
+ * This list is operated on registering and unregistering djprobe.
+ * Thus, It is not required processing speed. I decided using a 'list'.
+ */
+static DEFINE_SPINLOCK(djprobe_lock);
+static LIST_HEAD(djprobe_list);
+static int nr_instances = 0;
+
+static void free_djprobe_instance_nl(struct djprobe_instance *djpi)
+{
+ list_del(&djpi->list);
+ nr_instances -- ;
+ unregister_kprobe(&(djpi->kp));
+ kfree(djpi);
+}
+
+static int task_check_djprobe_instance(void *data)
+{
+ struct djprobe_instance *djpi = data;
+ int cpu;
+ /* all other cpus are running in stop_machine(),
+ so we can check all cpus */
+ for_each_online_cpu(cpu)
+ cpu_set(cpu, djpi->checked_cpus);
+ return 0;
+}
+
+void smp_check_safety_djprobe_instance(struct djprobe_instance *djpi)
+{
+ stop_machine_run(task_check_djprobe_instance,
+ djpi, NR_CPUS);
+ spin_lock(&djprobe_lock);
+ if (DJPI_EMPTY(djpi)) {
+ free_djprobe_instance_nl(djpi);
+ } else {
+ arch_install_djprobe_instance(djpi);
+ }
+ spin_unlock(&djprobe_lock);
+}
+
+/*Use kprobe to check safety and install */
+static int install_djprobe_instance(struct djprobe_instance *djpi)
+{
+ int ret;
+ djpi->kp.pre_handler = djprobe_bypass_handler;
+ ret = register_kprobe(&(djpi->kp));
+ cpus_clear(djpi->checked_cpus);
+ return ret;
+}
+/* Use kprobe to check safety and release */
+static void uninstall_djprobe_instance(struct djprobe_instance *djpi)
+{
+ arch_uninstall_djprobe_instance(djpi);
+ cpus_clear(djpi->checked_cpus);
+}
+
+int register_djprobe(struct djprobe * djp)
+{
+ struct djprobe_instance *djpi;
+ int ret = 0;
+
+ if (djp == NULL || djp->addr == NULL ||
+ djp->size > ARCH_STUB_INSN_MAX ||
+ djp->size < ARCH_STUB_INSN_MIN ||
+ djp->inst != NULL)
+ return -EINVAL;
+
+ if (in_interrupt())
+ return -EINVAL;
+
+ spin_lock(&djprobe_lock);
+
+ list_for_each_entry(djpi, &djprobe_list, list) {
+ if (djpi->kp.addr == djp->addr) {
+ if (!DJPI_EMPTY(djpi)) {
+ ret = -EBUSY; /* already used ... */
+ goto out;
+ }
+ djp->inst = djpi;
+ djpi->djp = djp; /*TODO: use list*/
+ cpus_clear(djpi->checked_cpus);
+ goto out;
+ }
+ }
+ /* could not find */
+ djpi = kmalloc(sizeof(struct djprobe_instance),GFP_KERNEL);
+ if (djpi == NULL) {
+ ret = -ENOMEM; /* memory allocation error */
+ goto out;
+ }
+
+ /* initialize */
+ memset(djpi, 0, sizeof(struct djprobe_instance));
+ INIT_LIST_HEAD(&djpi->list);
+ djpi->kp.addr = djp->addr;
+ arch_prepare_djprobe_instance(djpi, djp->size); /*TODO : remove size*/
+ nr_instances ++ ;
+ list_add(&djpi->list, &djprobe_list);
+
+ /* attach */
+ djp->inst = djpi;
+ djpi->djp = djp; /*TODO: use list*/
+
+ ret = install_djprobe_instance(djpi);
+ if (ret < 0) { /* failed to attach */
+ djp->inst = NULL;
+ list_del(&djpi->list);
+ nr_instances --;
+ kfree(djpi);
+ }
+out:
+ spin_unlock(&djprobe_lock);
+ if (ret == 0) smp_check_safety_djprobe_instance(djpi);
+ return ret;
+}
+
+void unregister_djprobe(struct djprobe * djp)
+{
+ struct djprobe_instance *djpi;
+ if (djp == NULL || djp->inst == NULL)
+ return ;
+
+ WARN_ON(in_interrupt());
+
+ djpi = djp->inst;
+ spin_lock(&djprobe_lock);
+ lock_kprobes();
+ djp->inst = NULL;
+ djpi->djp = NULL; /*TODO: use list*/
+ if (DJPI_EMPTY(djpi)) {
+ uninstall_djprobe_instance(djpi);
+ }
+ unlock_kprobes();
+ spin_unlock(&djprobe_lock);
+ smp_check_safety_djprobe_instance(djpi);
+}
+
+#endif /*DJPROBE*/
+
static int __init init_kprobes(void)
{
int i, err = 0;
@@ -277,3 +422,7 @@ EXPORT_SYMBOL_GPL(unregister_kprobe);
EXPORT_SYMBOL_GPL(register_jprobe);
EXPORT_SYMBOL_GPL(unregister_jprobe);
EXPORT_SYMBOL_GPL(jprobe_return);
+#ifdef CONFIG_DJPROBE
+EXPORT_SYMBOL_GPL(register_djprobe);
+EXPORT_SYMBOL_GPL(unregister_djprobe);
+#endif