summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrasanna S. Panchamukhi <prasanna@in.ibm.com>2005-01-20 16:00:15 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-20 16:00:15 -0800
commiteab5cc1e76bbb0320488cffe65185c20918a3d4a (patch)
tree15ec6f96423c3f16e517f9e7bea25a1aa1a19f66
parent95e5e682f1a4cf716c89c9f9e6c01da898fb1f9c (diff)
[PATCH] kprobes: x86_64 memory allocation changes
Minor changes to the kprobes code to provide memory allocation for x86_64 architecture outside kprobes spin lock. Signed-off-by: Prasanna S Panchamukhi <prasanna@in.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/i386/kernel/kprobes.c6
-rw-r--r--arch/ppc64/kernel/kprobes.c10
-rw-r--r--arch/sparc64/kernel/kprobes.c6
-rw-r--r--arch/x86_64/kernel/kprobes.c16
-rw-r--r--include/linux/kprobes.h1
-rw-r--r--kernel/kprobes.c13
6 files changed, 40 insertions, 12 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c
index 50e3fedc5372..f74b755704c4 100644
--- a/arch/i386/kernel/kprobes.c
+++ b/arch/i386/kernel/kprobes.c
@@ -62,10 +62,14 @@ static inline int is_IF_modifier(kprobe_opcode_t opcode)
int arch_prepare_kprobe(struct kprobe *p)
{
- memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
return 0;
}
+void arch_copy_kprobe(struct kprobe *p)
+{
+ memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+}
+
void arch_remove_kprobe(struct kprobe *p)
{
}
diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c
index 677f6272fc29..522e7742ac00 100644
--- a/arch/ppc64/kernel/kprobes.c
+++ b/arch/ppc64/kernel/kprobes.c
@@ -45,13 +45,19 @@ static struct pt_regs jprobe_saved_regs;
int arch_prepare_kprobe(struct kprobe *p)
{
- memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
- if (IS_MTMSRD(p->ainsn.insn[0]) || IS_RFID(p->ainsn.insn[0]))
+ kprobe_opcode_t insn = *p->addr;
+
+ if (IS_MTMSRD(insn) || IS_RFID(insn))
/* cannot put bp on RFID/MTMSRD */
return 1;
return 0;
}
+void arch_copy_kprobe(struct kprobe *p)
+{
+ memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+}
+
void arch_remove_kprobe(struct kprobe *p)
{
}
diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c
index 801ab91a739f..7d97138f5d2e 100644
--- a/arch/sparc64/kernel/kprobes.c
+++ b/arch/sparc64/kernel/kprobes.c
@@ -40,9 +40,13 @@
int arch_prepare_kprobe(struct kprobe *p)
{
+ return 0;
+}
+
+void arch_copy_kprobe(struct kprobe *p)
+{
p->ainsn.insn[0] = *p->addr;
p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2;
- return 0;
}
void arch_remove_kprobe(struct kprobe *p)
diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c
index dfef3128ff63..131d24ef9a63 100644
--- a/arch/x86_64/kernel/kprobes.c
+++ b/arch/x86_64/kernel/kprobes.c
@@ -39,6 +39,8 @@
#include <asm/pgtable.h>
#include <asm/kdebug.h>
+static DECLARE_MUTEX(kprobe_mutex);
+
/* kprobe_status settings */
#define KPROBE_HIT_ACTIVE 0x00000001
#define KPROBE_HIT_SS 0x00000002
@@ -75,17 +77,25 @@ static inline int is_IF_modifier(kprobe_opcode_t *insn)
int arch_prepare_kprobe(struct kprobe *p)
{
/* insn: must be on special executable page on x86_64. */
+ up(&kprobe_mutex);
p->ainsn.insn = get_insn_slot();
+ down(&kprobe_mutex);
if (!p->ainsn.insn) {
return -ENOMEM;
}
- memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE);
return 0;
}
+void arch_copy_kprobe(struct kprobe *p)
+{
+ memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE);
+}
+
void arch_remove_kprobe(struct kprobe *p)
{
+ up(&kprobe_mutex);
free_insn_slot(p->ainsn.insn);
+ down(&kprobe_mutex);
}
static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
@@ -425,12 +435,12 @@ static kprobe_opcode_t *get_insn_slot(void)
}
/* All out of space. Need to allocate a new page. Use slot 0.*/
- kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_ATOMIC);
+ kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
if (!kip) {
return NULL;
}
kip->insns = (kprobe_opcode_t*) __vmalloc(PAGE_SIZE,
- GFP_ATOMIC|__GFP_HIGHMEM, __pgprot(__PAGE_KERNEL_EXEC));
+ GFP_KERNEL|__GFP_HIGHMEM, __pgprot(__PAGE_KERNEL_EXEC));
if (!kip->insns) {
kfree(kip);
return NULL;
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 3177a7ffe573..f20c163de4f5 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -95,6 +95,7 @@ static inline int kprobe_running(void)
}
extern int arch_prepare_kprobe(struct kprobe *p);
+extern void arch_copy_kprobe(struct kprobe *p);
extern void arch_remove_kprobe(struct kprobe *p);
extern void show_registers(struct pt_regs *regs);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index cc6f72585f1e..4a331aed0866 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -76,18 +76,19 @@ struct kprobe *get_kprobe(void *addr)
int register_kprobe(struct kprobe *p)
{
int ret = 0;
- unsigned long flags;
+ unsigned long flags = 0;
+ if ((ret = arch_prepare_kprobe(p)) != 0) {
+ goto out;
+ }
spin_lock_irqsave(&kprobe_lock, flags);
INIT_HLIST_NODE(&p->hlist);
if (get_kprobe(p->addr)) {
ret = -EEXIST;
goto out;
}
+ arch_copy_kprobe(p);
- if ((ret = arch_prepare_kprobe(p)) != 0) {
- goto out;
- }
hlist_add_head(&p->hlist,
&kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
@@ -97,14 +98,16 @@ int register_kprobe(struct kprobe *p)
(unsigned long) p->addr + sizeof(kprobe_opcode_t));
out:
spin_unlock_irqrestore(&kprobe_lock, flags);
+ if (ret == -EEXIST)
+ arch_remove_kprobe(p);
return ret;
}
void unregister_kprobe(struct kprobe *p)
{
unsigned long flags;
- spin_lock_irqsave(&kprobe_lock, flags);
arch_remove_kprobe(p);
+ spin_lock_irqsave(&kprobe_lock, flags);
*p->addr = p->opcode;
hlist_del(&p->hlist);
flush_icache_range((unsigned long) p->addr,