This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[Patch 5/5][Djprobe]Djprobe Coexist with Kprobes
- From: Masami Hiramatsu <hiramatu at sdl dot hitachi dot co dot jp>
- To: systemtap at sources dot redhat dot com
- Cc: Satoshi Oshima <soshima at redhat dot com>, Hideo Aoki <haoki at redhat dot com>, sugita at sdl dot hitachi dot co dot jp
- Date: Thu, 29 Sep 2005 22:00:14 +0900
- Subject: [Patch 5/5][Djprobe]Djprobe Coexist with Kprobes
Hi,
This patch enables djprobe and kprobes to coexist
in the same address.
When a kprobe is inserted in the address in where
a djprobe was already inserted, the djprobe removes
a jump code and inserts a breakpoint (and it is driven
by kprobes).
When a djprobe is inserted in the address in where
a kprobe was already inserted, the djprobe behaves
like a kprobe.
After all kprobes are removed from the address, the
djprobe inserts a jump code again.
--
Masami HIRAMATSU
2nd Research Dept.
Hitachi, Ltd., Systems Development Laboratory
E-mail: hiramatu@sdl.hitachi.co.jp
arch/i386/kernel/kprobes.c | 7 ++
include/linux/kprobes.h | 2
kernel/kprobes.c | 110 ++++++++++++++++++++++++++++++++++-----------
3 files changed, 94 insertions(+), 25 deletions(-)
diff -Narup linux-2.6.13-mm1.djp.4/arch/i386/kernel/kprobes.c linux-2.6.13-mm1.djp.5/arch/i386/kernel/kprobes.c
--- linux-2.6.13-mm1.djp.4/arch/i386/kernel/kprobes.c 2005-09-28 20:14:16.000000000 +0900
+++ linux-2.6.13-mm1.djp.5/arch/i386/kernel/kprobes.c 2005-09-28 20:16:07.000000000 +0900
@@ -650,6 +650,13 @@ int __kprobes arch_prepare_djprobe_insta
return 0;
}
+ /*recover stub*/
+int __kprobes arch_recover_djprobe_instance(struct djprobe_instance *djpi)
+{
+ arch_prepare_djprobe_instance(djpi, DJPI_ARCH_SIZE(djpi));
+ djpi->stub.insn[ARCH_STUB_INST_IDX] = djpi->kp.ainsn.insn[0];
+ return 0;
+}
/* Insert "jmp" instruction into the probing point. */
void __kprobes arch_install_djprobe_instance(struct djprobe_instance *djpi)
diff -Narup linux-2.6.13-mm1.djp.4/include/linux/kprobes.h linux-2.6.13-mm1.djp.5/include/linux/kprobes.h
--- linux-2.6.13-mm1.djp.4/include/linux/kprobes.h 2005-09-28 20:14:16.000000000 +0900
+++ linux-2.6.13-mm1.djp.5/include/linux/kprobes.h 2005-09-28 20:16:07.000000000 +0900
@@ -155,6 +155,7 @@ struct djprobe_instance {
};
#define DJPI_EMPTY(djpi) (list_empty(&djpi->plist))
#define DJPI_CHECKED(djpi) (cpus_equal(djpi->checked_cpus, cpu_online_map))
+#define DJPI_FORCE_CHECK(djpi) (djpi->checked_cpus = cpu_online_map)
struct djprobe;
typedef void (*djprobe_handler_t)(struct djprobe *, struct pt_regs *);
@@ -229,6 +230,7 @@ extern int arch_prepare_djprobe_instance
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);
+extern int arch_recover_djprobe_instance(struct djprobe_instance *djpi);
#endif /* ARCH_SUPPORTS_DJPROBES */
int register_djprobe(struct djprobe *p);
diff -Narup linux-2.6.13-mm1.djp.4/kernel/kprobes.c linux-2.6.13-mm1.djp.5/kernel/kprobes.c
--- linux-2.6.13-mm1.djp.4/kernel/kprobes.c 2005-09-28 20:14:16.000000000 +0900
+++ linux-2.6.13-mm1.djp.5/kernel/kprobes.c 2005-09-28 20:16:07.000000000 +0900
@@ -77,8 +77,10 @@ struct kprobe_insn_page_list {
static struct kprobe_insn_page_list kprobe_insn_pages = {
HLIST_HEAD_INIT, MAX_INSN_SIZE};
-static struct djprobe_instance *
- __kprobes get_djprobe_instance(void *addr, int size);
+static void __kprobes djprobe_avoid_confliction(void *addr);
+static void __kprobes djprobe_recover_confliction(void *addr);
+static int __kprobes djprobe_through_handler(struct kprobe *kp,
+ struct pt_regs * regs);
/**
* __get_insn_slot() - Find a slot on an executable page for an instruction.
@@ -481,8 +483,8 @@ int __kprobes register_kprobe(struct kpr
return ret;
#ifdef ARCH_SUPPORTS_DJPROBES
if (p->pre_handler != djprobe_bypass_handler &&
- get_djprobe_instance(p->addr, 1) != NULL )
- return -EEXIST;
+ p->pre_handler != djprobe_through_handler)
+ djprobe_avoid_confliction(p->addr);
#endif
if ((ret = arch_prepare_kprobe(p)) != 0)
goto rm_kprobe;
@@ -522,6 +524,11 @@ void __kprobes unregister_kprobe(struct
cleanup_aggr_kprobe(old_p, p, flags);
else
cleanup_kprobe(p, flags);
+#ifdef ARCH_SUPPORTS_DJPROBES
+ if (p->pre_handler != djprobe_bypass_handler &&
+ p->pre_handler != djprobe_through_handler)
+ djprobe_recover_confliction(p->addr);
+#endif
} else
spin_unlock_irqrestore(&kprobe_lock, flags);
}
@@ -639,14 +646,45 @@ static inline struct djprobe_instance *
return NULL;
}
-static struct djprobe_instance *
- __kprobes get_djprobe_instance(void *addr, int size)
+static inline struct kprobe * __get_kprobe_range(void * addr, int size)
+{
+ struct kprobe *kp = NULL;
+ for (; 0 < size; size--) {
+ kp = get_kprobe((void*)((long)addr + size - 1));
+ if (kp != NULL) break;
+ }
+ return kp;
+}
+
+/* kprobe emulated djprobe */
+static int __kprobes djprobe_through_handler(struct kprobe *kp,
+ struct pt_regs * regs)
+{
+ struct djprobe_instance *djpi =
+ container_of(kp,struct djprobe_instance, kp);
+ struct djprobe * djp;
+
+ list_for_each_entry_rcu(djp, &djpi->plist, plist) {
+ if (djp->handler)
+ djp->handler(djp, regs);
+ }
+ return 0;
+}
+
+/* avoid confliction with kprobe */
+static void __kprobes djprobe_avoid_confliction(void *addr)
{
struct djprobe_instance *djpi;
spin_lock(&djprobe_lock);
- djpi = __get_djprobe_instance(addr, size);
+ djpi = __get_djprobe_instance(addr, 1);
+ if (djpi && djpi->kp.pre_handler != djprobe_through_handler) {
+ if (!DJPI_EMPTY(djpi))
+ DJPI_FORCE_CHECK(djpi); /* stop check worker */
+ /* conflict: switch a djprobe to a kprobe */
+ djpi->kp.pre_handler = djprobe_through_handler;
+ arch_uninstall_djprobe_instance(djpi);
+ }
spin_unlock(&djprobe_lock);
- return djpi;
}
#ifdef CONFIG_SMP
@@ -687,7 +725,8 @@ static void __kprobes work_check_djprobe
spin_unlock(&djprobe_lock);
}
-static inline void schedule_check_djprobe_instances(void)
+static void __kprobes
+ schedule_check_djprobe_instance(struct djprobe_instance *djpi)
{
int cpu;
struct work_struct *wk;
@@ -722,7 +761,7 @@ static int __kprobes install_djprobe_ins
{
int ret;
ret = register_kprobe(&(djpi->kp));
- if (ret == 0) {
+ if (ret == 0 && djpi->kp.pre_handler != djprobe_through_handler) {
__install_djprobe_instance(djpi);
}
return ret;
@@ -731,15 +770,38 @@ static int __kprobes install_djprobe_ins
/* Use kprobe to check safety and release */
static void __kprobes uninstall_djprobe_instance(struct djprobe_instance *djpi)
{
- arch_uninstall_djprobe_instance(djpi);
- __uninstall_djprobe_instance(djpi);
+ if (djpi->kp.pre_handler != djprobe_through_handler) {
+ arch_uninstall_djprobe_instance(djpi);
+ __uninstall_djprobe_instance(djpi);
+ } else
+ __free_djprobe_instance(djpi);
}
-int __kprobes register_djprobe(struct djprobe * djp)
+/* recover confliction with kprobe */
+static void __kprobes djprobe_recover_confliction(void *addr)
{
struct djprobe_instance *djpi;
struct kprobe *kp;
- int ret = 0, i;
+ spin_lock(&djprobe_lock);
+ djpi = __get_djprobe_instance(addr, 1);
+ if (djpi) {
+ kp = __get_kprobe_range(djpi->kp.addr, DJPI_ARCH_SIZE(djpi));
+ if (kp->addr == djpi->kp.addr) {
+ unsigned long flags;
+ spin_lock_irqsave(&kprobe_lock, flags);
+ arch_recover_djprobe_instance(djpi); /*recover stub*/
+ djpi->kp.pre_handler = djprobe_bypass_handler;
+ __install_djprobe_instance(djpi);
+ spin_unlock_irqrestore(&kprobe_lock, flags);
+ }
+ }
+ spin_unlock(&djprobe_lock);
+}
+
+int __kprobes register_djprobe(struct djprobe * djp)
+{
+ struct djprobe_instance *djpi;
+ int ret = 0;
if (djp == NULL || djp->addr == NULL ||
djp->size > ARCH_STUB_INSN_MAX ||
@@ -764,14 +826,6 @@ int __kprobes register_djprobe(struct dj
goto out;
}
}
- /* check confliction with kprobes */
- for ( i=0; i < djp->size; i++) {
- kp = get_kprobe((void*)((long)djp->addr+i));
- if (kp != NULL) {
- ret = -EEXIST; /* a kprobes were inserted */
- goto out;
- }
- }
/* make a new instance */
djpi = kmalloc(sizeof(struct djprobe_instance),GFP_KERNEL);
if (djpi == NULL) {
@@ -794,9 +848,15 @@ int __kprobes register_djprobe(struct dj
ret = -ENOMEM; /* memory allocation error */
goto out;
}
- djpi->kp.pre_handler = djprobe_bypass_handler;
- arch_prepare_djprobe_instance(djpi, djp->size); /*TODO : remove size*/
-
+ if (__get_kprobe_range(djp->addr, djp->size) != NULL) {
+ /* conflict with kprobes */
+ djpi->kp.pre_handler = djprobe_through_handler;
+ DJPI_ARCH_SIZE(djpi) = djp->size;
+ DJPI_FORCE_CHECK(djpi); /* assume to be checked */
+ } else {
+ djpi->kp.pre_handler = djprobe_bypass_handler;
+ arch_prepare_djprobe_instance(djpi, djp->size); /*TODO : remove size*/
+ }
ret = install_djprobe_instance(djpi);
if (ret < 0) { /* failed to install */
djp->inst = NULL;