Commit eab5cc1e authored by Prasanna S. Panchamukhi's avatar Prasanna S. Panchamukhi Committed by Linus Torvalds

[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: default avatarPrasanna S Panchamukhi <prasanna@in.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 95e5e682
...@@ -62,10 +62,14 @@ static inline int is_IF_modifier(kprobe_opcode_t opcode) ...@@ -62,10 +62,14 @@ static inline int is_IF_modifier(kprobe_opcode_t opcode)
int arch_prepare_kprobe(struct kprobe *p) int arch_prepare_kprobe(struct kprobe *p)
{ {
memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
return 0; 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) void arch_remove_kprobe(struct kprobe *p)
{ {
} }
......
...@@ -45,13 +45,19 @@ static struct pt_regs jprobe_saved_regs; ...@@ -45,13 +45,19 @@ static struct pt_regs jprobe_saved_regs;
int arch_prepare_kprobe(struct kprobe *p) int arch_prepare_kprobe(struct kprobe *p)
{ {
memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); kprobe_opcode_t insn = *p->addr;
if (IS_MTMSRD(p->ainsn.insn[0]) || IS_RFID(p->ainsn.insn[0]))
if (IS_MTMSRD(insn) || IS_RFID(insn))
/* cannot put bp on RFID/MTMSRD */ /* cannot put bp on RFID/MTMSRD */
return 1; return 1;
return 0; 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) void arch_remove_kprobe(struct kprobe *p)
{ {
} }
......
...@@ -39,10 +39,14 @@ ...@@ -39,10 +39,14 @@
*/ */
int arch_prepare_kprobe(struct kprobe *p) 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[0] = *p->addr;
p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2; p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2;
return 0;
} }
void arch_remove_kprobe(struct kprobe *p) void arch_remove_kprobe(struct kprobe *p)
......
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/kdebug.h> #include <asm/kdebug.h>
static DECLARE_MUTEX(kprobe_mutex);
/* kprobe_status settings */ /* kprobe_status settings */
#define KPROBE_HIT_ACTIVE 0x00000001 #define KPROBE_HIT_ACTIVE 0x00000001
#define KPROBE_HIT_SS 0x00000002 #define KPROBE_HIT_SS 0x00000002
...@@ -75,17 +77,25 @@ static inline int is_IF_modifier(kprobe_opcode_t *insn) ...@@ -75,17 +77,25 @@ static inline int is_IF_modifier(kprobe_opcode_t *insn)
int arch_prepare_kprobe(struct kprobe *p) int arch_prepare_kprobe(struct kprobe *p)
{ {
/* insn: must be on special executable page on x86_64. */ /* insn: must be on special executable page on x86_64. */
up(&kprobe_mutex);
p->ainsn.insn = get_insn_slot(); p->ainsn.insn = get_insn_slot();
down(&kprobe_mutex);
if (!p->ainsn.insn) { if (!p->ainsn.insn) {
return -ENOMEM; return -ENOMEM;
} }
memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE);
return 0; 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) void arch_remove_kprobe(struct kprobe *p)
{ {
up(&kprobe_mutex);
free_insn_slot(p->ainsn.insn); free_insn_slot(p->ainsn.insn);
down(&kprobe_mutex);
} }
static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
...@@ -425,12 +435,12 @@ static kprobe_opcode_t *get_insn_slot(void) ...@@ -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.*/ /* 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) { if (!kip) {
return NULL; return NULL;
} }
kip->insns = (kprobe_opcode_t*) __vmalloc(PAGE_SIZE, 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) { if (!kip->insns) {
kfree(kip); kfree(kip);
return NULL; return NULL;
......
...@@ -95,6 +95,7 @@ static inline int kprobe_running(void) ...@@ -95,6 +95,7 @@ static inline int kprobe_running(void)
} }
extern int arch_prepare_kprobe(struct kprobe *p); 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 arch_remove_kprobe(struct kprobe *p);
extern void show_registers(struct pt_regs *regs); extern void show_registers(struct pt_regs *regs);
......
...@@ -76,18 +76,19 @@ struct kprobe *get_kprobe(void *addr) ...@@ -76,18 +76,19 @@ struct kprobe *get_kprobe(void *addr)
int register_kprobe(struct kprobe *p) int register_kprobe(struct kprobe *p)
{ {
int ret = 0; 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); spin_lock_irqsave(&kprobe_lock, flags);
INIT_HLIST_NODE(&p->hlist); INIT_HLIST_NODE(&p->hlist);
if (get_kprobe(p->addr)) { if (get_kprobe(p->addr)) {
ret = -EEXIST; ret = -EEXIST;
goto out; goto out;
} }
arch_copy_kprobe(p);
if ((ret = arch_prepare_kprobe(p)) != 0) {
goto out;
}
hlist_add_head(&p->hlist, hlist_add_head(&p->hlist,
&kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]); &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
...@@ -97,14 +98,16 @@ int register_kprobe(struct kprobe *p) ...@@ -97,14 +98,16 @@ int register_kprobe(struct kprobe *p)
(unsigned long) p->addr + sizeof(kprobe_opcode_t)); (unsigned long) p->addr + sizeof(kprobe_opcode_t));
out: out:
spin_unlock_irqrestore(&kprobe_lock, flags); spin_unlock_irqrestore(&kprobe_lock, flags);
if (ret == -EEXIST)
arch_remove_kprobe(p);
return ret; return ret;
} }
void unregister_kprobe(struct kprobe *p) void unregister_kprobe(struct kprobe *p)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&kprobe_lock, flags);
arch_remove_kprobe(p); arch_remove_kprobe(p);
spin_lock_irqsave(&kprobe_lock, flags);
*p->addr = p->opcode; *p->addr = p->opcode;
hlist_del(&p->hlist); hlist_del(&p->hlist);
flush_icache_range((unsigned long) p->addr, flush_icache_range((unsigned long) p->addr,
......
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