diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-12-18 09:34:43 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-12-18 09:53:41 -0500 |
commit | 797245f5da543074ee7db0e0516da744c89aa17f (patch) | |
tree | 429a08d54bb870c720139a3f04ad27ded7f93a8f /arch/arm | |
parent | 2395d66d09ce10c25b6756575c4aeb219760d1fc (diff) |
ARM: Convert VFP/Crunch/XscaleCP thread_release() to exit_thread()
This avoids races in the VFP code where the dead thread may have
state on another CPU. By moving this code to exit_thread(), we
will be running as the thread, and therefore be running on the
current CPU.
This means that we can ensure that the only local state is accessed
in the thread notifiers.
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/include/asm/thread_notify.h | 2 | ||||
-rw-r--r-- | arch/arm/kernel/crunch.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 12 | ||||
-rw-r--r-- | arch/arm/kernel/xscale-cp0.c | 2 | ||||
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 19 |
5 files changed, 19 insertions, 18 deletions
diff --git a/arch/arm/include/asm/thread_notify.h b/arch/arm/include/asm/thread_notify.h index f27379d7f72a..c4391ba20350 100644 --- a/arch/arm/include/asm/thread_notify.h +++ b/arch/arm/include/asm/thread_notify.h | |||
@@ -41,7 +41,7 @@ static inline void thread_notify(unsigned long rc, struct thread_info *thread) | |||
41 | * These are the reason codes for the thread notifier. | 41 | * These are the reason codes for the thread notifier. |
42 | */ | 42 | */ |
43 | #define THREAD_NOTIFY_FLUSH 0 | 43 | #define THREAD_NOTIFY_FLUSH 0 |
44 | #define THREAD_NOTIFY_RELEASE 1 | 44 | #define THREAD_NOTIFY_EXIT 1 |
45 | #define THREAD_NOTIFY_SWITCH 2 | 45 | #define THREAD_NOTIFY_SWITCH 2 |
46 | 46 | ||
47 | #endif | 47 | #endif |
diff --git a/arch/arm/kernel/crunch.c b/arch/arm/kernel/crunch.c index 769abe15cf91..25ef223ba7f3 100644 --- a/arch/arm/kernel/crunch.c +++ b/arch/arm/kernel/crunch.c | |||
@@ -51,7 +51,7 @@ static int crunch_do(struct notifier_block *self, unsigned long cmd, void *t) | |||
51 | * initialised state information on the first fault. | 51 | * initialised state information on the first fault. |
52 | */ | 52 | */ |
53 | 53 | ||
54 | case THREAD_NOTIFY_RELEASE: | 54 | case THREAD_NOTIFY_EXIT: |
55 | crunch_task_release(thread); | 55 | crunch_task_release(thread); |
56 | break; | 56 | break; |
57 | 57 | ||
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 0d96d0171c05..67304138a2ca 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
@@ -274,17 +274,18 @@ void show_regs(struct pt_regs * regs) | |||
274 | __backtrace(); | 274 | __backtrace(); |
275 | } | 275 | } |
276 | 276 | ||
277 | ATOMIC_NOTIFIER_HEAD(thread_notify_head); | ||
278 | |||
279 | EXPORT_SYMBOL_GPL(thread_notify_head); | ||
280 | |||
277 | /* | 281 | /* |
278 | * Free current thread data structures etc.. | 282 | * Free current thread data structures etc.. |
279 | */ | 283 | */ |
280 | void exit_thread(void) | 284 | void exit_thread(void) |
281 | { | 285 | { |
286 | thread_notify(THREAD_NOTIFY_EXIT, current_thread_info()); | ||
282 | } | 287 | } |
283 | 288 | ||
284 | ATOMIC_NOTIFIER_HEAD(thread_notify_head); | ||
285 | |||
286 | EXPORT_SYMBOL_GPL(thread_notify_head); | ||
287 | |||
288 | void flush_thread(void) | 289 | void flush_thread(void) |
289 | { | 290 | { |
290 | struct thread_info *thread = current_thread_info(); | 291 | struct thread_info *thread = current_thread_info(); |
@@ -299,9 +300,6 @@ void flush_thread(void) | |||
299 | 300 | ||
300 | void release_thread(struct task_struct *dead_task) | 301 | void release_thread(struct task_struct *dead_task) |
301 | { | 302 | { |
302 | struct thread_info *thread = task_thread_info(dead_task); | ||
303 | |||
304 | thread_notify(THREAD_NOTIFY_RELEASE, thread); | ||
305 | } | 303 | } |
306 | 304 | ||
307 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); | 305 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); |
diff --git a/arch/arm/kernel/xscale-cp0.c b/arch/arm/kernel/xscale-cp0.c index 17127db906fa..1796157e3dd5 100644 --- a/arch/arm/kernel/xscale-cp0.c +++ b/arch/arm/kernel/xscale-cp0.c | |||
@@ -70,7 +70,7 @@ static int iwmmxt_do(struct notifier_block *self, unsigned long cmd, void *t) | |||
70 | * initialised state information on the first fault. | 70 | * initialised state information on the first fault. |
71 | */ | 71 | */ |
72 | 72 | ||
73 | case THREAD_NOTIFY_RELEASE: | 73 | case THREAD_NOTIFY_EXIT: |
74 | iwmmxt_task_release(thread); | 74 | iwmmxt_task_release(thread); |
75 | break; | 75 | break; |
76 | 76 | ||
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index aed05bc3c2ea..f60a5400a25b 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
@@ -63,14 +63,15 @@ static void vfp_thread_flush(struct thread_info *thread) | |||
63 | put_cpu(); | 63 | put_cpu(); |
64 | } | 64 | } |
65 | 65 | ||
66 | static void vfp_thread_release(struct thread_info *thread) | 66 | static void vfp_thread_exit(struct thread_info *thread) |
67 | { | 67 | { |
68 | /* release case: Per-thread VFP cleanup. */ | 68 | /* release case: Per-thread VFP cleanup. */ |
69 | union vfp_state *vfp = &thread->vfpstate; | 69 | union vfp_state *vfp = &thread->vfpstate; |
70 | unsigned int cpu = thread->cpu; | 70 | unsigned int cpu = get_cpu(); |
71 | 71 | ||
72 | if (last_VFP_context[cpu] == vfp) | 72 | if (last_VFP_context[cpu] == vfp) |
73 | last_VFP_context[cpu] = NULL; | 73 | last_VFP_context[cpu] = NULL; |
74 | put_cpu(); | ||
74 | } | 75 | } |
75 | 76 | ||
76 | /* | 77 | /* |
@@ -88,11 +89,13 @@ static void vfp_thread_release(struct thread_info *thread) | |||
88 | * but may change at any time. | 89 | * but may change at any time. |
89 | * - we could be preempted if tree preempt rcu is enabled, so | 90 | * - we could be preempted if tree preempt rcu is enabled, so |
90 | * it is unsafe to use thread->cpu. | 91 | * it is unsafe to use thread->cpu. |
91 | * THREAD_NOTIFY_RELEASE: | 92 | * THREAD_NOTIFY_EXIT |
92 | * - the thread (v) will not be running on any CPU; it is a dead thread. | 93 | * - the thread (v) will be running on the local CPU, so |
93 | * - thread->cpu will be the last CPU the thread ran on, which may not | 94 | * v === current_thread_info() |
94 | * be the current CPU. | 95 | * - thread->cpu is the local CPU number at the time it is accessed, |
95 | * - we could be preempted if tree preempt rcu is enabled. | 96 | * but may change at any time. |
97 | * - we could be preempted if tree preempt rcu is enabled, so | ||
98 | * it is unsafe to use thread->cpu. | ||
96 | */ | 99 | */ |
97 | static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) | 100 | static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) |
98 | { | 101 | { |
@@ -133,7 +136,7 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) | |||
133 | if (cmd == THREAD_NOTIFY_FLUSH) | 136 | if (cmd == THREAD_NOTIFY_FLUSH) |
134 | vfp_thread_flush(thread); | 137 | vfp_thread_flush(thread); |
135 | else | 138 | else |
136 | vfp_thread_release(thread); | 139 | vfp_thread_exit(thread); |
137 | 140 | ||
138 | return NOTIFY_DONE; | 141 | return NOTIFY_DONE; |
139 | } | 142 | } |