This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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;


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]