2013-08-21 12:01:47

by Heiko Carstens

[permalink] [raw]
Subject: [PATCH 0/3] kprobes: add new dma insn slot cache for s390

The current kpropes insn caches allocate memory areas for insn slots with
module_alloc(). The assumption is that the kernel image and module area
are both within the same +/- 2GB memory area.
This however is not true for s390 where the kernel image resides within
the first 2GB (DMA memory area), but the module area is far away in the
vmalloc area, usually somewhere close below the 4TB area.

For new pc relative instructions s390 needs insn slots that are within
+/- 2GB of each area. That way we can patch displacements of pc-relative
instructions within the insn slots just like x86 and powerpc.

The module area works already with the normal insn slot allocator, however
there is currently no way to get insn slots that are within the first 2GB
on s390 (aka DMA area).

Therefore this patch set introduces a third insn slot cache besides the
normal insn and optinsn slot caches: the dmainsn slot cache. Slots can be
allocated and freed with get_dmainsn_slot() and free_dmainsn_slot().

Patch 1 unifies the current insn and optinsn caches implementation so we
don't end up with a lot of code duplication when adding a third cache.

Patch 2 simply adds the new dmainsn slot cache.

Patch 3 is the s390 usage of the new cache.

Looking at the last couple of sign-off chains I'm not sure how kprobes
patches should go upstream.. Andrew, Ingo, or simply via the s390 tree?

Heiko Carstens (3):
kprobes: unify insn caches
kprobes: provide new dmainsn cache
s390/kprobes: add support for pc-relative long displacement instructions

arch/Kconfig | 7 +++
arch/s390/Kconfig | 1 +
arch/s390/include/asm/kprobes.h | 4 +-
arch/s390/kernel/kprobes.c | 124 ++++++++++++++++++++++++++++++++++++---
include/linux/kprobes.h | 31 ++++++++--
kernel/kprobes.c | 98 +++++++++++++++----------------
6 files changed, 203 insertions(+), 62 deletions(-)

--
1.7.10.4


2013-08-21 12:01:24

by Heiko Carstens

[permalink] [raw]
Subject: [PATCH 1/3] kprobes: unify insn caches

The two insn caches (insn, and optinsn) each have an own mutex and
alloc/free functions (get_[opt]insn_slot() / free_[opt]insn_slot()).

Since I need yet another insn cache which satifies dma allocations,
unify and simplify the current implementation:

- Move the per insn cache mutex into struct kprobe_insn_cache.
- Move the alloc/free functions to kprobe.h so they are simply
wrappers for the generic __get_insn_slot/__free_insn_slot.
The implementation is done with a DEFINE_INSN_CACHE_OPS() macro
which provides the alloc/free functions for each cache if needed.

Signed-off-by: Heiko Carstens <[email protected]>
---
include/linux/kprobes.h | 26 +++++++++++++++---
kernel/kprobes.c | 70 +++++++++++++++--------------------------------
2 files changed, 44 insertions(+), 52 deletions(-)

diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index ca1d27a..ffd9171 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -264,10 +264,28 @@ extern void arch_arm_kprobe(struct kprobe *p);
extern void arch_disarm_kprobe(struct kprobe *p);
extern int arch_init_kprobes(void);
extern void show_registers(struct pt_regs *regs);
-extern kprobe_opcode_t *get_insn_slot(void);
-extern void free_insn_slot(kprobe_opcode_t *slot, int dirty);
extern void kprobes_inc_nmissed_count(struct kprobe *p);

