933 lines · 23.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Alarmtimer interface * * This interface provides a timer which is similarto hrtimers, * but triggers a RTC alarm if the box is suspend. * * This interface is influenced by the Android RTC Alarm timer * interface. * * Copyright (C) 2010 IBM Corperation * * Author: John Stultz <john.stultz@linaro.org> */#include <linux/time.h>#include <linux/hrtimer.h>#include <linux/timerqueue.h>#include <linux/rtc.h>#include <linux/sched/signal.h>#include <linux/sched/debug.h>#include <linux/alarmtimer.h>#include <linux/mutex.h>#include <linux/platform_device.h>#include <linux/posix-timers.h>#include <linux/workqueue.h>#include <linux/freezer.h>#include <linux/compat.h>#include <linux/module.h>#include <linux/time_namespace.h> #include "posix-timers.h" #define CREATE_TRACE_POINTS#include <trace/events/alarmtimer.h> /** * struct alarm_base - Alarm timer bases * @lock:		Lock for syncrhonized access to the base * @timerqueue:		Timerqueue head managing the list of events * @get_ktime:		Function to read the time correlating to the base * @get_timespec:	Function to read the namespace time correlating to the base * @base_clockid:	clockid for the base */static struct alarm_base {	spinlock_t		lock;	struct timerqueue_head	timerqueue;	ktime_t			(*get_ktime)(void);	void			(*get_timespec)(struct timespec64 *tp);	clockid_t		base_clockid;} alarm_bases[ALARM_NUMTYPE]; #if defined(CONFIG_POSIX_TIMERS) || defined(CONFIG_RTC_CLASS)/* freezer information to handle clock_nanosleep triggered wakeups */static enum alarmtimer_type freezer_alarmtype;static ktime_t freezer_expires;static ktime_t freezer_delta;static DEFINE_SPINLOCK(freezer_delta_lock);#endif #ifdef CONFIG_RTC_CLASS/* rtc timer and device for setting alarm wakeups at suspend */static struct rtc_timer		rtctimer;static struct rtc_device	*rtcdev;static DEFINE_SPINLOCK(rtcdev_lock); /** * alarmtimer_get_rtcdev - Return selected rtcdevice * * This function returns the rtc device to use for wakealarms. */struct rtc_device *alarmtimer_get_rtcdev(void){	unsigned long flags;	struct rtc_device *ret; 	spin_lock_irqsave(&rtcdev_lock, flags);	ret = rtcdev;	spin_unlock_irqrestore(&rtcdev_lock, flags); 	return ret;}EXPORT_SYMBOL_GPL(alarmtimer_get_rtcdev); static int alarmtimer_rtc_add_device(struct device *dev,				struct class_interface *class_intf){	unsigned long flags;	struct rtc_device *rtc = to_rtc_device(dev);	struct platform_device *pdev;	int ret = 0; 	if (rtcdev)		return -EBUSY; 	if (!rtc->ops->set_alarm)		return -1;	if (!device_may_wakeup(rtc->dev.parent))		return -1; 	pdev = platform_device_register_data(dev, "alarmtimer",					     PLATFORM_DEVID_AUTO, NULL, 0);	if (!IS_ERR(pdev))		device_init_wakeup(&pdev->dev, true); 	spin_lock_irqsave(&rtcdev_lock, flags);	if (!IS_ERR(pdev) && !rtcdev) {		if (!try_module_get(rtc->owner)) {			ret = -1;			goto unlock;		} 		rtcdev = rtc;		/* hold a reference so it doesn't go away */		get_device(dev);		pdev = NULL;	} else {		ret = -1;	}unlock:	spin_unlock_irqrestore(&rtcdev_lock, flags); 	platform_device_unregister(pdev); 	return ret;} static inline void alarmtimer_rtc_timer_init(void){	rtc_timer_init(&rtctimer, NULL, NULL);} static struct class_interface alarmtimer_rtc_interface = {	.add_dev = &alarmtimer_rtc_add_device,}; static int alarmtimer_rtc_interface_setup(void){	alarmtimer_rtc_interface.class = rtc_class;	return class_interface_register(&alarmtimer_rtc_interface);}static void alarmtimer_rtc_interface_remove(void){	class_interface_unregister(&alarmtimer_rtc_interface);}#elsestatic inline int alarmtimer_rtc_interface_setup(void) { return 0; }static inline void alarmtimer_rtc_interface_remove(void) { }static inline void alarmtimer_rtc_timer_init(void) { }#endif /** * alarmtimer_enqueue - Adds an alarm timer to an alarm_base timerqueue * @base: pointer to the base where the timer is being run * @alarm: pointer to alarm being enqueued. * * Adds alarm to a alarm_base timerqueue * * Must hold base->lock when calling. */static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm){	if (alarm->state & ALARMTIMER_STATE_ENQUEUED)		timerqueue_del(&base->timerqueue, &alarm->node); 	timerqueue_add(&base->timerqueue, &alarm->node);	alarm->state |= ALARMTIMER_STATE_ENQUEUED;} /** * alarmtimer_dequeue - Removes an alarm timer from an alarm_base timerqueue * @base: pointer to the base where the timer is running * @alarm: pointer to alarm being removed * * Removes alarm to a alarm_base timerqueue * * Must hold base->lock when calling. */static void alarmtimer_dequeue(struct alarm_base *base, struct alarm *alarm){	if (!(alarm->state & ALARMTIMER_STATE_ENQUEUED))		return; 	timerqueue_del(&base->timerqueue, &alarm->node);	alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;}  /** * alarmtimer_fired - Handles alarm hrtimer being fired. * @timer: pointer to hrtimer being run * * When a alarm timer fires, this runs through the timerqueue to * see which alarms expired, and runs those. If there are more alarm * timers queued for the future, we set the hrtimer to fire when * the next future alarm timer expires. */static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer){	struct alarm *alarm = container_of(timer, struct alarm, timer);	struct alarm_base *base = &alarm_bases[alarm->type];	unsigned long flags;	int ret = HRTIMER_NORESTART;	int restart = ALARMTIMER_NORESTART; 	spin_lock_irqsave(&base->lock, flags);	alarmtimer_dequeue(base, alarm);	spin_unlock_irqrestore(&base->lock, flags); 	if (alarm->function)		restart = alarm->function(alarm, base->get_ktime()); 	spin_lock_irqsave(&base->lock, flags);	if (restart != ALARMTIMER_NORESTART) {		hrtimer_set_expires(&alarm->timer, alarm->node.expires);		alarmtimer_enqueue(base, alarm);		ret = HRTIMER_RESTART;	}	spin_unlock_irqrestore(&base->lock, flags); 	trace_alarmtimer_fired(alarm, base->get_ktime());	return ret; } ktime_t alarm_expires_remaining(const struct alarm *alarm){	struct alarm_base *base = &alarm_bases[alarm->type];	return ktime_sub(alarm->node.expires, base->get_ktime());}EXPORT_SYMBOL_GPL(alarm_expires_remaining); #ifdef CONFIG_RTC_CLASS/** * alarmtimer_suspend - Suspend time callback * @dev: unused * * When we are going into suspend, we look through the bases * to see which is the soonest timer to expire. We then * set an rtc timer to fire that far into the future, which * will wake us from suspend. */static int alarmtimer_suspend(struct device *dev){	ktime_t min, now, expires;	int i, ret, type;	struct rtc_device *rtc;	unsigned long flags;	struct rtc_time tm; 	spin_lock_irqsave(&freezer_delta_lock, flags);	min = freezer_delta;	expires = freezer_expires;	type = freezer_alarmtype;	freezer_delta = 0;	spin_unlock_irqrestore(&freezer_delta_lock, flags); 	rtc = alarmtimer_get_rtcdev();	/* If we have no rtcdev, just return */	if (!rtc)		return 0; 	/* Find the soonest timer to expire*/	for (i = 0; i < ALARM_NUMTYPE; i++) {		struct alarm_base *base = &alarm_bases[i];		struct timerqueue_node *next;		ktime_t delta; 		spin_lock_irqsave(&base->lock, flags);		next = timerqueue_getnext(&base->timerqueue);		spin_unlock_irqrestore(&base->lock, flags);		if (!next)			continue;		delta = ktime_sub(next->expires, base->get_ktime());		if (!min || (delta < min)) {			expires = next->expires;			min = delta;			type = i;		}	}	if (min == 0)		return 0; 	if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {		pm_wakeup_event(dev, 2 * MSEC_PER_SEC);		return -EBUSY;	} 	trace_alarmtimer_suspend(expires, type); 	/* Setup an rtc timer to fire that far in the future */	rtc_timer_cancel(rtc, &rtctimer);	rtc_read_time(rtc, &tm);	now = rtc_tm_to_ktime(tm);	now = ktime_add(now, min); 	/* Set alarm, if in the past reject suspend briefly to handle */	ret = rtc_timer_start(rtc, &rtctimer, now, 0);	if (ret < 0)		pm_wakeup_event(dev, MSEC_PER_SEC);	return ret;} static int alarmtimer_resume(struct device *dev){	struct rtc_device *rtc; 	rtc = alarmtimer_get_rtcdev();	if (rtc)		rtc_timer_cancel(rtc, &rtctimer);	return 0;} #elsestatic int alarmtimer_suspend(struct device *dev){	return 0;} static int alarmtimer_resume(struct device *dev){	return 0;}#endif static void__alarm_init(struct alarm *alarm, enum alarmtimer_type type,	     enum alarmtimer_restart (*function)(struct alarm *, ktime_t)){	timerqueue_init(&alarm->node);	alarm->timer.function = alarmtimer_fired;	alarm->function = function;	alarm->type = type;	alarm->state = ALARMTIMER_STATE_INACTIVE;} /** * alarm_init - Initialize an alarm structure * @alarm: ptr to alarm to be initialized * @type: the type of the alarm * @function: callback that is run when the alarm fires */void alarm_init(struct alarm *alarm, enum alarmtimer_type type,		enum alarmtimer_restart (*function)(struct alarm *, ktime_t)){	hrtimer_init(&alarm->timer, alarm_bases[type].base_clockid,		     HRTIMER_MODE_ABS);	__alarm_init(alarm, type, function);}EXPORT_SYMBOL_GPL(alarm_init); /** * alarm_start - Sets an absolute alarm to fire * @alarm: ptr to alarm to set * @start: time to run the alarm */void alarm_start(struct alarm *alarm, ktime_t start){	struct alarm_base *base = &alarm_bases[alarm->type];	unsigned long flags; 	spin_lock_irqsave(&base->lock, flags);	alarm->node.expires = start;	alarmtimer_enqueue(base, alarm);	hrtimer_start(&alarm->timer, alarm->node.expires, HRTIMER_MODE_ABS);	spin_unlock_irqrestore(&base->lock, flags); 	trace_alarmtimer_start(alarm, base->get_ktime());}EXPORT_SYMBOL_GPL(alarm_start); /** * alarm_start_relative - Sets a relative alarm to fire * @alarm: ptr to alarm to set * @start: time relative to now to run the alarm */void alarm_start_relative(struct alarm *alarm, ktime_t start){	struct alarm_base *base = &alarm_bases[alarm->type]; 	start = ktime_add_safe(start, base->get_ktime());	alarm_start(alarm, start);}EXPORT_SYMBOL_GPL(alarm_start_relative); void alarm_restart(struct alarm *alarm){	struct alarm_base *base = &alarm_bases[alarm->type];	unsigned long flags; 	spin_lock_irqsave(&base->lock, flags);	hrtimer_set_expires(&alarm->timer, alarm->node.expires);	hrtimer_restart(&alarm->timer);	alarmtimer_enqueue(base, alarm);	spin_unlock_irqrestore(&base->lock, flags);}EXPORT_SYMBOL_GPL(alarm_restart); /** * alarm_try_to_cancel - Tries to cancel an alarm timer * @alarm: ptr to alarm to be canceled * * Returns 1 if the timer was canceled, 0 if it was not running, * and -1 if the callback was running */int alarm_try_to_cancel(struct alarm *alarm){	struct alarm_base *base = &alarm_bases[alarm->type];	unsigned long flags;	int ret; 	spin_lock_irqsave(&base->lock, flags);	ret = hrtimer_try_to_cancel(&alarm->timer);	if (ret >= 0)		alarmtimer_dequeue(base, alarm);	spin_unlock_irqrestore(&base->lock, flags); 	trace_alarmtimer_cancel(alarm, base->get_ktime());	return ret;}EXPORT_SYMBOL_GPL(alarm_try_to_cancel);  /** * alarm_cancel - Spins trying to cancel an alarm timer until it is done * @alarm: ptr to alarm to be canceled * * Returns 1 if the timer was canceled, 0 if it was not active. */int alarm_cancel(struct alarm *alarm){	for (;;) {		int ret = alarm_try_to_cancel(alarm);		if (ret >= 0)			return ret;		hrtimer_cancel_wait_running(&alarm->timer);	}}EXPORT_SYMBOL_GPL(alarm_cancel);  u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval){	u64 overrun = 1;	ktime_t delta; 	delta = ktime_sub(now, alarm->node.expires); 	if (delta < 0)		return 0; 	if (unlikely(delta >= interval)) {		s64 incr = ktime_to_ns(interval); 		overrun = ktime_divns(delta, incr); 		alarm->node.expires = ktime_add_ns(alarm->node.expires,							incr*overrun); 		if (alarm->node.expires > now)			return overrun;		/*		 * This (and the ktime_add() below) is the		 * correction for exact:		 */		overrun++;	} 	alarm->node.expires = ktime_add_safe(alarm->node.expires, interval);	return overrun;}EXPORT_SYMBOL_GPL(alarm_forward); u64 alarm_forward_now(struct alarm *alarm, ktime_t interval){	struct alarm_base *base = &alarm_bases[alarm->type]; 	return alarm_forward(alarm, base->get_ktime(), interval);}EXPORT_SYMBOL_GPL(alarm_forward_now); #ifdef CONFIG_POSIX_TIMERS static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type){	struct alarm_base *base;	unsigned long flags;	ktime_t delta; 	switch(type) {	case ALARM_REALTIME:		base = &alarm_bases[ALARM_REALTIME];		type = ALARM_REALTIME_FREEZER;		break;	case ALARM_BOOTTIME:		base = &alarm_bases[ALARM_BOOTTIME];		type = ALARM_BOOTTIME_FREEZER;		break;	default:		WARN_ONCE(1, "Invalid alarm type: %d\n", type);		return;

Lines 1–500 of 933.