This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[RFC][PATCH 1/4][kprobe](djprobe) generalize the length of the instruction
- From: Masami Hiramatsu <masami dot hiramatsu dot pt at hitachi dot com>
- To: Masami Hiramatsu <masami dot hiramatsu dot pt at hitachi dot com>, "Keshavamurthy, Anil S" <anil dot s dot keshavamurthy at intel dot com>, Ingo Molnar <mingo at redhat dot com>, SystemTAP <systemtap at sources dot redhat dot com>, Ananth N Mavinakayanahalli <ananth at in dot ibm dot com>, Prasanna S Panchamukhi <prasanna at in dot ibm dot com>
- Cc: Satoshi Oshima <soshima at redhat dot com>, Hideo Aoki <haoki at redhat dot com>, Yumiko Sugita <yumiko dot sugita dot yf at hitachi dot com>, Jim Keniston <jkenisto at us dot ibm dot com>, Martin Bligh <mbligh at google dot com>, Greg Kroah-Hartman <gregkh at suse dot de>
- Date: Tue, 21 Nov 2006 15:53:09 +0900
- Subject: [RFC][PATCH 1/4][kprobe](djprobe) generalize the length of the instruction
- Organization: Systems Development Lab., Hitachi, Ltd., Japan
- References: <4562A150.2030606@hitachi.com>
Hi,
This patch generalizes get/free_insn_slot() to manage multiple length
instruction slots, because the djprobe (Direct Jump Optimized kprobe)
needs longer instruction slots than kprobes.
Thanks,
---
Masami HIRAMATSU
Linux Technology Center
Hitachi, Ltd., Systems Development Laboratory
E-mail: masami.hiramatsu.pt@hitachi.com
---
include/linux/kprobes.h | 6 +++
kernel/kprobes.c | 85 ++++++++++++++++++++++++++++++------------------
2 files changed, 60 insertions(+), 31 deletions(-)
Index: linux-2.6.19-rc5-mm2/include/linux/kprobes.h
===================================================================
--- linux-2.6.19-rc5-mm2.orig/include/linux/kprobes.h
+++ linux-2.6.19-rc5-mm2/include/linux/kprobes.h
@@ -157,6 +157,12 @@ struct kretprobe_instance {
struct task_struct *task;
};
+struct kprobe_insn_page_list {
+ struct hlist_head list;
+ int insn_size; /* size of an instruction slot */
+ int nr_garbage;
+};
+
extern spinlock_t kretprobe_lock;
extern struct mutex kprobe_mutex;
extern int arch_prepare_kprobe(struct kprobe *p);
Index: linux-2.6.19-rc5-mm2/kernel/kprobes.c
===================================================================
--- linux-2.6.19-rc5-mm2.orig/kernel/kprobes.c
+++ linux-2.6.19-rc5-mm2/kernel/kprobes.c
@@ -77,19 +77,23 @@ static struct notifier_block kprobe_page
* stepping on the instruction on a vmalloced/kmalloced/data page
* is a recipe for disaster
*/
-#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
+#define INSNS_PER_PAGE(size) (PAGE_SIZE/(size * sizeof(kprobe_opcode_t)))
struct kprobe_insn_page {
struct hlist_node hlist;
kprobe_opcode_t *insns; /* Page of instruction slots */
- char slot_used[INSNS_PER_PAGE];
int nused;
int ngarbage;
+ char slot_used[1];
};
-static struct hlist_head kprobe_insn_pages;
-static int kprobe_garbage_slots;
-static int collect_garbage_slots(void);
+static struct kprobe_insn_page_list kprobe_insn_pages = {
+ .list = HLIST_HEAD_INIT,
+ .insn_size = MAX_INSN_SIZE,
+ .nr_garbage = 0,
+};
+
+static int collect_garbage_slots(struct kprobe_insn_page_list *plist);
static int __kprobes check_safety(void)
{
@@ -116,37 +120,41 @@ 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)
+kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_page_list *plist)
{
struct kprobe_insn_page *kip;
struct hlist_node *pos;
+ int max_insn = INSNS_PER_PAGE(plist->insn_size);
retry:
- hlist_for_each(pos, &kprobe_insn_pages) {
+ hlist_for_each(pos, &plist->list) {
kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
- if (kip->nused < INSNS_PER_PAGE) {
+ if (kip->nused < max_insn) {
int i;
- for (i = 0; i < INSNS_PER_PAGE; i++) {
+ for (i = 0; i < max_insn; i++) {
if (!kip->slot_used[i]) {
kip->slot_used[i] = 1;
kip->nused++;
- return kip->insns + (i * MAX_INSN_SIZE);
+ return kip->insns +
+ (i * plist->insn_size);
}
}
/* Surprise! No unused slots. Fix kip->nused. */
- kip->nused = INSNS_PER_PAGE;
+ kip->nused = max_insn;
}
}
/* If there are any garbage slots, collect it and try again. */
- if (kprobe_garbage_slots && collect_garbage_slots() == 0) {
+ if (plist->nr_garbage > 0 && collect_garbage_slots(plist) == 0) {
goto retry;
}
/* All out of space. Need to allocate a new page. Use slot 0. */
- kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
+ kip = kmalloc(sizeof(struct kprobe_insn_page) +
+ sizeof(char) * (max_insn - 1),
+ GFP_KERNEL);
if (!kip) {
return NULL;
}
@@ -162,8 +170,8 @@ kprobe_opcode_t __kprobes *get_insn_slot
return NULL;
}
INIT_HLIST_NODE(&kip->hlist);
- hlist_add_head(&kip->hlist, &kprobe_insn_pages);
- memset(kip->slot_used, 0, INSNS_PER_PAGE);
+ hlist_add_head(&kip->hlist, &plist->list);
+ memset(kip->slot_used, 0, max_insn);
kip->slot_used[0] = 1;
kip->nused = 1;
kip->ngarbage = 0;
@@ -171,7 +179,8 @@ kprobe_opcode_t __kprobes *get_insn_slot
}
/* 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_list *plist,
+ struct kprobe_insn_page *kip, int idx)
{
kip->slot_used[idx] = 0;
kip->nused--;
@@ -183,10 +192,10 @@ static int __kprobes collect_one_slot(st
* next time somebody inserts a probe.
*/
hlist_del(&kip->hlist);
- if (hlist_empty(&kprobe_insn_pages)) {
+ if (hlist_empty(&plist->list)) {
INIT_HLIST_NODE(&kip->hlist);
hlist_add_head(&kip->hlist,
- &kprobe_insn_pages);
+ &plist->list);
} else {
module_free(NULL, kip->insns);
kfree(kip);
@@ -196,54 +205,68 @@ static int __kprobes collect_one_slot(st
return 0;
}
-static int __kprobes collect_garbage_slots(void)
+static int __kprobes
+ collect_garbage_slots(struct kprobe_insn_page_list *plist)
{
struct kprobe_insn_page *kip;
struct hlist_node *pos, *next;
+ int max_insn = INSNS_PER_PAGE(plist->insn_size);
/* Ensure no-one is preepmted on the garbages */
if (check_safety() != 0)
return -EAGAIN;
- hlist_for_each_safe(pos, next, &kprobe_insn_pages) {
+ hlist_for_each_safe(pos, next, &plist->list) {
int i;
kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
if (kip->ngarbage == 0)
continue;
kip->ngarbage = 0; /* we will collect all garbages */
- for (i = 0; i < INSNS_PER_PAGE; i++) {
+ for (i = 0; i < max_insn; i++) {
if (kip->slot_used[i] == -1 &&
- collect_one_slot(kip, i))
+ collect_one_slot(plist, kip, i))
break;
}
}
- kprobe_garbage_slots = 0;
+ plist->nr_garbage = 0;
return 0;
}
-void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
+void __kprobes __free_insn_slot(struct kprobe_insn_page_list *plist,
+ kprobe_opcode_t * slot, int dirty)
{
struct kprobe_insn_page *kip;
struct hlist_node *pos;
+ int max_insn = INSNS_PER_PAGE(plist->insn_size);
- hlist_for_each(pos, &kprobe_insn_pages) {
+ hlist_for_each(pos, &plist->list) {
kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
if (kip->insns <= slot &&
- slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) {
- int i = (slot - kip->insns) / MAX_INSN_SIZE;
+ slot < kip->insns + (max_insn * plist->insn_size)) {
+ int i = (slot - kip->insns) / plist->insn_size;
if (dirty) {
kip->slot_used[i] = -1;
kip->ngarbage++;
} else {
- collect_one_slot(kip, i);
+ collect_one_slot(plist, kip, i);
}
break;
}
}
- if (dirty && (++kprobe_garbage_slots > INSNS_PER_PAGE)) {
- collect_garbage_slots();
+ if (dirty && (++plist->nr_garbage > max_insn)) {
+ collect_garbage_slots(plist);
}
}
+
+kprobe_opcode_t __kprobes *get_insn_slot(void)
+{
+ return __get_insn_slot(&kprobe_insn_pages);
+}
+
+void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
+{
+ __free_insn_slot(&kprobe_insn_pages, slot, dirty);
+}
#endif
/* We have preemption disabled.. so it is safe to use __ versions */