+struct kprobe_insn_cache;
+extern kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c);
+extern void __free_insn_slot(struct kprobe_insn_cache *c,
+ kprobe_opcode_t *slot, int dirty);
+
+#define DEFINE_INSN_CACHE_OPS(__name) \
+extern struct kprobe_insn_cache kprobe_##__name##_slots; \
+ \
+static inline kprobe_opcode_t *get_##__name##_slot(void) \
+{ \
+ return __get_insn_slot(&kprobe_##__name##_slots); \
+} \
+ \
+static inline void free_##__name##_slot(kprobe_opcode_t *slot, int dirty)\
+{ \
+ __free_insn_slot(&kprobe_##__name##_slots, slot, dirty); \
+} \
+
+DEFINE_INSN_CACHE_OPS(insn);
+
#ifdef CONFIG_OPTPROBES
/*
* Internal structure for direct jump optimized probe
@@ -287,13 +305,13 @@ extern void arch_optimize_kprobes(struct list_head *oplist);
extern void arch_unoptimize_kprobes(struct list_head *oplist,
struct list_head *done_list);
extern void arch_unoptimize_kprobe(struct optimized_kprobe *op);
-extern kprobe_opcode_t *get_optinsn_slot(void);
-extern void free_optinsn_slot(kprobe_opcode_t *slot, int dirty);
extern int arch_within_optimized_kprobe(struct optimized_kprobe *op,
unsigned long addr);

extern void opt_pre_handler(struct kprobe *p, struct pt_regs *regs);

+DEFINE_INSN_CACHE_OPS(optinsn);
+
#ifdef CONFIG_SYSCTL
extern int sysctl_kprobes_optimization;
extern int proc_kprobes_optimization_handler(struct ctl_table *table,
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 6e33498..30659b3 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -122,6 +122,7 @@ struct kprobe_insn_page {
(sizeof(char) * (slots)))

struct kprobe_insn_cache {
+ struct mutex mutex;
struct list_head pages; /* list of kprobe_insn_page */
size_t insn_size; /* size of instruction slot */
int nr_garbage;
@@ -138,8 +139,8 @@ enum kprobe_slot_state {
SLOT_USED = 2,
};

-static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_slots */
-static struct kprobe_insn_cache kprobe_insn_slots = {
+struct kprobe_insn_cache kprobe_insn_slots = {
+ .mutex = __MUTEX_INITIALIZER(kprobe_insn_slots.mutex),
.pages = LIST_HEAD_INIT(kprobe_insn_slots.pages),
.insn_size = MAX_INSN_SIZE,
.nr_garbage = 0,
@@ -150,10 +151,12 @@ static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c);
* __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.
*/
-static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
+kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
{
struct kprobe_insn_page *kip;
+ kprobe_opcode_t *slot = NULL;

+ mutex_lock(&c->mutex);
retry:
list_for_each_entry(kip, &c->pages, list) {
if (kip->nused < slots_per_page(c)) {
@@ -162,7 +165,8 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
if (kip->slot_used[i] == SLOT_CLEAN) {
kip->slot_used[i] = SLOT_USED;
kip->nused++;
- return kip->insns + (i * c->insn_size);
+ slot = kip->insns + (i * c->insn_size);
+ goto out;
}
}
/* kip->nused is broken. Fix it. */
@@ -178,7 +182,7 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
/* All out of space. Need to allocate a new page. */
kip = kmalloc(KPROBE_INSN_PAGE_SIZE(slots_per_page(c)), GFP_KERNEL);
if (!kip)
- return NULL;
+ goto out;

/*
* Use module_alloc so this page is within +/- 2GB of where the
@@ -188,7 +192,7 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
kip->insns = module_alloc(PAGE_SIZE);
if (!kip->insns) {
kfree(kip);
- return NULL;
+ goto out;
}
INIT_LIST_HEAD(&kip->list);
memset(kip->slot_used, SLOT_CLEAN, slots_per_page(c));
@@ -196,19 +200,10 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
kip->nused = 1;
kip->ngarbage = 0;
list_add(&kip->list, &c->pages);
- return kip->insns;
-}
-
-
-kprobe_opcode_t __kprobes *get_insn_slot(void)
-{
- kprobe_opcode_t *ret = NULL;
-
- mutex_lock(&kprobe_insn_mutex);
- ret = __get_insn_slot(&kprobe_insn_slots);
- mutex_unlock(&kprobe_insn_mutex);
-
- return ret;
+ slot = kip->insns;
+out:
+ mutex_unlock(&c->mutex);
+ return slot;
}

/* Return 1 if all garbages are collected, otherwise 0. */
@@ -255,11 +250,12 @@ static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c)
return 0;
}

-static void __kprobes __free_insn_slot(struct kprobe_insn_cache *c,
- kprobe_opcode_t *slot, int dirty)
+void __kprobes __free_insn_slot(struct kprobe_insn_cache *c,
+ kprobe_opcode_t *slot, int dirty)
{
struct kprobe_insn_page *kip;

+ mutex_lock(&c->mutex);
list_for_each_entry(kip, &c->pages, list) {
long idx = ((long)slot - (long)kip->insns) /
(c->insn_size * sizeof(kprobe_opcode_t));
@@ -272,45 +268,23 @@ static void __kprobes __free_insn_slot(struct kprobe_insn_cache *c,
collect_garbage_slots(c);
} else
collect_one_slot(kip, idx);
- return;
+ goto out;
}
}
/* Could not free this slot. */
WARN_ON(1);
+out:
+ mutex_unlock(&c->mutex);
}

-void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
-{
- mutex_lock(&kprobe_insn_mutex);
- __free_insn_slot(&kprobe_insn_slots, slot, dirty);
- mutex_unlock(&kprobe_insn_mutex);
-}
#ifdef CONFIG_OPTPROBES
/* For optimized_kprobe buffer */
-static DEFINE_MUTEX(kprobe_optinsn_mutex); /* Protects kprobe_optinsn_slots */
-static struct kprobe_insn_cache kprobe_optinsn_slots = {
+struct kprobe_insn_cache kprobe_optinsn_slots = {
+ .mutex = __MUTEX_INITIALIZER(kprobe_optinsn_slots.mutex),
.pages = LIST_HEAD_INIT(kprobe_optinsn_slots.pages),
/* .insn_size is initialized later */
.nr_garbage = 0,
};
-/* Get a slot for optimized_kprobe buffer */
-kprobe_opcode_t __kprobes *get_optinsn_slot(void)
-{
- kprobe_opcode_t *ret = NULL;
-
- mutex_lock(&kprobe_optinsn_mutex);
- ret = __get_insn_slot(&kprobe_optinsn_slots);
- mutex_unlock(&kprobe_optinsn_mutex);
-
- return ret;
-}
-
-void __kprobes free_optinsn_slot(kprobe_opcode_t * slot, int dirty)
-{
- mutex_lock(&kprobe_optinsn_mutex);
- __free_insn_slot(&kprobe_optinsn_slots, slot, dirty);
- mutex_unlock(&kprobe_optinsn_mutex);
-}
#endif
#endif

--
1.7.10.4

2013-08-21 12:01:31

by Heiko Carstens

[permalink] [raw]
Subject: [PATCH 3/3] s390/kprobes: add support for pc-relative long displacement instructions

With the general-instruction extension facility (z10) a couple of
instructions with a pc-relative long displacement were introduced.
The kprobes support for these instructions however was never implemented.

In result, if anybody ever put a probe on any of these instructions the
result would have been random behaviour after the instruction got executed
within the insn slot.

So lets add the missing handling for these instructions. Since all of the
new instructions have 32 bit signed displacement the easiest solution is
to allocate an insn slot that is within the same 2GB area like the original
instruction and patch the displacement field.

Signed-off-by: Heiko Carstens <[email protected]>
---
arch/s390/Kconfig | 1 +
arch/s390/include/asm/kprobes.h | 4 +-
arch/s390/kernel/kprobes.c | 124 ++++++++++++++++++++++++++++++++++++---
3 files changed, 121 insertions(+), 8 deletions(-)

diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 8a4cae7..ce389a9 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -96,6 +96,7 @@ config S390
select ARCH_WANT_IPC_PARSE_VERSION
select BUILDTIME_EXTABLE_SORT
select CLONE_BACKWARDS2
+ select DMAPROBES if KPROBES
select GENERIC_CLOCKEVENTS
select GENERIC_CPU_DEVICES if !SMP
select GENERIC_SMP_IDLE_THREAD
diff --git a/arch/s390/include/asm/kprobes.h b/arch/s390/include/asm/kprobes.h
index dcf6948..4176dfe 100644
--- a/arch/s390/include/asm/kprobes.h
+++ b/arch/s390/include/asm/kprobes.h
@@ -31,6 +31,8 @@
#include <linux/ptrace.h>
#include <linux/percpu.h>

+#define __ARCH_WANT_KPROBES_INSN_SLOT
+
struct pt_regs;
struct kprobe;

@@ -57,7 +59,7 @@ typedef u16 kprobe_opcode_t;
/* Architecture specific copy of original instruction */
struct arch_specific_insn {
/* copy of original instruction */
- kprobe_opcode_t insn[MAX_INSN_SIZE];
+ kprobe_opcode_t *insn;
};

struct prev_kprobe {
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
index 3388b2b..bc1071c 100644
--- a/arch/s390/kernel/kprobes.c
+++ b/arch/s390/kernel/kprobes.c
@@ -100,9 +100,8 @@ static int __kprobes get_fixup_type(kprobe_opcode_t *insn)
fixup |= FIXUP_RETURN_REGISTER;
break;
case 0xc0:
- if ((insn[0] & 0x0f) == 0x00 || /* larl */
- (insn[0] & 0x0f) == 0x05) /* brasl */
- fixup |= FIXUP_RETURN_REGISTER;
+ if ((insn[0] & 0x0f) == 0x05) /* brasl */
+ fixup |= FIXUP_RETURN_REGISTER;
break;
case 0xeb:
if ((insn[2] & 0xff) == 0x44 || /* bxhg */
@@ -117,18 +116,128 @@ static int __kprobes get_fixup_type(kprobe_opcode_t *insn)
return fixup;
}

+static int __kprobes is_insn_relative_long(kprobe_opcode_t *insn)
+{
+ /* Check if we have a RIL-b or RIL-c format instruction which
+ * we need to modify in order to avoid instruction emulation. */
+ switch (insn[0] >> 8) {
+ case 0xc0:
+ if ((insn[0] & 0x0f) == 0x00) /* larl */
+ return true;
+ break;
+ case 0xc4:
+ switch (insn[0] & 0x0f) {
+ case 0x02: /* llhrl */
+ case 0x04: /* lghrl */
+ case 0x05: /* lhrl */
+ case 0x06: /* llghrl */
+ case 0x07: /* sthrl */
+ case 0x08: /* lgrl */
+ case 0x0b: /* stgrl */
+ case 0x0c: /* lgfrl */
+ case 0x0d: /* lrl */
+ case 0x0e: /* llgfrl */
+ case 0x0f: /* strl */
+ return true;
+ }
+ break;
+ case 0xc6:
+ switch (insn[0] & 0x0f) {
+ case 0x00: /* exrl */
+ case 0x02: /* pfdrl */
+ case 0x04: /* cghrl */
+ case 0x05: /* chrl */
+ case 0x06: /* clghrl */
+ case 0x07: /* clhrl */
+ case 0x08: /* cgrl */
+ case 0x0a: /* clgrl */
+ case 0x0c: /* cgfrl */
+ case 0x0d: /* crl */
+ case 0x0e: /* clgfrl */
+ case 0x0f: /* clrl */
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+static void __kprobes copy_instruction(struct kprobe *p)
+{
+ s64 disp, new_disp;
+ u64 addr, new_addr;
+
+ memcpy(p->ainsn.insn, p->addr, ((p->opcode >> 14) + 3) & -2);
+ if (!is_insn_relative_long(p->ainsn.insn))
+ return;
+ /*
+ * For pc-relative instructions in RIL-b or RIL-c format patch the
+ * RI2 displacement field. We have already made sure that the insn
+ * slot for the patched instruction is within the same 2GB area
+ * as the original instruction (either kernel image or module area).
+ * Therefore the new displacement will always fit.
+ */
+ disp = *(s32 *)&p->ainsn.insn[1];
+ addr = (u64)(unsigned long)p->addr;
+ new_addr = (u64)(unsigned long)p->ainsn.insn;
+ new_disp = ((addr + (disp * 2)) - new_addr) / 2;
+ *(s32 *)&p->ainsn.insn[1] = new_disp;
+}
+
+static inline int is_kernel_addr(void *addr)
+{
+ return addr < (void *)_end;
+}
+
+static inline int is_module_addr(void *addr)
+{
+#ifdef CONFIG_64BIT
+ BUILD_BUG_ON(MODULES_LEN > (1UL << 31));
+ if (addr < (void *)MODULES_VADDR)
+ return 0;
+ if (addr > (void *)MODULES_END)
+ return 0;
+#endif
+ return 1;
+}
+
+static int __kprobes s390_get_insn_slot(struct kprobe *p)
+{
+ /*
+ * Get an insn slot that is within the same 2GB area like the original
+ * instruction. That way instructions with a 32bit signed displacement
+ * field can be patched and executed within the insn slot.
+ */
+ p->ainsn.insn = NULL;
+ if (is_kernel_addr(p->addr))
+ p->ainsn.insn = get_dmainsn_slot();
+ if (is_module_addr(p->addr))
+ p->ainsn.insn = get_insn_slot();
+ return p->ainsn.insn ? 0 : -ENOMEM;
+}
+
+static void __kprobes s390_free_insn_slot(struct kprobe *p)
+{
+ if (!p->ainsn.insn)
+ return;
+ if (is_kernel_addr(p->addr))
+ free_dmainsn_slot(p->ainsn.insn, 0);
+ else
+ free_insn_slot(p->ainsn.insn, 0);
+ p->ainsn.insn = NULL;
+}
+
int __kprobes arch_prepare_kprobe(struct kprobe *p)
{
if ((unsigned long) p->addr & 0x01)
return -EINVAL;
-
/* Make sure the probe isn't going on a difficult instruction */
if (is_prohibited_opcode(p->addr))
return -EINVAL;
-
+ if (s390_get_insn_slot(p))
+ return -ENOMEM;
p->opcode = *p->addr;
- memcpy(p->ainsn.insn, p->addr, ((p->opcode >> 14) + 3) & -2);
-
+ copy_instruction(p);
return 0;
}

@@ -169,6 +278,7 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p)

void __kprobes arch_remove_kprobe(struct kprobe *p)
{
+ s390_free_insn_slot(p);
}

static void __kprobes enable_singlestep(struct kprobe_ctlblk *kcb,
--
1.7.10.4

2013-08-21 12:02:00

by Heiko Carstens

[permalink] [raw]
Subject: [PATCH 2/3] kprobes: provide new dmainsn cache

The current kpropes insn caches allocate memory areas for insn slots with
module_alloc(). The assumption is that the kernel image and module area
are both within the same +/- 2GB memory area.
This however is not true for s390 where the kernel image resides within
the first 2GB (DMA memory area), but the module area is far away in the
vmalloc area, usually somewhere close below the 4TB area.

For new pc relative instructions s390 needs insn slots that are within
+/- 2GB of each area. That way we can patch displacements of pc-relative
instructions within the insn slots just like x86 and powerpc.

The module area works already with the normal insn slot allocator, however
there is currently no way to get insn slots that are within the first 2GB
on s390 (aka DMA area).

Therefore this patch introduces the dmainsn slot cache. Slots can be
allocated and freed with get_dmainsn_slot() and free_dmainsn_slot().

Signed-off-by: Heiko Carstens <[email protected]>
---
arch/Kconfig | 7 +++++++
include/linux/kprobes.h | 5 +++++
kernel/kprobes.c | 28 ++++++++++++++++++++++++++--
3 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 1feb169..7010d68 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -76,6 +76,13 @@ config OPTPROBES
depends on KPROBES && HAVE_OPTPROBES
depends on !PREEMPT

+config DMAPROBES
+ bool
+ help
+ Architectures may want to put kprobes instruction slots into
+ the dma memory region. E.g. s390 has the kernel image in the
+ dma memory region but the module area far away.
+
config KPROBES_ON_FTRACE
def_bool y
depends on KPROBES && HAVE_KPROBES_ON_FTRACE
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index ffd9171..a5290f6 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -320,6 +320,11 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table,
#endif

#endif /* CONFIG_OPTPROBES */
+
+#ifdef CONFIG_DMAPROBES
+DEFINE_INSN_CACHE_OPS(dmainsn);
+#endif
+
#ifdef CONFIG_KPROBES_ON_FTRACE
extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ops, struct pt_regs *regs);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 30659b3..3b8b073 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -114,6 +114,7 @@ struct kprobe_insn_page {
kprobe_opcode_t *insns; /* Page of instruction slots */
int nused;
int ngarbage;
+ bool dma_alloc;
char slot_used[];
};

@@ -126,6 +127,7 @@ struct kprobe_insn_cache {
struct list_head pages; /* list of kprobe_insn_page */
size_t insn_size; /* size of instruction slot */
int nr_garbage;
+ bool dma_alloc;
};

static int slots_per_page(struct kprobe_insn_cache *c)
@@ -144,6 +146,7 @@ struct kprobe_insn_cache kprobe_insn_slots = {
.pages = LIST_HEAD_INIT(kprobe_insn_slots.pages),
.insn_size = MAX_INSN_SIZE,
.nr_garbage = 0,
+ .dma_alloc = false,
};
static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c);

@@ -189,7 +192,10 @@ kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
* kernel image and loaded module images reside. This is required
* so x86_64 can correctly handle the %rip-relative fixups.
*/
- kip->insns = module_alloc(PAGE_SIZE);
+ if (c->dma_alloc)
+ kip->insns = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+ else
+ kip->insns = module_alloc(PAGE_SIZE);
if (!kip->insns) {
kfree(kip);
goto out;
@@ -199,6 +205,7 @@ kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
kip->slot_used[0] = SLOT_USED;
kip->nused = 1;
kip->ngarbage = 0;
+ kip->dma_alloc = c->dma_alloc;
list_add(&kip->list, &c->pages);
slot = kip->insns;
out:
@@ -220,7 +227,10 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
*/
if (!list_is_singular(&kip->list)) {
list_del(&kip->list);
- module_free(NULL, kip->insns);
+ if (kip->dma_alloc)
+ free_page((unsigned long)kip->insns);
+ else
+ module_free(NULL, kip->insns);
kfree(kip);
}
return 1;
@@ -284,6 +294,20 @@ struct kprobe_insn_cache kprobe_optinsn_slots = {
.pages = LIST_HEAD_INIT(kprobe_optinsn_slots.pages),
/* .insn_size is initialized later */
.nr_garbage = 0,
+ .dma_alloc = false,
+};
+#endif
+#ifdef CONFIG_DMAPROBES
+/*
+ * Special buffer for architectures which require insn slots
+ * to be in the GFP_DMA memory range.
+ */
+struct kprobe_insn_cache kprobe_dmainsn_slots = {
+ .mutex = __MUTEX_INITIALIZER(kprobe_dmainsn_slots.mutex),
+ .pages = LIST_HEAD_INIT(kprobe_dmainsn_slots.pages),
+ .insn_size = MAX_INSN_SIZE,
+ .nr_garbage = 0,
+ .dma_alloc = true,
};
#endif
#endif
--
1.7.10.4

Subject: Re: [PATCH 0/3] kprobes: add new dma insn slot cache for s390

Hi Heiko,

(2013/08/21 21:01), Heiko Carstens wrote:
> The current kpropes insn caches allocate memory areas for insn slots with
> module_alloc(). The assumption is that the kernel image and module area
> are both within the same +/- 2GB memory area.
> This however is not true for s390 where the kernel image resides within
> the first 2GB (DMA memory area), but the module area is far away in the
> vmalloc area, usually somewhere close below the 4TB area.
>
> For new pc relative instructions s390 needs insn slots that are within
> +/- 2GB of each area. That way we can patch displacements of pc-relative
> instructions within the insn slots just like x86 and powerpc.
>
> The module area works already with the normal insn slot allocator, however
> there is currently no way to get insn slots that are within the first 2GB
> on s390 (aka DMA area).

The reason why we allocate instruction buffers from module area is
to execute a piece of code on the buffer, which should be executable.
I'm not good for s390, is that allows kernel to execute the code
on such DMA buffer?

> Therefore this patch set introduces a third insn slot cache besides the
> normal insn and optinsn slot caches: the dmainsn slot cache. Slots can be
> allocated and freed with get_dmainsn_slot() and free_dmainsn_slot().

OK, but it seems that your patch introduced unneeded complexity. Perhaps,
you just have to introduce 2 weak functions to allocate/release such
executable and jump-able buffers, like below,

void * __weak arch_allocate_executable_page(void)
{
return module_alloc(PAGE_SIZE);
}

void __weak arch_free_executable_page(void *page)
{
module_free(NULL, page);
}

Thus, all you need to do is implementing dmaalloc() version of above
functions on s390. No kconfig, no ifdefs are needed. :)

>
> Patch 1 unifies the current insn and optinsn caches implementation so we
> don't end up with a lot of code duplication when adding a third cache.
>
> Patch 2 simply adds the new dmainsn slot cache.
>
> Patch 3 is the s390 usage of the new cache.
>
> Looking at the last couple of sign-off chains I'm not sure how kprobes
> patches should go upstream.. Andrew, Ingo, or simply via the s390 tree?

Hmm, AFAIK, currently all noarch kprobes works go to -tip tree, and
the arch dependent parts go to each arch tree (only x86 goes to -tip tree).

Thank you,

--
Masami HIRAMATSU
IT Management Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]

2013-08-22 05:53:02

by Heiko Carstens

[permalink] [raw]
Subject: Re: [PATCH 0/3] kprobes: add new dma insn slot cache for s390

Hi Masami,

> (2013/08/21 21:01), Heiko Carstens wrote:
> > The current kpropes insn caches allocate memory areas for insn slots with
> > module_alloc(). The assumption is that the kernel image and module area
> > are both within the same +/- 2GB memory area.
> > This however is not true for s390 where the kernel image resides within
> > the first 2GB (DMA memory area), but the module area is far away in the
> > vmalloc area, usually somewhere close below the 4TB area.
> >
> > For new pc relative instructions s390 needs insn slots that are within
> > +/- 2GB of each area. That way we can patch displacements of pc-relative
> > instructions within the insn slots just like x86 and powerpc.
> >
> > The module area works already with the normal insn slot allocator, however
> > there is currently no way to get insn slots that are within the first 2GB
> > on s390 (aka DMA area).
>
> The reason why we allocate instruction buffers from module area is
> to execute a piece of code on the buffer, which should be executable.
> I'm not good for s390, is that allows kernel to execute the code
> on such DMA buffer?

Yes, the kernel image itself resides in DMA capable memory and it is all
executable.

> > Therefore this patch set introduces a third insn slot cache besides the
> > normal insn and optinsn slot caches: the dmainsn slot cache. Slots can be
> > allocated and freed with get_dmainsn_slot() and free_dmainsn_slot().
>
> OK, but it seems that your patch introduced unneeded complexity. Perhaps,
> you just have to introduce 2 weak functions to allocate/release such
> executable and jump-able buffers, like below,
>
> void * __weak arch_allocate_executable_page(void)
> {
> return module_alloc(PAGE_SIZE);
> }
>
> void __weak arch_free_executable_page(void *page)
> {
> module_free(NULL, page);
> }
>
> Thus, all you need to do is implementing dmaalloc() version of above
> functions on s390. No kconfig, no ifdefs are needed. :)

Hm, I don't see how that can work, or maybe I just don't get your idea ;)
Or maybe my intention was not clear? So let me try again:

If the to be probed instruction resides within the first 2GB of memory
(aka DMA memory, aka kernel image) the insn slot must be within the first
2GB as well, otherwise I can't patch pc-relative instructions.

On the other hand if the to be probed instruction resides in a module
(aka part of the vmalloc area), the insn slot must reside within the same
2GB area as well.

Therefore I need to different insn slot caches, where the slots are either
allocated with __get_free_page(GFP_KERNEL | GFP_DMA) (for the kernel image)
or module_alloc(PAGE_SIZE) for modules.

I can't have a single cache which satifies both areas.

Subject: Re: Re: [PATCH 0/3] kprobes: add new dma insn slot cache for s390

(2013/08/22 14:52), Heiko Carstens wrote:
> Hi Masami,
>
>> (2013/08/21 21:01), Heiko Carstens wrote:
>>> The current kpropes insn caches allocate memory areas for insn slots with
>>> module_alloc(). The assumption is that the kernel image and module area
>>> are both within the same +/- 2GB memory area.
>>> This however is not true for s390 where the kernel image resides within
>>> the first 2GB (DMA memory area), but the module area is far away in the
>>> vmalloc area, usually somewhere close below the 4TB area.
>>>
>>> For new pc relative instructions s390 needs insn slots that are within
>>> +/- 2GB of each area. That way we can patch displacements of pc-relative
>>> instructions within the insn slots just like x86 and powerpc.
>>>
>>> The module area works already with the normal insn slot allocator, however
>>> there is currently no way to get insn slots that are within the first 2GB
>>> on s390 (aka DMA area).
>>
>> The reason why we allocate instruction buffers from module area is
>> to execute a piece of code on the buffer, which should be executable.
>> I'm not good for s390, is that allows kernel to execute the code
>> on such DMA buffer?
>
> Yes, the kernel image itself resides in DMA capable memory and it is all
> executable.
>
>>> Therefore this patch set introduces a third insn slot cache besides the
>>> normal insn and optinsn slot caches: the dmainsn slot cache. Slots can be
>>> allocated and freed with get_dmainsn_slot() and free_dmainsn_slot().
>>
>> OK, but it seems that your patch introduced unneeded complexity. Perhaps,
>> you just have to introduce 2 weak functions to allocate/release such
>> executable and jump-able buffers, like below,
>>
>> void * __weak arch_allocate_executable_page(void)
>> {
>> return module_alloc(PAGE_SIZE);
>> }
>>
>> void __weak arch_free_executable_page(void *page)
>> {
>> module_free(NULL, page);
>> }
>>
>> Thus, all you need to do is implementing dmaalloc() version of above
>> functions on s390. No kconfig, no ifdefs are needed. :)
>
> Hm, I don't see how that can work, or maybe I just don't get your idea ;)
> Or maybe my intention was not clear? So let me try again:
>
> If the to be probed instruction resides within the first 2GB of memory
> (aka DMA memory, aka kernel image) the insn slot must be within the first
> 2GB as well, otherwise I can't patch pc-relative instructions.
>
> On the other hand if the to be probed instruction resides in a module
> (aka part of the vmalloc area), the insn slot must reside within the same
> 2GB area as well.
>
> Therefore I need to different insn slot caches, where the slots are either
> allocated with __get_free_page(GFP_KERNEL | GFP_DMA) (for the kernel image)
> or module_alloc(PAGE_SIZE) for modules.
>
> I can't have a single cache which satifies both areas.

