x86: implement atomic text_poke() via fixmap
Use fixmaps instead of vmap/vunmap in text_poke() for avoiding page allocation and delayed unmapping. At the result of above change, text_poke() becomes atomic and can be called from stop_machine() etc. Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com> Acked-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> LKML-Reference: <49B14352.2040705@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
committed by
Ingo Molnar
parent
3945dab45a
commit
78ff7fae04
@@ -111,6 +111,8 @@ enum fixed_addresses {
|
|||||||
#ifdef CONFIG_PARAVIRT
|
#ifdef CONFIG_PARAVIRT
|
||||||
FIX_PARAVIRT_BOOTMAP,
|
FIX_PARAVIRT_BOOTMAP,
|
||||||
#endif
|
#endif
|
||||||
|
FIX_TEXT_POKE0, /* reserve 2 pages for text_poke() */
|
||||||
|
FIX_TEXT_POKE1,
|
||||||
__end_of_permanent_fixed_addresses,
|
__end_of_permanent_fixed_addresses,
|
||||||
#ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
|
#ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
|
||||||
FIX_OHCI1394_BASE,
|
FIX_OHCI1394_BASE,
|
||||||
|
@@ -13,7 +13,9 @@
|
|||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
#include <asm/vsyscall.h>
|
#include <asm/vsyscall.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
|
#include <asm/tlbflush.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
#include <asm/fixmap.h>
|
||||||
|
|
||||||
#define MAX_PATCH_LEN (255-1)
|
#define MAX_PATCH_LEN (255-1)
|
||||||
|
|
||||||
@@ -505,15 +507,16 @@ void *text_poke_early(void *addr, const void *opcode, size_t len)
|
|||||||
* It means the size must be writable atomically and the address must be aligned
|
* It means the size must be writable atomically and the address must be aligned
|
||||||
* in a way that permits an atomic write. It also makes sure we fit on a single
|
* in a way that permits an atomic write. It also makes sure we fit on a single
|
||||||
* page.
|
* page.
|
||||||
|
*
|
||||||
|
* Note: Must be called under text_mutex.
|
||||||
*/
|
*/
|
||||||
void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
|
void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
char *vaddr;
|
char *vaddr;
|
||||||
int nr_pages = 2;
|
|
||||||
struct page *pages[2];
|
struct page *pages[2];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
might_sleep();
|
|
||||||
if (!core_kernel_text((unsigned long)addr)) {
|
if (!core_kernel_text((unsigned long)addr)) {
|
||||||
pages[0] = vmalloc_to_page(addr);
|
pages[0] = vmalloc_to_page(addr);
|
||||||
pages[1] = vmalloc_to_page(addr + PAGE_SIZE);
|
pages[1] = vmalloc_to_page(addr + PAGE_SIZE);
|
||||||
@@ -523,14 +526,17 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
|
|||||||
pages[1] = virt_to_page(addr + PAGE_SIZE);
|
pages[1] = virt_to_page(addr + PAGE_SIZE);
|
||||||
}
|
}
|
||||||
BUG_ON(!pages[0]);
|
BUG_ON(!pages[0]);
|
||||||
if (!pages[1])
|
set_fixmap(FIX_TEXT_POKE0, page_to_phys(pages[0]));
|
||||||
nr_pages = 1;
|
if (pages[1])
|
||||||
vaddr = vmap(pages, nr_pages, VM_MAP, PAGE_KERNEL);
|
set_fixmap(FIX_TEXT_POKE1, page_to_phys(pages[1]));
|
||||||
BUG_ON(!vaddr);
|
vaddr = (char *)fix_to_virt(FIX_TEXT_POKE0);
|
||||||
local_irq_disable();
|
local_irq_save(flags);
|
||||||
memcpy(&vaddr[(unsigned long)addr & ~PAGE_MASK], opcode, len);
|
memcpy(&vaddr[(unsigned long)addr & ~PAGE_MASK], opcode, len);
|
||||||
local_irq_enable();
|
local_irq_restore(flags);
|
||||||
vunmap(vaddr);
|
clear_fixmap(FIX_TEXT_POKE0);
|
||||||
|
if (pages[1])
|
||||||
|
clear_fixmap(FIX_TEXT_POKE1);
|
||||||
|
local_flush_tlb();
|
||||||
sync_core();
|
sync_core();
|
||||||
/* Could also do a CLFLUSH here to speed up CPU recovery; but
|
/* Could also do a CLFLUSH here to speed up CPU recovery; but
|
||||||
that causes hangs on some VIA CPUs. */
|
that causes hangs on some VIA CPUs. */
|
||||||
|
Reference in New Issue
Block a user