/** * 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
*/ staticstruct 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];
#ifdef CONFIG_RTC_CLASS /* rtc timer and device for setting alarm wakeups at suspend */ staticstruct rtc_timer rtctimer; staticstruct 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)
{ struct rtc_device *ret;
guard(spinlock_irqsave)(&rtcdev_lock);
ret = rtcdev;
scoped_guard(spinlock_irqsave, &rtcdev_lock) { if (!IS_ERR(pdev) && !rtcdev && try_module_get(rtc->owner)) {
rtcdev = rtc; /* hold a reference so it doesn't go away */
get_device(dev);
pdev = NULL;
} else {
ret = -1;
}
}
/** * 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.
*/ staticvoid alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
{ if (alarm->state & ALARMTIMER_STATE_ENQUEUED)
timerqueue_del(&base->timerqueue, &alarm->node);
/** * 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.
*/ staticvoid alarmtimer_dequeue(struct alarm_base *base, struct alarm *alarm)
{ if (!(alarm->state & ALARMTIMER_STATE_ENQUEUED)) return;
/** * 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.
*/ staticenum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
{ struct alarm *alarm = container_of(timer, struct alarm, timer); struct alarm_base *base = &alarm_bases[alarm->type];
#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.
*/ staticint alarmtimer_suspend(struct device *dev)
{
ktime_t min, now, expires; struct rtc_device *rtc; struct rtc_time tm; int i, ret, type;
scoped_guard(spinlock_irqsave, &freezer_delta_lock) {
min = freezer_delta;
expires = freezer_expires;
type = freezer_alarmtype;
freezer_delta = 0;
}
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;
scoped_guard(spinlock_irqsave, &base->lock)
next = timerqueue_getnext(&base->timerqueue); 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;
/* 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);
/* * If the RTC alarm timer only supports a limited time offset, set the * alarm time to the maximum supported value. * The system may wake up earlier (possibly much earlier) than expected * when the alarmtimer runs. This is the best the kernel can do if * the alarmtimer exceeds the time that the rtc device can be programmed * for.
*/
min = rtc_bound_alarmtime(rtc, min);
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;
}
/** * 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, void (*function)(struct alarm *, ktime_t))
{
hrtimer_setup(&alarm->timer, alarmtimer_fired, 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];
/** * 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];
/** * 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]; int ret;
scoped_guard(spinlock_irqsave, &base->lock) {
ret = hrtimer_try_to_cancel(&alarm->timer); if (ret >= 0)
alarmtimer_dequeue(base, alarm);
}
/** * 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);
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;
}
/** * alarm_timer_forward - Posix timer callback for forwarding timer * @timr: Pointer to the posixtimer data struct * @now: Current time to forward the timer against
*/ static s64 alarm_timer_forward(struct k_itimer *timr, ktime_t now)
{ struct alarm *alarm = &timr->it.alarm.alarmtimer;
/** * alarm_timer_remaining - Posix timer callback to retrieve remaining time * @timr: Pointer to the posixtimer data struct * @now: Current time to calculate against
*/ static ktime_t alarm_timer_remaining(struct k_itimer *timr, ktime_t now)
{ struct alarm *alarm = &timr->it.alarm.alarmtimer;
return ktime_sub(alarm->node.expires, now);
}
/** * alarm_timer_try_to_cancel - Posix timer callback to cancel a timer * @timr: Pointer to the posixtimer data struct
*/ staticint alarm_timer_try_to_cancel(struct k_itimer *timr)
{ return alarm_try_to_cancel(&timr->it.alarm.alarmtimer);
}
/** * alarm_timer_wait_running - Posix timer callback to wait for a timer * @timr: Pointer to the posixtimer data struct * * Called from the core code when timer cancel detected that the callback * is running. @timr is unlocked and rcu read lock is held to prevent it * from being freed.
*/ staticvoid alarm_timer_wait_running(struct k_itimer *timr)
{
hrtimer_cancel_wait_running(&timr->it.alarm.alarmtimer.timer);
}
/** * alarm_timer_arm - Posix timer callback to arm a timer * @timr: Pointer to the posixtimer data struct * @expires: The new expiry time * @absolute: Expiry value is absolute time * @sigev_none: Posix timer does not deliver signals
*/ staticvoid alarm_timer_arm(struct k_itimer *timr, ktime_t expires, bool absolute, bool sigev_none)
{ struct alarm *alarm = &timr->it.alarm.alarmtimer; struct alarm_base *base = &alarm_bases[alarm->type];
if (!absolute)
expires = ktime_add_safe(expires, base->get_ktime()); if (sigev_none)
alarm->node.expires = expires; else
alarm_start(&timr->it.alarm.alarmtimer, expires);
}
/** * alarm_clock_getres - posix getres interface * @which_clock: clockid * @tp: timespec to fill * * Returns the granularity of underlying alarm base clock
*/ staticint alarm_clock_getres(const clockid_t which_clock, struct timespec64 *tp)
{ if (!alarmtimer_get_rtcdev()) return -EINVAL;
type = clock2alarm(new_timer->it_clock);
alarm_init(&new_timer->it.alarm.alarmtimer, type, alarm_handle_timer); return 0;
}
/** * alarmtimer_nsleep_wakeup - Wakeup function for alarm_timer_nsleep * @alarm: ptr to alarm that fired * @now: time at the timer expiration * * Wakes up the task that set the alarmtimer
*/ staticvoid alarmtimer_nsleep_wakeup(struct alarm *alarm, ktime_t now)
{ struct task_struct *task = alarm->data;
alarm->data = NULL; if (task)
wake_up_process(task);
}
/** * alarmtimer_do_nsleep - Internal alarmtimer nsleep implementation * @alarm: ptr to alarmtimer * @absexp: absolute expiration time * @type: alarm type (BOOTTIME/REALTIME). * * Sets the alarm timer and sleeps until it is fired or interrupted.
*/ staticint alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp, enum alarmtimer_type type)
{ struct restart_block *restart;
alarm->data = (void *)current; do {
set_current_state(TASK_INTERRUPTIBLE);
alarm_start(alarm, absexp); if (likely(alarm->data))
schedule();
alarm_cancel(alarm);
} while (alarm->data && !signal_pending(current));
__set_current_state(TASK_RUNNING);
destroy_hrtimer_on_stack(&alarm->timer);
if (!alarm->data) return 0;
if (freezing(current))
alarmtimer_freezerset(absexp, type);
restart = ¤t->restart_block; if (restart->nanosleep.type != TT_NONE) { struct timespec64 rmt;
ktime_t rem;
rem = ktime_sub(absexp, alarm_bases[type].get_ktime());
if (rem <= 0) return 0;
rmt = ktime_to_timespec64(rem);
exp = timespec64_to_ktime(*tsreq); /* Convert (if necessary) to absolute time */ if (flags != TIMER_ABSTIME) {
ktime_t now = alarm_bases[type].get_ktime();
/** * alarmtimer_init - Initialize alarm timer code * * This function initializes the alarm bases and registers * the posix clock ids.
*/ staticint __init alarmtimer_init(void)
{ int error; int i;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.