Oh, I see.
Indeed, that enough reason to add a new cache... By the way, is there
any way to implement it without new kconfig like DMAPROBE and dma flag?
AFAICS, since such flag is strongly depends on the s390 arch, I don't
like to put it in kernel/kprobes.c.

Perhaps, we can make insn slot more generic, e.g. create new slot type
with passing page allocator.

Thank you,

--
Masami HIRAMATSU
IT Management Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]

2013-08-23 08:10:32

by Heiko Carstens

[permalink] [raw]
Subject: Re: Re: [PATCH 0/3] kprobes: add new dma insn slot cache for s390

On Fri, Aug 23, 2013 at 01:31:23PM +0900, Masami Hiramatsu wrote:
> (2013/08/22 14:52), Heiko Carstens wrote:
> > Therefore I need to different insn slot caches, where the slots are either
> > allocated with __get_free_page(GFP_KERNEL | GFP_DMA) (for the kernel image)
> > or module_alloc(PAGE_SIZE) for modules.
> >
> > I can't have a single cache which satifies both areas.
>
> Oh, I see.
> Indeed, that enough reason to add a new cache... By the way, is there
> any way to implement it without new kconfig like DMAPROBE and dma flag?
> AFAICS, since such flag is strongly depends on the s390 arch, I don't
> like to put it in kernel/kprobes.c.
>
> Perhaps, we can make insn slot more generic, e.g. create new slot type
> with passing page allocator.

