kprobes: add kprobe_insn_mutex and cleanup arch_remove_kprobe()
Masami Hiramatsu [Tue, 6 Jan 2009 22:41:50 +0000 (14:41 -0800)]
Add kprobe_insn_mutex for protecting kprobe_insn_pages hlist, and remove
kprobe_mutex from architecture dependent code.

This allows us to call arch_remove_kprobe() (and free_insn_slot) while
holding kprobe_mutex.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Acked-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

arch/arm/kernel/kprobes.c
arch/ia64/kernel/kprobes.c
arch/powerpc/kernel/kprobes.c
arch/s390/kernel/kprobes.c
arch/x86/kernel/kprobes.c
include/linux/kprobes.h
kernel/kprobes.c

index 3f9abe0..f692efd 100644 (file)
@@ -92,9 +92,7 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
 void __kprobes arch_remove_kprobe(struct kprobe *p)
 {
        if (p->ainsn.insn) {
-               mutex_lock(&kprobe_mutex);
                free_insn_slot(p->ainsn.insn, 0);
-               mutex_unlock(&kprobe_mutex);
                p->ainsn.insn = NULL;
        }
 }
index f07688d..097b84d 100644 (file)
@@ -670,9 +670,11 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
 
 void __kprobes arch_remove_kprobe(struct kprobe *p)
 {
-       mutex_lock(&kprobe_mutex);
-       free_insn_slot(p->ainsn.insn, p->ainsn.inst_flag & INST_FLAG_BOOSTABLE);
-       mutex_unlock(&kprobe_mutex);
+       if (p->ainsn.insn) {
+               free_insn_slot(p->ainsn.insn,
+                              p->ainsn.inst_flag & INST_FLAG_BOOSTABLE);
+               p->ainsn.insn = NULL;
+       }
 }
 /*
  * We are resuming execution after a single step fault, so the pt_regs
index de79915..989edcd 100644 (file)
@@ -96,9 +96,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
 
 void __kprobes arch_remove_kprobe(struct kprobe *p)
 {
-       mutex_lock(&kprobe_mutex);
-       free_insn_slot(p->ainsn.insn, 0);
-       mutex_unlock(&kprobe_mutex);
+       if (p->ainsn.insn) {
+               free_insn_slot(p->ainsn.insn, 0);
+               p->ainsn.insn = NULL;
+       }
 }
 
 static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
index 569079e..9b92856 100644 (file)
@@ -218,9 +218,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
 
 void __kprobes arch_remove_kprobe(struct kprobe *p)
 {
-       mutex_lock(&kprobe_mutex);
-       free_insn_slot(p->ainsn.insn, 0);
-       mutex_unlock(&kprobe_mutex);
+       if (p->ainsn.insn) {
+               free_insn_slot(p->ainsn.insn, 0);
+               p->ainsn.insn = NULL;
+       }
 }
 
 static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
index 6c27679..eead6f8 100644 (file)
@@ -376,9 +376,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
 
 void __kprobes arch_remove_kprobe(struct kprobe *p)
 {
-       mutex_lock(&kprobe_mutex);
-       free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1));
-       mutex_unlock(&kprobe_mutex);
+       if (p->ainsn.insn) {
+               free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1));
+               p->ainsn.insn = NULL;
+       }
 }
 
 static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
index 497b1d1..b93e44c 100644 (file)
@@ -201,7 +201,6 @@ static inline int init_test_probes(void)
 }
 #endif /* CONFIG_KPROBES_SANITY_TEST */
 
-extern struct mutex kprobe_mutex;
 extern int arch_prepare_kprobe(struct kprobe *p);
 extern void arch_arm_kprobe(struct kprobe *p);
 extern void arch_disarm_kprobe(struct kprobe *p);
index 3afd354..29e8792 100644 (file)
@@ -69,7 +69,7 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
 /* NOTE: change this value only with kprobe_mutex held */
 static bool kprobe_enabled;
 
-DEFINE_MUTEX(kprobe_mutex);            /* Protects kprobe_table */
+static DEFINE_MUTEX(kprobe_mutex);     /* Protects kprobe_table */
 static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL;
 static struct {
        spinlock_t lock ____cacheline_aligned_in_smp;
@@ -115,6 +115,7 @@ enum kprobe_slot_state {
        SLOT_USED = 2,
 };
 
+static DEFINE_MUTEX(kprobe_insn_mutex);        /* Protects kprobe_insn_pages */
 static struct hlist_head kprobe_insn_pages;
 static int kprobe_garbage_slots;
 static int collect_garbage_slots(void);
@@ -144,10 +145,10 @@ loop_end:
 }
 
 /**
- * get_insn_slot() - Find a slot on an executable page for an instruction.
+ * __get_insn_slot() - Find a slot on an executable page for an instruction.
  * We allocate an executable page if there's no room on existing ones.
  */
-kprobe_opcode_t __kprobes *get_insn_slot(void)
+static kprobe_opcode_t __kprobes *__get_insn_slot(void)
 {
        struct kprobe_insn_page *kip;
        struct hlist_node *pos;
@@ -196,6 +197,15 @@ kprobe_opcode_t __kprobes *get_insn_slot(void)
        return kip->insns;
 }
 
+kprobe_opcode_t __kprobes *get_insn_slot(void)
+{
+       kprobe_opcode_t *ret;
+       mutex_lock(&kprobe_insn_mutex);
+       ret = __get_insn_slot();
+       mutex_unlock(&kprobe_insn_mutex);
+       return ret;
+}
+
 /* Return 1 if all garbages are collected, otherwise 0. */
 static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
 {
@@ -226,9 +236,13 @@ static int __kprobes collect_garbage_slots(void)
 {
        struct kprobe_insn_page *kip;
        struct hlist_node *pos, *next;
+       int safety;
 
        /* Ensure no-one is preepmted on the garbages */
-       if (check_safety() != 0)
+       mutex_unlock(&kprobe_insn_mutex);
+       safety = check_safety();
+       mutex_lock(&kprobe_insn_mutex);
+       if (safety != 0)
                return -EAGAIN;
 
        hlist_for_each_entry_safe(kip, pos, next, &kprobe_insn_pages, hlist) {
@@ -251,6 +265,7 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
        struct kprobe_insn_page *kip;
        struct hlist_node *pos;
 
+       mutex_lock(&kprobe_insn_mutex);
        hlist_for_each_entry(kip, pos, &kprobe_insn_pages, hlist) {
                if (kip->insns <= slot &&
                    slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) {
@@ -267,6 +282,8 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
 
        if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE)
                collect_garbage_slots();
+
+       mutex_unlock(&kprobe_insn_mutex);
 }
 #endif