Commit 12941560 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Linus Torvalds

kprobes: add kprobe_insn_mutex and cleanup arch_remove_kprobe()

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: default avatarMasami Hiramatsu <mhiramat@redhat.com>
Acked-by: default avatarAnanth 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent a06f6211
...@@ -92,9 +92,7 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) ...@@ -92,9 +92,7 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
void __kprobes arch_remove_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p)
{ {
if (p->ainsn.insn) { if (p->ainsn.insn) {
mutex_lock(&kprobe_mutex);
free_insn_slot(p->ainsn.insn, 0); free_insn_slot(p->ainsn.insn, 0);
mutex_unlock(&kprobe_mutex);
p->ainsn.insn = NULL; p->ainsn.insn = NULL;
} }
} }
......
...@@ -670,9 +670,11 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) ...@@ -670,9 +670,11 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
void __kprobes arch_remove_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p)
{ {
mutex_lock(&kprobe_mutex); if (p->ainsn.insn) {
free_insn_slot(p->ainsn.insn, p->ainsn.inst_flag & INST_FLAG_BOOSTABLE); free_insn_slot(p->ainsn.insn,
mutex_unlock(&kprobe_mutex); p->ainsn.inst_flag & INST_FLAG_BOOSTABLE);
p->ainsn.insn = NULL;
}
} }
/* /*
* We are resuming execution after a single step fault, so the pt_regs * We are resuming execution after a single step fault, so the pt_regs
......
...@@ -96,9 +96,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) ...@@ -96,9 +96,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
void __kprobes arch_remove_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p)
{ {
mutex_lock(&kprobe_mutex); if (p->ainsn.insn) {
free_insn_slot(p->ainsn.insn, 0); free_insn_slot(p->ainsn.insn, 0);
mutex_unlock(&kprobe_mutex); p->ainsn.insn = NULL;
}
} }
static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
......
...@@ -218,9 +218,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) ...@@ -218,9 +218,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
void __kprobes arch_remove_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p)
{ {
mutex_lock(&kprobe_mutex); if (p->ainsn.insn) {
free_insn_slot(p->ainsn.insn, 0); free_insn_slot(p->ainsn.insn, 0);
mutex_unlock(&kprobe_mutex); p->ainsn.insn = NULL;
}
} }
static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
......
...@@ -376,9 +376,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) ...@@ -376,9 +376,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)
void __kprobes arch_remove_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p)
{ {
mutex_lock(&kprobe_mutex); if (p->ainsn.insn) {
free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1)); free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1));
mutex_unlock(&kprobe_mutex); p->ainsn.insn = NULL;
}
} }
static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
......
...@@ -201,7 +201,6 @@ static inline int init_test_probes(void) ...@@ -201,7 +201,6 @@ static inline int init_test_probes(void)
} }
#endif /* CONFIG_KPROBES_SANITY_TEST */ #endif /* CONFIG_KPROBES_SANITY_TEST */
extern struct mutex kprobe_mutex;
extern int arch_prepare_kprobe(struct kprobe *p); extern int arch_prepare_kprobe(struct kprobe *p);
extern void arch_arm_kprobe(struct kprobe *p); extern void arch_arm_kprobe(struct kprobe *p);
extern void arch_disarm_kprobe(struct kprobe *p); extern void arch_disarm_kprobe(struct kprobe *p);
......
...@@ -69,7 +69,7 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; ...@@ -69,7 +69,7 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
/* NOTE: change this value only with kprobe_mutex held */ /* NOTE: change this value only with kprobe_mutex held */
static bool kprobe_enabled; 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 DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL;
static struct { static struct {
spinlock_t lock ____cacheline_aligned_in_smp; spinlock_t lock ____cacheline_aligned_in_smp;
...@@ -115,6 +115,7 @@ enum kprobe_slot_state { ...@@ -115,6 +115,7 @@ enum kprobe_slot_state {
SLOT_USED = 2, SLOT_USED = 2,
}; };
static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_pages */
static struct hlist_head kprobe_insn_pages; static struct hlist_head kprobe_insn_pages;
static int kprobe_garbage_slots; static int kprobe_garbage_slots;
static int collect_garbage_slots(void); static int collect_garbage_slots(void);
...@@ -144,10 +145,10 @@ static int __kprobes check_safety(void) ...@@ -144,10 +145,10 @@ static int __kprobes check_safety(void)
} }
/** /**
* 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. * 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 kprobe_insn_page *kip;
struct hlist_node *pos; struct hlist_node *pos;
...@@ -196,6 +197,15 @@ kprobe_opcode_t __kprobes *get_insn_slot(void) ...@@ -196,6 +197,15 @@ kprobe_opcode_t __kprobes *get_insn_slot(void)
return kip->insns; 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. */ /* Return 1 if all garbages are collected, otherwise 0. */
static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx) static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
{ {
...@@ -226,9 +236,13 @@ static int __kprobes collect_garbage_slots(void) ...@@ -226,9 +236,13 @@ static int __kprobes collect_garbage_slots(void)
{ {
struct kprobe_insn_page *kip; struct kprobe_insn_page *kip;
struct hlist_node *pos, *next; struct hlist_node *pos, *next;
int safety;
/* Ensure no-one is preepmted on the garbages */ /* 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; return -EAGAIN;
hlist_for_each_entry_safe(kip, pos, next, &kprobe_insn_pages, hlist) { 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) ...@@ -251,6 +265,7 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
struct kprobe_insn_page *kip; struct kprobe_insn_page *kip;
struct hlist_node *pos; struct hlist_node *pos;
mutex_lock(&kprobe_insn_mutex);
hlist_for_each_entry(kip, pos, &kprobe_insn_pages, hlist) { hlist_for_each_entry(kip, pos, &kprobe_insn_pages, hlist) {
if (kip->insns <= slot && if (kip->insns <= slot &&
slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) { slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) {
...@@ -267,6 +282,8 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty) ...@@ -267,6 +282,8 @@ void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE) if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE)
collect_garbage_slots(); collect_garbage_slots();
mutex_unlock(&kprobe_insn_mutex);
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment