Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: Fix watchpoint restoration

Commit f51246efee2b ("MIPS: Get rid of finish_arch_switch().") moved the
__restore_watch() call from finish_arch_switch() (i.e. after resume()
returns) to before the resume() call in switch_to(). This results in
watchpoints only being restored when a task is descheduled, preventing
the watchpoints from being effective most of the time, except due to
chance before the watchpoints are lazily removed.

Fix the call sequence from switch_to() through to
mips_install_watch_registers() to pass the task_struct pointer of the
next task, instead of using current. This allows the watchpoints for the
next (non-current) task to be restored without reintroducing
finish_arch_switch().

Fixes: f51246efee2b ("MIPS: Get rid of finish_arch_switch().")
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: <stable@vger.kernel.org> # 4.3.x-
Patchwork: https://patchwork.linux-mips.org/patch/12726/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

James Hogan and committed by
Ralf Baechle
a7e89326 81a76d71

+9 -10
+1 -1
arch/mips/include/asm/switch_to.h
··· 105 105 __clear_software_ll_bit(); \ 106 106 if (cpu_has_userlocal) \ 107 107 write_c0_userlocal(task_thread_info(next)->tp_value); \ 108 - __restore_watch(); \ 108 + __restore_watch(next); \ 109 109 (last) = resume(prev, next, task_thread_info(next)); \ 110 110 } while (0) 111 111
+5 -5
arch/mips/include/asm/watch.h
··· 12 12 13 13 #include <asm/mipsregs.h> 14 14 15 - void mips_install_watch_registers(void); 15 + void mips_install_watch_registers(struct task_struct *t); 16 16 void mips_read_watch_registers(void); 17 17 void mips_clear_watch_registers(void); 18 18 void mips_probe_watch_registers(struct cpuinfo_mips *c); 19 19 20 20 #ifdef CONFIG_HARDWARE_WATCHPOINTS 21 - #define __restore_watch() do { \ 21 + #define __restore_watch(task) do { \ 22 22 if (unlikely(test_bit(TIF_LOAD_WATCH, \ 23 - &current_thread_info()->flags))) { \ 24 - mips_install_watch_registers(); \ 23 + &task_thread_info(task)->flags))) { \ 24 + mips_install_watch_registers(task); \ 25 25 } \ 26 26 } while (0) 27 27 28 28 #else 29 - #define __restore_watch() do {} while (0) 29 + #define __restore_watch(task) do {} while (0) 30 30 #endif 31 31 32 32 #endif /* _ASM_WATCH_H */
+1 -1
arch/mips/kernel/pm.c
··· 56 56 write_c0_userlocal(current_thread_info()->tp_value); 57 57 58 58 /* Restore watch registers */ 59 - __restore_watch(); 59 + __restore_watch(current); 60 60 } 61 61 62 62 /**
+2 -3
arch/mips/kernel/watch.c
··· 15 15 * Install the watch registers for the current thread. A maximum of 16 16 * four registers are installed although the machine may have more. 17 17 */ 18 - void mips_install_watch_registers(void) 18 + void mips_install_watch_registers(struct task_struct *t) 19 19 { 20 - struct mips3264_watch_reg_state *watches = 21 - &current->thread.watch.mips3264; 20 + struct mips3264_watch_reg_state *watches = &t->thread.watch.mips3264; 22 21 switch (current_cpu_data.watch_reg_use_cnt) { 23 22 default: 24 23 BUG();