aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/common
diff options
context:
space:
mode:
authorDave Martin <dave.martin@linaro.org>2013-05-22 14:08:16 -0400
committerNicolas Pitre <nicolas.pitre@linaro.org>2013-09-23 18:39:19 -0400
commit0577fee283fb385afbcdb78d1f4c398d7326b68f (patch)
tree76219feca51038c85b1f5cc076a1907ffc30bd01 /arch/arm/common
parent491990e29f5d285a1b75e74785e3160716b79040 (diff)
ARM: bL_switcher: Add switch completion callback for bL_switch_request()
There is no explicit way to know when a switch started via bL_switch_request() is complete. This can lead to unpredictable behaviour when the switcher is controlled by a subsystem which makes dynamic decisions (such as cpufreq). The CPU PM notifier is not really suitable for signalling completion, because the CPU could get suspended and resumed for other, independent reasons while a switch request is in flight. Adding a whole new notifier for this seems excessive, and may tempt people to put heavyweight code on this path. This patch implements a new bL_switch_request_cb() function that allows for a per-request lightweight callback, private between the switcher and the caller of bL_switch_request_cb(). Overlapping switches on a single CPU are considered incorrect if they are requested via bL_switch_request_cb() with a callback (they will lead to an unpredictable final state without explicit external synchronisation to force the requests into a particular order). Queuing requests robustly would be overkill because only one subsystem should be attempting to control the switcher at any time. Overlapping requests of this kind will be failed with -EBUSY to indicate that the second request won't take effect and the completer will never be called for it. bL_switch_request() is retained as a wrapper round the new function, with the old, fire-and-forget semantics. In this case the last request will always win. The request may still be denied if a previous request with a completer is still pending. Signed-off-by: Dave Martin <dave.martin@linaro.org> Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Diffstat (limited to 'arch/arm/common')
-rw-r--r--arch/arm/common/bL_switcher.c53
1 files changed, 48 insertions, 5 deletions
diff --git a/arch/arm/common/bL_switcher.c b/arch/arm/common/bL_switcher.c
index 016488730cb7..34316be404d5 100644
--- a/arch/arm/common/bL_switcher.c
+++ b/arch/arm/common/bL_switcher.c
@@ -9,6 +9,7 @@
9 * published by the Free Software Foundation. 9 * published by the Free Software Foundation.
10 */ 10 */
11 11
12#include <linux/atomic.h>
12#include <linux/init.h> 13#include <linux/init.h>
13#include <linux/kernel.h> 14#include <linux/kernel.h>
14#include <linux/module.h> 15#include <linux/module.h>
@@ -25,6 +26,7 @@
25#include <linux/notifier.h> 26#include <linux/notifier.h>
26#include <linux/mm.h> 27#include <linux/mm.h>
27#include <linux/mutex.h> 28#include <linux/mutex.h>
29#include <linux/spinlock.h>
28#include <linux/string.h> 30#include <linux/string.h>
29#include <linux/sysfs.h> 31#include <linux/sysfs.h>
30#include <linux/irqchip/arm-gic.h> 32#include <linux/irqchip/arm-gic.h>
@@ -224,10 +226,13 @@ static int bL_switch_to(unsigned int new_cluster_id)
224} 226}
225 227
226struct bL_thread { 228struct bL_thread {
229 spinlock_t lock;
227 struct task_struct *task; 230 struct task_struct *task;
228 wait_queue_head_t wq; 231 wait_queue_head_t wq;
229 int wanted_cluster; 232 int wanted_cluster;
230 struct completion started; 233 struct completion started;
234 bL_switch_completion_handler completer;
235 void *completer_cookie;
231}; 236};
232 237
233static struct bL_thread bL_threads[NR_CPUS]; 238static struct bL_thread bL_threads[NR_CPUS];
@@ -237,6 +242,8 @@ static int bL_switcher_thread(void *arg)
237 struct bL_thread *t = arg; 242 struct bL_thread *t = arg;
238 struct sched_param param = { .sched_priority = 1 }; 243 struct sched_param param = { .sched_priority = 1 };
239 int cluster; 244 int cluster;
245 bL_switch_completion_handler completer;
246 void *completer_cookie;
240 247
241 sched_setscheduler_nocheck(current, SCHED_FIFO, &param); 248 sched_setscheduler_nocheck(current, SCHED_FIFO, &param);
242 complete(&t->started); 249 complete(&t->started);
@@ -247,9 +254,21 @@ static int bL_switcher_thread(void *arg)
247 wait_event_interruptible(t->wq, 254 wait_event_interruptible(t->wq,
248 t->wanted_cluster != -1 || 255 t->wanted_cluster != -1 ||
249 kthread_should_stop()); 256 kthread_should_stop());
250 cluster = xchg(&t->wanted_cluster, -1); 257
251 if (cluster != -1) 258 spin_lock(&t->lock);
259 cluster = t->wanted_cluster;
260 completer = t->completer;
261 completer_cookie = t->completer_cookie;
262 t->wanted_cluster = -1;
263 t->completer = NULL;
264 spin_unlock(&t->lock);
265
266 if (cluster != -1) {
252 bL_switch_to(cluster); 267 bL_switch_to(cluster);
268
269 if (completer)
270 completer(completer_cookie);
271 }
253 } while (!kthread_should_stop()); 272 } while (!kthread_should_stop());
254 273
255 return 0; 274 return 0;
@@ -270,16 +289,30 @@ static struct task_struct *bL_switcher_thread_create(int cpu, void *arg)
270} 289}
271 290
272/* 291/*
273 * bL_switch_request - Switch to a specific cluster for the given CPU 292 * bL_switch_request_cb - Switch to a specific cluster for the given CPU,
293 * with completion notification via a callback
274 * 294 *
275 * @cpu: the CPU to switch 295 * @cpu: the CPU to switch
276 * @new_cluster_id: the ID of the cluster to switch to. 296 * @new_cluster_id: the ID of the cluster to switch to.
297 * @completer: switch completion callback. if non-NULL,
298 * @completer(@completer_cookie) will be called on completion of
299 * the switch, in non-atomic context.
300 * @completer_cookie: opaque context argument for @completer.
277 * 301 *
278 * This function causes a cluster switch on the given CPU by waking up 302 * This function causes a cluster switch on the given CPU by waking up
279 * the appropriate switcher thread. This function may or may not return 303 * the appropriate switcher thread. This function may or may not return
280 * before the switch has occurred. 304 * before the switch has occurred.
305 *
306 * If a @completer callback function is supplied, it will be called when
307 * the switch is complete. This can be used to determine asynchronously
308 * when the switch is complete, regardless of when bL_switch_request()
309 * returns. When @completer is supplied, no new switch request is permitted
310 * for the affected CPU until after the switch is complete, and @completer
311 * has returned.
281 */ 312 */
282int bL_switch_request(unsigned int cpu, unsigned int new_cluster_id) 313int bL_switch_request_cb(unsigned int cpu, unsigned int new_cluster_id,
314 bL_switch_completion_handler completer,
315 void *completer_cookie)
283{ 316{
284 struct bL_thread *t; 317 struct bL_thread *t;
285 318
@@ -289,16 +322,25 @@ int bL_switch_request(unsigned int cpu, unsigned int new_cluster_id)
289 } 322 }
290 323
291 t = &bL_threads[cpu]; 324 t = &bL_threads[cpu];
325
292 if (IS_ERR(t->task)) 326 if (IS_ERR(t->task))
293 return PTR_ERR(t->task); 327 return PTR_ERR(t->task);
294 if (!t->task) 328 if (!t->task)
295 return -ESRCH; 329 return -ESRCH;
296 330
331 spin_lock(&t->lock);
332 if (t->completer) {
333 spin_unlock(&t->lock);
334 return -EBUSY;
335 }
336 t->completer = completer;
337 t->completer_cookie = completer_cookie;
297 t->wanted_cluster = new_cluster_id; 338 t->wanted_cluster = new_cluster_id;
339 spin_unlock(&t->lock);
298 wake_up(&t->wq); 340 wake_up(&t->wq);
299 return 0; 341 return 0;
300} 342}
301EXPORT_SYMBOL_GPL(bL_switch_request); 343EXPORT_SYMBOL_GPL(bL_switch_request_cb);
302 344
303/* 345/*
304 * Activation and configuration code. 346 * Activation and configuration code.
@@ -460,6 +502,7 @@ static int bL_switcher_enable(void)
460 502
461 for_each_online_cpu(cpu) { 503 for_each_online_cpu(cpu) {
462 struct bL_thread *t = &bL_threads[cpu]; 504 struct bL_thread *t = &bL_threads[cpu];
505 spin_lock_init(&t->lock);
463 init_waitqueue_head(&t->wq); 506 init_waitqueue_head(&t->wq);
464 init_completion(&t->started); 507 init_completion(&t->started);
465 t->wanted_cluster = -1; 508 t->wanted_cluster = -1;