Something like below?
(only compile tested and on top of the previous patches).

I'm not sure, since that would expose struct kprobe_insn_cache.

arch/Kconfig | 7 -------
arch/s390/Kconfig | 1 -
arch/s390/kernel/kprobes.c | 20 ++++++++++++++++++
include/linux/kprobes.h | 14 ++++++++-----
kernel/kprobes.c | 51 ++++++++++++++++------------------------------
5 files changed, 47 insertions(+), 46 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 7010d68..1feb169 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -76,13 +76,6 @@ config OPTPROBES
depends on KPROBES && HAVE_OPTPROBES
depends on !PREEMPT

-config DMAPROBES
- bool
- help
- Architectures may want to put kprobes instruction slots into
- the dma memory region. E.g. s390 has the kernel image in the
- dma memory region but the module area far away.
-
config KPROBES_ON_FTRACE
def_bool y
depends on KPROBES && HAVE_KPROBES_ON_FTRACE
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index ce389a9..8a4cae7 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -96,7 +96,6 @@ config S390
select ARCH_WANT_IPC_PARSE_VERSION
select BUILDTIME_EXTABLE_SORT
select CLONE_BACKWARDS2
- select DMAPROBES if KPROBES
select GENERIC_CLOCKEVENTS
select GENERIC_CPU_DEVICES if !SMP
select GENERIC_SMP_IDLE_THREAD
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
index bc1071c..cb7ac9e 100644
--- a/arch/s390/kernel/kprobes.c
+++ b/arch/s390/kernel/kprobes.c
@@ -37,6 +37,26 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);

