From ac53c4fca42c394d8a06c7a470ae2d1d50503717 Mon Sep 17 00:00:00 2001 From: David Daney Date: Mon, 3 Dec 2012 12:44:26 -0800 Subject: [PATCH 1/5] MIPS: Avoid mcheck by flushing page range in huge_ptep_set_access_flags() Problem: 1) Huge page mapping of anonymous memory is initially invalid. Will be faulted in by copy-on-write mechanism. 2) Userspace attempts store at the end of the huge mapping. 3) TLB Refill exception handler fill TLB with a normal (4K sized) invalid page at the end of the huge mapping virtual address range. 4) Userspace restarted, and re-attempts the store at the end of the huge mapping. 5) Page from #3 is invalid, we get a fault and go to the hugepage fault handler. This tries to map a huge page and calls huge_ptep_set_access_flags() to install the mapping. 6) We just call the generic ptep_set_access_flags() to set up the page tables, but the flush there assumes a normal (4K sized) page and only tries to flush the first part of the huge page virtual address out of the TLB, since the existing entry from step #3 doesn't conflict, nothing is flushed. 7) We attempt to load the mapping into the TLB, but because it conflicts with the entry from step #3, we get a Machine Check exception. The fix: Flush the entire rage covered by the huge page in huge_ptep_set_access_flags(), and remove the optimization in local_flush_tlb_range() so that the flush actually does the correct thing. Signed-off-by: David Daney Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Cc: Hillf Danton Patchwork: https://patchwork.linux-mips.org/patch/4661/ Signed-off-by: Ralf Baechle (cherry picked from commit dd617f258cc39d36be26afee9912624a2d23112c) --- arch/mips/include/asm/hugetlb.h | 12 +++++++++++- arch/mips/mm/tlb-r4k.c | 18 ++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/mips/include/asm/hugetlb.h b/arch/mips/include/asm/hugetlb.h index bd94946a18f343..ef99db994c2f97 100644 --- a/arch/mips/include/asm/hugetlb.h +++ b/arch/mips/include/asm/hugetlb.h @@ -95,7 +95,17 @@ static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, pte_t *ptep, pte_t pte, int dirty) { - return ptep_set_access_flags(vma, addr, ptep, pte, dirty); + int changed = !pte_same(*ptep, pte); + + if (changed) { + set_pte_at(vma->vm_mm, addr, ptep, pte); + /* + * There could be some standard sized pages in there, + * get them all. + */ + flush_tlb_range(vma, addr, addr + HPAGE_SIZE); + } + return changed; } static inline pte_t huge_ptep_get(pte_t *ptep) diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index 4b9b935a070e0c..88e79ad6f811e7 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -120,18 +120,11 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, if (cpu_context(cpu, mm) != 0) { unsigned long size, flags; - int huge = is_vm_hugetlb_page(vma); ENTER_CRITICAL(flags); - if (huge) { - start = round_down(start, HPAGE_SIZE); - end = round_up(end, HPAGE_SIZE); - size = (end - start) >> HPAGE_SHIFT; - } else { - start = round_down(start, PAGE_SIZE << 1); - end = round_up(end, PAGE_SIZE << 1); - size = (end - start) >> (PAGE_SHIFT + 1); - } + start = round_down(start, PAGE_SIZE << 1); + end = round_up(end, PAGE_SIZE << 1); + size = (end - start) >> (PAGE_SHIFT + 1); if (size <= current_cpu_data.tlbsize/2) { int oldpid = read_c0_entryhi(); int newpid = cpu_asid(cpu, mm); @@ -140,10 +133,7 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, int idx; write_c0_entryhi(start | newpid); - if (huge) - start += HPAGE_SIZE; - else - start += (PAGE_SIZE << 1); + start += (PAGE_SIZE << 1); mtc0_tlbw_hazard(); tlb_probe(); tlb_probe_hazard(); From d5563715a30706b54ad9ec978b30b81d3fb320f3 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 4 Dec 2012 17:40:44 +0100 Subject: [PATCH 2/5] MIPS: N32: Fix preadv(2) and pwritev(2) entry points. By using the native syscall entry point the kernel was also expecting 64-bit iovec structures. This is broken since ddd9e91b71072b8ebe89311c3a44b077defa1756 [preadv/ pwritev: MIPS: Add preadv(2) and pwritev(2) syscalls.] which originally added these two syscalls. I walked through piles of code, including libc and couldn't find anything that would have worked around the issue so this change the API to what it should always have been. Noticed and patch suggested by Al Viro. Signed-off-by: Ralf Baechle --- arch/mips/kernel/scall64-n32.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index f6ba8381ee0186..ad8b657f867e4e 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -403,8 +403,8 @@ EXPORT(sysn32_call_table) PTR sys_dup3 /* 6290 */ PTR sys_pipe2 PTR sys_inotify_init1 - PTR sys_preadv - PTR sys_pwritev + PTR compat_sys_preadv + PTR compat_sys_pwritev PTR compat_sys_rt_tgsigqueueinfo /* 6295 */ PTR sys_perf_event_open PTR sys_accept4 From 97daa768010296d393196cbcd77549ce436a4a54 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 4 Dec 2012 18:50:48 +0100 Subject: [PATCH 3/5] MIPS: N32: Fix signalfd4 syscall entry point This needs to use the compat entry point or it's going to fail on big endian systems. Noticed by Al Viro. Signed-off-by: Ralf Baechle --- arch/mips/kernel/scall64-n32.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index ad8b657f867e4e..86ec03f0e00c48 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -397,7 +397,7 @@ EXPORT(sysn32_call_table) PTR sys_timerfd_create PTR compat_sys_timerfd_gettime /* 6285 */ PTR compat_sys_timerfd_settime - PTR sys_signalfd4 + PTR compat_sys_signalfd4 PTR sys_eventfd2 PTR sys_epoll_create1 PTR sys_dup3 /* 6290 */ From 2d33976fb395a5b9cb00f2c24132b3f272eb9a9d Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Fri, 8 Jun 2012 07:10:27 +0100 Subject: [PATCH 4/5] MIPS: R3000/R3081: Fix CPU detection. Broken since e05ea74fc56f347f872ef9946d27c53e8bf20864 (lmo) rsp. cea7e2dfdef53fe55f359d00da562a268be06fd2 (kernel.org) [MIPS: Sort out CPU type to name translation.] These CPUs are no longer very popular to say the least ... Signed-off-by: Ralf Baechle Reported-by: Murphy McCauley --- arch/mips/kernel/cpu-probe.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index b1fb7af3c35058..cce3782c96c9b7 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -510,7 +510,6 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) c->cputype = CPU_R3000A; __cpu_name[cpu] = "R3000A"; } - break; } else { c->cputype = CPU_R3000; __cpu_name[cpu] = "R3000"; From c90e6fbb220d44988cb65af3707367c57cdb65a8 Mon Sep 17 00:00:00 2001 From: Dmitry Adamushko Date: Thu, 5 Apr 2012 20:37:34 +0200 Subject: [PATCH 5/5] MIPS: Fix endless loop when processing signals for kernel tasks The problem occurs [1] when a kernel-mode task returns from a system call with a pending signal. A real-life scenario is a child of 'khelper' returning from a failed kernel_execve() in ____call_usermodehelper() [ kernel/kmod.c ]. kernel_execve() fails due to a pending SIGKILL, which is the result of "kill -9 -1" (at least, busybox's init does it upon reboot). The loop is as follows: * syscall_exit_work: - work_pending: // start_of_the_loop - work_notifysig: - do_notify_resume() - do_signal() - if (!user_mode(regs)) return; - resume_userspace // TIF_SIGPENDING is still set - work_pending // so we call work_pending => goto // start_of_the_loop More information can be found in another LKML thread: http://www.serverphorums.com/read.php?12,457826 [1] The problem was also reproduced on !CONFIG_VM86 x86, and the following fix was accepted. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=29a2e2836ff9ea65a603c89df217f4198973a74f Signed-off-by: Dmitry Adamushko Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3571/ Signed-off-by: Ralf Baechle --- arch/mips/kernel/entry.S | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index a6c133212003ed..9b00362f32f6d9 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -36,6 +36,11 @@ FEXPORT(ret_from_exception) FEXPORT(ret_from_irq) LONG_S s0, TI_REGS($28) FEXPORT(__ret_from_irq) +/* + * We can be coming here from a syscall done in the kernel space, + * e.g. a failed kernel_execve(). + */ +resume_userspace_check: LONG_L t0, PT_STATUS(sp) # returning to kernel mode? andi t0, t0, KU_USER beqz t0, resume_kernel @@ -162,7 +167,7 @@ work_notifysig: # deal with pending signals and move a0, sp li a1, 0 jal do_notify_resume # a2 already loaded - j resume_userspace + j resume_userspace_check FEXPORT(syscall_exit_partial) local_irq_disable # make sure need_resched doesn't