s390/kprobes: use static buffer for insn_page
authorGerald Schaefer <gerald.schaefer@de.ibm.com>
Sun, 3 Feb 2019 20:36:46 +0000 (21:36 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 29 Apr 2019 08:47:10 +0000 (10:47 +0200)
With a relocatable kernel that could reside at any place in memory, the
current logic for allocating a kprobes insn_page does not work. The
GFP_DMA allocated buffer might be more than 2 GB away from the kernel.

Use a static buffer for the insn_page instead.

Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Reviewed-by: Philipp Rudo <prudo@linux.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/kernel/kprobes.c

index 7c0a095..263426d 100644 (file)
@@ -27,29 +27,30 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
 struct kretprobe_blackpoint kretprobe_blacklist[] = { };
 
-DEFINE_INSN_CACHE_OPS(dmainsn);
+DEFINE_INSN_CACHE_OPS(s390_insn);
 
-static void *alloc_dmainsn_page(void)
-{
-       void *page;
+static int insn_page_in_use;
+static char insn_page[PAGE_SIZE] __aligned(PAGE_SIZE);
 
-       page = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
-       if (page)
-               set_memory_x((unsigned long) page, 1);
-       return page;
+static void *alloc_s390_insn_page(void)
+{
+       if (xchg(&insn_page_in_use, 1) == 1)
+               return NULL;
+       set_memory_x((unsigned long) &insn_page, 1);
+       return &insn_page;
 }
 
-static void free_dmainsn_page(void *page)
+static void free_s390_insn_page(void *page)
 {
        set_memory_nx((unsigned long) page, 1);
-       free_page((unsigned long)page);
+       xchg(&insn_page_in_use, 0);
 }
 
-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),
+struct kprobe_insn_cache kprobe_s390_insn_slots = {
+       .mutex = __MUTEX_INITIALIZER(kprobe_s390_insn_slots.mutex),
+       .alloc = alloc_s390_insn_page,
+       .free = free_s390_insn_page,
+       .pages = LIST_HEAD_INIT(kprobe_s390_insn_slots.pages),
        .insn_size = MAX_INSN_SIZE,
 };
 
@@ -102,7 +103,7 @@ static int s390_get_insn_slot(struct kprobe *p)
         */
        p->ainsn.insn = NULL;
        if (is_kernel_addr(p->addr))
-               p->ainsn.insn = get_dmainsn_slot();
+               p->ainsn.insn = get_s390_insn_slot();
        else if (is_module_addr(p->addr))
                p->ainsn.insn = get_insn_slot();
        return p->ainsn.insn ? 0 : -ENOMEM;
@@ -114,7 +115,7 @@ static void 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);
+               free_s390_insn_slot(p->ainsn.insn, 0);
        else
                free_insn_slot(p->ainsn.insn, 0);
        p->ainsn.insn = NULL;