struct kretprobe_blackpoint kretprobe_blacklist[] = { };

+DEFINE_INSN_CACHE_OPS(dmainsn);
+
+static void *alloc_dmainsn_page(void)
+{
+ return (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+}
+
+static void free_dmainsn_page(void *page)
+{
+ free_page((unsigned long)page);
+}
+
+struct kprobe_insn_cache kprobe_dmainsn_slots = {
+ .mutex = __MUTEX_INITIALIZER(kprobe_dmainsn_slots.mutex),
+ .alloc = alloc_dmainsn_page,
+ .free = free_dmainsn_page,
+ .pages = LIST_HEAD_INIT(kprobe_dmainsn_slots.pages),
+ .insn_size = MAX_INSN_SIZE,
+};
+
static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn)
{
switch (insn[0] >> 8) {
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index a5290f6..4e96827 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -266,7 +266,15 @@ extern int arch_init_kprobes(void);
extern void show_registers(struct pt_regs *regs);
extern void kprobes_inc_nmissed_count(struct kprobe *p);

-struct kprobe_insn_cache;
+struct kprobe_insn_cache {
+ struct mutex mutex;
+ void *(*alloc)(void); /* allocate insn page */
+ void (*free)(void *); /* free insn page */
+ struct list_head pages; /* list of kprobe_insn_page */
+ size_t insn_size; /* size of instruction slot */
+ int nr_garbage;
+};
+
extern kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c);
extern void __free_insn_slot(struct kprobe_insn_cache *c,
kprobe_opcode_t *slot, int dirty);
@@ -321,10 +329,6 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table,

#endif /* CONFIG_OPTPROBES */

-#ifdef CONFIG_DMAPROBES
-DEFINE_INSN_CACHE_OPS(dmainsn);
-#endif
-
#ifdef CONFIG_KPROBES_ON_FTRACE
extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ops, struct pt_regs *regs);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 3b8b073..a0d367a 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -112,9 +112,9 @@ static struct kprobe_blackpoint kprobe_blacklist[] = {
struct kprobe_insn_page {
struct list_head list;
kprobe_opcode_t *insns; /* Page of instruction slots */
+ struct kprobe_insn_cache *cache;
int nused;
int ngarbage;
- bool dma_alloc;
char slot_used[];
};

@@ -122,14 +122,6 @@ struct kprobe_insn_page {
(offsetof(struct kprobe_insn_page, slot_used) + \
(sizeof(char) * (slots)))

-struct kprobe_insn_cache {
- struct mutex mutex;
- struct list_head pages; /* list of kprobe_insn_page */
- size_t insn_size; /* size of instruction slot */
- int nr_garbage;
- bool dma_alloc;
-};
-
static int slots_per_page(struct kprobe_insn_cache *c)
{
return PAGE_SIZE/(c->insn_size * sizeof(kprobe_opcode_t));
@@ -141,12 +133,23 @@ enum kprobe_slot_state {
SLOT_USED = 2,
};

+static void *alloc_insn_page(void)
+{
+ return module_alloc(PAGE_SIZE);
+}
+
+static void free_insn_page(void *page)
+{
+ module_free(NULL, page);
+}
+
struct kprobe_insn_cache kprobe_insn_slots = {
.mutex = __MUTEX_INITIALIZER(kprobe_insn_slots.mutex),
+ .alloc = alloc_insn_page,
+ .free = free_insn_page,
.pages = LIST_HEAD_INIT(kprobe_insn_slots.pages),
.insn_size = MAX_INSN_SIZE,
.nr_garbage = 0,
- .dma_alloc = false,
};
static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c);

@@ -192,10 +195,7 @@ kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
* kernel image and loaded module images reside. This is required
* so x86_64 can correctly handle the %rip-relative fixups.
*/
- if (c->dma_alloc)
- kip->insns = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
- else
- kip->insns = module_alloc(PAGE_SIZE);
+ kip->insns = c->alloc();
if (!kip->insns) {
kfree(kip);
goto out;
@@ -205,7 +205,7 @@ kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
kip->slot_used[0] = SLOT_USED;
kip->nused = 1;
kip->ngarbage = 0;
- kip->dma_alloc = c->dma_alloc;
+ kip->cache = c;
list_add(&kip->list, &c->pages);
slot = kip->insns;
out:
@@ -227,10 +227,7 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
*/
if (!list_is_singular(&kip->list)) {
list_del(&kip->list);
- if (kip->dma_alloc)
- free_page((unsigned long)kip->insns);
- else
- module_free(NULL, kip->insns);
+ kip->cache->free(kip->insns);
kfree(kip);
}
return 1;
@@ -291,23 +288,11 @@ out:
/* For optimized_kprobe buffer */
struct kprobe_insn_cache kprobe_optinsn_slots = {
.mutex = __MUTEX_INITIALIZER(kprobe_optinsn_slots.mutex),
+ .alloc = alloc_insn_page,
+ .free = free_insn_page,
.pages = LIST_HEAD_INIT(kprobe_optinsn_slots.pages),
/* .insn_size is initialized later */
.nr_garbage = 0,
- .dma_alloc = false,
-};
-#endif
-#ifdef CONFIG_DMAPROBES
-/*
- * Special buffer for architectures which require insn slots
- * to be in the GFP_DMA memory range.
- */
-struct kprobe_insn_cache kprobe_dmainsn_slots = {
- .mutex = __MUTEX_INITIALIZER(kprobe_dmainsn_slots.mutex),
- .pages = LIST_HEAD_INIT(kprobe_dmainsn_slots.pages),
- .insn_size = MAX_INSN_SIZE,
- .nr_garbage = 0,
- .dma_alloc = true,
};
#endif
#endif
--
1.8.2.3

Subject: Re: [PATCH 0/3] kprobes: add new dma insn slot cache for s390

(2013/08/23 17:09), Heiko Carstens wrote:
> On Fri, Aug 23, 2013 at 01:31:23PM +0900, Masami Hiramatsu wrote:
>> (2013/08/22 14:52), Heiko Carstens wrote:
>>> Therefore I need to different insn slot caches, where the slots are either
>>> allocated with __get_free_page(GFP_KERNEL | GFP_DMA) (for the kernel image)
>>> or module_alloc(PAGE_SIZE) for modules.
>>>
>>> I can't have a single cache which satifies both areas.
>>
>> Oh, I see.
>> Indeed, that enough reason to add a new cache... By the way, is there
>> any way to implement it without new kconfig like DMAPROBE and dma flag?
>> AFAICS, since such flag is strongly depends on the s390 arch, I don't
>> like to put it in kernel/kprobes.c.
>>
>> Perhaps, we can make insn slot more generic, e.g. create new slot type
>> with passing page allocator.
>
> Something like below?
> (only compile tested and on top of the previous patches).

Yes! this is exactly what I thought :)

> I'm not sure, since that would expose struct kprobe_insn_cache.

That is OK, since it is required for some arch.
Could you update the series with this change?

Thank you!

>
> arch/Kconfig | 7 -------
> arch/s390/Kconfig | 1 -
> arch/s390/kernel/kprobes.c | 20 ++++++++++++++++++
> include/linux/kprobes.h | 14 ++++++++-----
> kernel/kprobes.c | 51 ++++++++++++++++------------------------------
> 5 files changed, 47 insertions(+), 46 deletions(-)
>
> diff --git a/arch/Kconfig b/arch/Kconfig
> index 7010d68..1feb169 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -76,13 +76,6 @@ config OPTPROBES
> depends on KPROBES && HAVE_OPTPROBES
> depends on !PREEMPT
>
> -config DMAPROBES
> - bool
> - help
> - Architectures may want to put kprobes instruction slots into
> - the dma memory region. E.g. s390 has the kernel image in the
> - dma memory region but the module area far away.
> -
> config KPROBES_ON_FTRACE
> def_bool y
> depends on KPROBES && HAVE_KPROBES_ON_FTRACE
> diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
> index ce389a9..8a4cae7 100644
> --- a/arch/s390/Kconfig
> +++ b/arch/s390/Kconfig
> @@ -96,7 +96,6 @@ config S390
> select ARCH_WANT_IPC_PARSE_VERSION
> select BUILDTIME_EXTABLE_SORT
> select CLONE_BACKWARDS2
> - select DMAPROBES if KPROBES
> select GENERIC_CLOCKEVENTS
> select GENERIC_CPU_DEVICES if !SMP
> select GENERIC_SMP_IDLE_THREAD
> diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
> index bc1071c..cb7ac9e 100644
> --- a/arch/s390/kernel/kprobes.c
> +++ b/arch/s390/kernel/kprobes.c
> @@ -37,6 +37,26 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
>
> struct kretprobe_blackpoint kretprobe_blacklist[] = { };
>
> +DEFINE_INSN_CACHE_OPS(dmainsn);
> +
> +static void *alloc_dmainsn_page(void)
> +{
> + return (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
> +}
> +
> +static void free_dmainsn_page(void *page)
> +{
> + free_page((unsigned long)page);
> +}
> +
> +struct kprobe_insn_cache kprobe_dmainsn_slots = {
> + .mutex = __MUTEX_INITIALIZER(kprobe_dmainsn_slots.mutex),
> + .alloc = alloc_dmainsn_page,
> + .free = free_dmainsn_page,
> + .pages = LIST_HEAD_INIT(kprobe_dmainsn_slots.pages),
> + .insn_size = MAX_INSN_SIZE,
> +};
> +
> static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn)
> {
> switch (insn[0] >> 8) {
> diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
> index a5290f6..4e96827 100644
> --- a/include/linux/kprobes.h
> +++ b/include/linux/kprobes.h
> @@ -266,7 +266,15 @@ extern int arch_init_kprobes(void);
> extern void show_registers(struct pt_regs *regs);
> extern void kprobes_inc_nmissed_count(struct kprobe *p);
>
> -struct kprobe_insn_cache;
> +struct kprobe_insn_cache {
> + struct mutex mutex;
> + void *(*alloc)(void); /* allocate insn page */
> + void (*free)(void *); /* free insn page */
> + struct list_head pages; /* list of kprobe_insn_page */
> + size_t insn_size; /* size of instruction slot */
> + int nr_garbage;
> +};
> +
> extern kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c);
> extern void __free_insn_slot(struct kprobe_insn_cache *c,
> kprobe_opcode_t *slot, int dirty);
> @@ -321,10 +329,6 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table,
>
> #endif /* CONFIG_OPTPROBES */
>
> -#ifdef CONFIG_DMAPROBES
> -DEFINE_INSN_CACHE_OPS(dmainsn);
> -#endif
> -
> #ifdef CONFIG_KPROBES_ON_FTRACE
> extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
> struct ftrace_ops *ops, struct pt_regs *regs);
> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> index 3b8b073..a0d367a 100644
> --- a/kernel/kprobes.c
> +++ b/kernel/kprobes.c
> @@ -112,9 +112,9 @@ static struct kprobe_blackpoint kprobe_blacklist[] = {
> struct kprobe_insn_page {
> struct list_head list;
> kprobe_opcode_t *insns; /* Page of instruction slots */
> + struct kprobe_insn_cache *cache;
> int nused;
> int ngarbage;
> - bool dma_alloc;
> char slot_used[];
> };
>
> @@ -122,14 +122,6 @@ struct kprobe_insn_page {
> (offsetof(struct kprobe_insn_page, slot_used) + \
> (sizeof(char) * (slots)))
>
> -struct kprobe_insn_cache {
> - struct mutex mutex;
> - struct list_head pages; /* list of kprobe_insn_page */
> - size_t insn_size; /* size of instruction slot */
> - int nr_garbage;
> - bool dma_alloc;
> -};
> -
> static int slots_per_page(struct kprobe_insn_cache *c)
> {
> return PAGE_SIZE/(c->insn_size * sizeof(kprobe_opcode_t));
> @@ -141,12 +133,23 @@ enum kprobe_slot_state {
> SLOT_USED = 2,
> };
>
> +static void *alloc_insn_page(void)
> +{
> + return module_alloc(PAGE_SIZE);
> +}
> +
> +static void free_insn_page(void *page)
> +{
> + module_free(NULL, page);
> +}
> +
> struct kprobe_insn_cache kprobe_insn_slots = {
> .mutex = __MUTEX_INITIALIZER(kprobe_insn_slots.mutex),
> + .alloc = alloc_insn_page,
> + .free = free_insn_page,
> .pages = LIST_HEAD_INIT(kprobe_insn_slots.pages),
> .insn_size = MAX_INSN_SIZE,
> .nr_garbage = 0,
> - .dma_alloc = false,
> };
> static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c);
>
> @@ -192,10 +195,7 @@ kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
> * kernel image and loaded module images reside. This is required
> * so x86_64 can correctly handle the %rip-relative fixups.
> */
> - if (c->dma_alloc)
> - kip->insns = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
> - else
> - kip->insns = module_alloc(PAGE_SIZE);
> + kip->insns = c->alloc();
> if (!kip->insns) {
> kfree(kip);
> goto out;
> @@ -205,7 +205,7 @@ kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
> kip->slot_used[0] = SLOT_USED;
> kip->nused = 1;
> kip->ngarbage = 0;
> - kip->dma_alloc = c->dma_alloc;
> + kip->cache = c;
> list_add(&kip->list, &c->pages);
> slot = kip->insns;
> out:
> @@ -227,10 +227,7 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
> */
> if (!list_is_singular(&kip->list)) {
> list_del(&kip->list);
> - if (kip->dma_alloc)
> - free_page((unsigned long)kip->insns);
> - else
> - module_free(NULL, kip->insns);
> + kip->cache->free(kip->insns);
> kfree(kip);
> }
> return 1;
> @@ -291,23 +288,11 @@ out:
> /* For optimized_kprobe buffer */
> struct kprobe_insn_cache kprobe_optinsn_slots = {
> .mutex = __MUTEX_INITIALIZER(kprobe_optinsn_slots.mutex),
> + .alloc = alloc_insn_page,
> + .free = free_insn_page,
> .pages = LIST_HEAD_INIT(kprobe_optinsn_slots.pages),
> /* .insn_size is initialized later */
> .nr_garbage = 0,
> - .dma_alloc = false,
> -};
> -#endif
> -#ifdef CONFIG_DMAPROBES
> -/*
> - * Special buffer for architectures which require insn slots
> - * to be in the GFP_DMA memory range.
> - */
> -struct kprobe_insn_cache kprobe_dmainsn_slots = {
> - .mutex = __MUTEX_INITIALIZER(kprobe_dmainsn_slots.mutex),
> - .pages = LIST_HEAD_INIT(kprobe_dmainsn_slots.pages),
> - .insn_size = MAX_INSN_SIZE,
> - .nr_garbage = 0,
> - .dma_alloc = true,
> };
> #endif
> #endif
>


--
Masami HIRAMATSU
IT Management Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]