aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time/clockevents.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2013-04-25 16:31:50 -0400
committerThomas Gleixner <tglx@linutronix.de>2013-05-16 05:09:18 -0400
commit03e13cf5ee60584fe0c831682c67212effb7fca4 (patch)
treeb40a9a0a88a675e4511ad6b6c0bf072b6a1ef9f4 /kernel/time/clockevents.c
parent45cb8e01b2ecef1c2afb18333e95793fa1a90281 (diff)
clockevents: Implement unbind functionality
Provide a sysfs interface to allow unbinding of clockevent devices. The device is unbound if it is unused or if there is a replacement device available. Unbinding of broadcast devices is not supported as we don't want to foster that nonsense. If no replacement device is available the unbind returns -EBUSY. Unbind is available from the kernel and through sysfs, which is necessary to drop the module refcount. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: John Stultz <john.stultz@linaro.org> Cc: Magnus Damm <magnus.damm@gmail.com> Link: http://lkml.kernel.org/r/20130425143436.499216659@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/time/clockevents.c')
-rw-r--r--kernel/time/clockevents.c125
1 files changed, 125 insertions, 0 deletions
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 0a23f4f29934..38959c866789 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -25,6 +25,13 @@ static LIST_HEAD(clockevent_devices);
25static LIST_HEAD(clockevents_released); 25static LIST_HEAD(clockevents_released);
26/* Protection for the above */ 26/* Protection for the above */
27static DEFINE_RAW_SPINLOCK(clockevents_lock); 27static DEFINE_RAW_SPINLOCK(clockevents_lock);
28/* Protection for unbind operations */
29static DEFINE_MUTEX(clockevents_mutex);
30
31struct ce_unbind {
32 struct clock_event_device *ce;
33 int res;
34};
28 35
29/** 36/**
30 * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds 37 * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
@@ -245,6 +252,90 @@ static void clockevents_notify_released(void)
245 } 252 }
246} 253}
247 254
255/*
256 * Try to install a replacement clock event device
257 */
258static int clockevents_replace(struct clock_event_device *ced)
259{
260 struct clock_event_device *dev, *newdev = NULL;
261
262 list_for_each_entry(dev, &clockevent_devices, list) {
263 if (dev == ced || dev->mode != CLOCK_EVT_MODE_UNUSED)
264 continue;
265
266 if (!tick_check_replacement(newdev, dev))
267 continue;
268
269 if (!try_module_get(dev->owner))
270 continue;
271
272 if (newdev)
273 module_put(newdev->owner);
274 newdev = dev;
275 }
276 if (newdev) {
277 tick_install_replacement(newdev);
278 list_del_init(&ced->list);
279 }
280 return newdev ? 0 : -EBUSY;
281}
282
283/*
284 * Called with clockevents_mutex and clockevents_lock held
285 */
286static int __clockevents_try_unbind(struct clock_event_device *ced, int cpu)
287{
288 /* Fast track. Device is unused */
289 if (ced->mode == CLOCK_EVT_MODE_UNUSED) {
290 list_del_init(&ced->list);
291 return 0;
292 }
293
294 return ced == per_cpu(tick_cpu_device, cpu).evtdev ? -EAGAIN : -EBUSY;
295}
296
297/*
298 * SMP function call to unbind a device
299 */
300static void __clockevents_unbind(void *arg)
301{
302 struct ce_unbind *cu = arg;
303 int res;
304
305 raw_spin_lock(&clockevents_lock);
306 res = __clockevents_try_unbind(cu->ce, smp_processor_id());
307 if (res == -EAGAIN)
308 res = clockevents_replace(cu->ce);
309 cu->res = res;
310 raw_spin_unlock(&clockevents_lock);
311}
312
313/*
314 * Issues smp function call to unbind a per cpu device. Called with
315 * clockevents_mutex held.
316 */
317static int clockevents_unbind(struct clock_event_device *ced, int cpu)
318{
319 struct ce_unbind cu = { .ce = ced, .res = -ENODEV };
320
321 smp_call_function_single(cpu, __clockevents_unbind, &cu, 1);
322 return cu.res;
323}
324
325/*
326 * Unbind a clockevents device.
327 */
328int clockevents_unbind_device(struct clock_event_device *ced, int cpu)
329{
330 int ret;
331
332 mutex_lock(&clockevents_mutex);
333 ret = clockevents_unbind(ced, cpu);
334 mutex_unlock(&clockevents_mutex);
335 return ret;
336}
337EXPORT_SYMBOL_GPL(clockevents_unbind);
338
248/** 339/**
249 * clockevents_register_device - register a clock event device 340 * clockevents_register_device - register a clock event device
250 * @dev: device to register 341 * @dev: device to register
@@ -487,6 +578,38 @@ static ssize_t sysfs_show_current_tick_dev(struct device *dev,
487} 578}
488static DEVICE_ATTR(current_device, 0444, sysfs_show_current_tick_dev, NULL); 579static DEVICE_ATTR(current_device, 0444, sysfs_show_current_tick_dev, NULL);
489 580
581/* We don't support the abomination of removable broadcast devices */
582static ssize_t sysfs_unbind_tick_dev(struct device *dev,
583 struct device_attribute *attr,
584 const char *buf, size_t count)
585{
586 char name[CS_NAME_LEN];
587 size_t ret = sysfs_get_uname(buf, name, count);
588 struct clock_event_device *ce;
589
590 if (ret < 0)
591 return ret;
592
593 ret = -ENODEV;
594 mutex_lock(&clockevents_mutex);
595 raw_spin_lock_irq(&clockevents_lock);
596 list_for_each_entry(ce, &clockevent_devices, list) {
597 if (!strcmp(ce->name, name)) {
598 ret = __clockevents_try_unbind(ce, dev->id);
599 break;
600 }
601 }
602 raw_spin_unlock_irq(&clockevents_lock);
603 /*
604 * We hold clockevents_mutex, so ce can't go away
605 */
606 if (ret == -EAGAIN)
607 ret = clockevents_unbind(ce, dev->id);
608 mutex_unlock(&clockevents_mutex);
609 return ret ? ret : count;
610}
611static DEVICE_ATTR(unbind_device, 0200, NULL, sysfs_unbind_tick_dev);
612
490#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST 613#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
491static struct device tick_bc_dev = { 614static struct device tick_bc_dev = {
492 .init_name = "broadcast", 615 .init_name = "broadcast",
@@ -529,6 +652,8 @@ static int __init tick_init_sysfs(void)
529 err = device_register(dev); 652 err = device_register(dev);
530 if (!err) 653 if (!err)
531 err = device_create_file(dev, &dev_attr_current_device); 654 err = device_create_file(dev, &dev_attr_current_device);
655 if (!err)
656 err = device_create_file(dev, &dev_attr_unbind_device);
532 if (err) 657 if (err)
533 return err; 658 return err;
534 } 659 }