Commit 67e15fa5 authored by Russell King's avatar Russell King

ARM: mm: fix alignment handler faults under memory pressure

When the system has high memory pressure, the page containing the
instruction may be paged out.  Using probe_kernel_address() means that
if the page is swapped out, the resulting page fault will not be
handled because page faults are disabled by this function.

Use get_user() to read the instruction instead.
Reported-by: default avatarJing Xiangfeng <jingxiangfeng@huawei.com>
Fixes: b255188f ("ARM: fix scheduling while atomic warning in alignment handling code")
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
parent 4d856f72
...@@ -767,6 +767,36 @@ do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs, ...@@ -767,6 +767,36 @@ do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs,
return NULL; return NULL;
} }
static int alignment_get_arm(struct pt_regs *regs, u32 *ip, unsigned long *inst)
{
u32 instr = 0;
int fault;
if (user_mode(regs))
fault = get_user(instr, ip);
else
fault = probe_kernel_address(ip, instr);
*inst = __mem_to_opcode_arm(instr);
return fault;
}
static int alignment_get_thumb(struct pt_regs *regs, u16 *ip, u16 *inst)
{
u16 instr = 0;
int fault;
if (user_mode(regs))
fault = get_user(instr, ip);
else
fault = probe_kernel_address(ip, instr);
*inst = __mem_to_opcode_thumb16(instr);
return fault;
}
static int static int
do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{ {
...@@ -774,10 +804,10 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) ...@@ -774,10 +804,10 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
unsigned long instr = 0, instrptr; unsigned long instr = 0, instrptr;
int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs); int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs);
unsigned int type; unsigned int type;
unsigned int fault;
u16 tinstr = 0; u16 tinstr = 0;
int isize = 4; int isize = 4;
int thumb2_32b = 0; int thumb2_32b = 0;
int fault;
if (interrupts_enabled(regs)) if (interrupts_enabled(regs))
local_irq_enable(); local_irq_enable();
...@@ -786,15 +816,14 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) ...@@ -786,15 +816,14 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
if (thumb_mode(regs)) { if (thumb_mode(regs)) {
u16 *ptr = (u16 *)(instrptr & ~1); u16 *ptr = (u16 *)(instrptr & ~1);
fault = probe_kernel_address(ptr, tinstr);
tinstr = __mem_to_opcode_thumb16(tinstr); fault = alignment_get_thumb(regs, ptr, &tinstr);
if (!fault) { if (!fault) {
if (cpu_architecture() >= CPU_ARCH_ARMv7 && if (cpu_architecture() >= CPU_ARCH_ARMv7 &&
IS_T32(tinstr)) { IS_T32(tinstr)) {
/* Thumb-2 32-bit */ /* Thumb-2 32-bit */
u16 tinst2 = 0; u16 tinst2;
fault = probe_kernel_address(ptr + 1, tinst2); fault = alignment_get_thumb(regs, ptr + 1, &tinst2);
tinst2 = __mem_to_opcode_thumb16(tinst2);
instr = __opcode_thumb32_compose(tinstr, tinst2); instr = __opcode_thumb32_compose(tinstr, tinst2);
thumb2_32b = 1; thumb2_32b = 1;
} else { } else {
...@@ -803,8 +832,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) ...@@ -803,8 +832,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
} }
} }
} else { } else {
fault = probe_kernel_address((void *)instrptr, instr); fault = alignment_get_arm(regs, (void *)instrptr, &instr);
instr = __mem_to_opcode_arm(instr);
} }
if (fault) { if (fault) {
......
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