|
@@ -215,6 +215,21 @@ static int alarmtimer_suspend(struct device *dev)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type)
|
|
|
|
+{
|
|
|
|
+ ktime_t delta;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ struct alarm_base *base = &alarm_bases[type];
|
|
|
|
+
|
|
|
|
+ delta = ktime_sub(absexp, base->gettime());
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&freezer_delta_lock, flags);
|
|
|
|
+ if (!freezer_delta.tv64 || (delta.tv64 < freezer_delta.tv64))
|
|
|
|
+ freezer_delta = delta;
|
|
|
|
+ spin_unlock_irqrestore(&freezer_delta_lock, flags);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
/**************************************************************************
|
|
/**************************************************************************
|
|
* alarm kernel interface code
|
|
* alarm kernel interface code
|
|
*/
|
|
*/
|
|
@@ -279,6 +294,309 @@ void alarm_cancel(struct alarm *alarm)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+/**************************************************************************
|
|
|
|
+ * alarm posix interface code
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * clock2alarm - helper that converts from clockid to alarmtypes
|
|
|
|
+ * @clockid: clockid.
|
|
|
|
+ *
|
|
|
|
+ * Helper function that converts from clockids to alarmtypes
|
|
|
|
+ */
|
|
|
|
+static enum alarmtimer_type clock2alarm(clockid_t clockid)
|
|
|
|
+{
|
|
|
|
+ if (clockid == CLOCK_REALTIME_ALARM)
|
|
|
|
+ return ALARM_REALTIME;
|
|
|
|
+ if (clockid == CLOCK_BOOTTIME_ALARM)
|
|
|
|
+ return ALARM_BOOTTIME;
|
|
|
|
+ return -1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * alarm_handle_timer - Callback for posix timers
|
|
|
|
+ * @alarm: alarm that fired
|
|
|
|
+ *
|
|
|
|
+ * Posix timer callback for expired alarm timers.
|
|
|
|
+ */
|
|
|
|
+static void alarm_handle_timer(struct alarm *alarm)
|
|
|
|
+{
|
|
|
|
+ struct k_itimer *ptr = container_of(alarm, struct k_itimer,
|
|
|
|
+ it.alarmtimer);
|
|
|
|
+ if (posix_timer_event(ptr, 0) != 0)
|
|
|
|
+ ptr->it_overrun++;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * alarm_clock_getres - posix getres interface
|
|
|
|
+ * @which_clock: clockid
|
|
|
|
+ * @tp: timespec to fill
|
|
|
|
+ *
|
|
|
|
+ * Returns the granularity of underlying alarm base clock
|
|
|
|
+ */
|
|
|
|
+static int alarm_clock_getres(const clockid_t which_clock, struct timespec *tp)
|
|
|
|
+{
|
|
|
|
+ clockid_t baseid = alarm_bases[clock2alarm(which_clock)].base_clockid;
|
|
|
|
+
|
|
|
|
+ return hrtimer_get_res(baseid, tp);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * alarm_clock_get - posix clock_get interface
|
|
|
|
+ * @which_clock: clockid
|
|
|
|
+ * @tp: timespec to fill.
|
|
|
|
+ *
|
|
|
|
+ * Provides the underlying alarm base time.
|
|
|
|
+ */
|
|
|
|
+static int alarm_clock_get(clockid_t which_clock, struct timespec *tp)
|
|
|
|
+{
|
|
|
|
+ struct alarm_base *base = &alarm_bases[clock2alarm(which_clock)];
|
|
|
|
+
|
|
|
|
+ *tp = ktime_to_timespec(base->gettime());
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * alarm_timer_create - posix timer_create interface
|
|
|
|
+ * @new_timer: k_itimer pointer to manage
|
|
|
|
+ *
|
|
|
|
+ * Initializes the k_itimer structure.
|
|
|
|
+ */
|
|
|
|
+static int alarm_timer_create(struct k_itimer *new_timer)
|
|
|
|
+{
|
|
|
|
+ enum alarmtimer_type type;
|
|
|
|
+ struct alarm_base *base;
|
|
|
|
+
|
|
|
|
+ if (!capable(CAP_WAKE_ALARM))
|
|
|
|
+ return -EPERM;
|
|
|
|
+
|
|
|
|
+ type = clock2alarm(new_timer->it_clock);
|
|
|
|
+ base = &alarm_bases[type];
|
|
|
|
+ alarm_init(&new_timer->it.alarmtimer, type, alarm_handle_timer);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * alarm_timer_get - posix timer_get interface
|
|
|
|
+ * @new_timer: k_itimer pointer
|
|
|
|
+ * @cur_setting: itimerspec data to fill
|
|
|
|
+ *
|
|
|
|
+ * Copies the itimerspec data out from the k_itimer
|
|
|
|
+ */
|
|
|
|
+static void alarm_timer_get(struct k_itimer *timr,
|
|
|
|
+ struct itimerspec *cur_setting)
|
|
|
|
+{
|
|
|
|
+ cur_setting->it_interval =
|
|
|
|
+ ktime_to_timespec(timr->it.alarmtimer.period);
|
|
|
|
+ cur_setting->it_value =
|
|
|
|
+ ktime_to_timespec(timr->it.alarmtimer.node.expires);
|
|
|
|
+ return;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * alarm_timer_del - posix timer_del interface
|
|
|
|
+ * @timr: k_itimer pointer to be deleted
|
|
|
|
+ *
|
|
|
|
+ * Cancels any programmed alarms for the given timer.
|
|
|
|
+ */
|
|
|
|
+static int alarm_timer_del(struct k_itimer *timr)
|
|
|
|
+{
|
|
|
|
+ alarm_cancel(&timr->it.alarmtimer);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * alarm_timer_set - posix timer_set interface
|
|
|
|
+ * @timr: k_itimer pointer to be deleted
|
|
|
|
+ * @flags: timer flags
|
|
|
|
+ * @new_setting: itimerspec to be used
|
|
|
|
+ * @old_setting: itimerspec being replaced
|
|
|
|
+ *
|
|
|
|
+ * Sets the timer to new_setting, and starts the timer.
|
|
|
|
+ */
|
|
|
|
+static int alarm_timer_set(struct k_itimer *timr, int flags,
|
|
|
|
+ struct itimerspec *new_setting,
|
|
|
|
+ struct itimerspec *old_setting)
|
|
|
|
+{
|
|
|
|
+ /* Save old values */
|
|
|
|
+ old_setting->it_interval =
|
|
|
|
+ ktime_to_timespec(timr->it.alarmtimer.period);
|
|
|
|
+ old_setting->it_value =
|
|
|
|
+ ktime_to_timespec(timr->it.alarmtimer.node.expires);
|
|
|
|
+
|
|
|
|
+ /* If the timer was already set, cancel it */
|
|
|
|
+ alarm_cancel(&timr->it.alarmtimer);
|
|
|
|
+
|
|
|
|
+ /* start the timer */
|
|
|
|
+ alarm_start(&timr->it.alarmtimer,
|
|
|
|
+ timespec_to_ktime(new_setting->it_value),
|
|
|
|
+ timespec_to_ktime(new_setting->it_interval));
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * alarmtimer_nsleep_wakeup - Wakeup function for alarm_timer_nsleep
|
|
|
|
+ * @alarm: ptr to alarm that fired
|
|
|
|
+ *
|
|
|
|
+ * Wakes up the task that set the alarmtimer
|
|
|
|
+ */
|
|
|
|
+static void alarmtimer_nsleep_wakeup(struct alarm *alarm)
|
|
|
|
+{
|
|
|
|
+ struct task_struct *task = (struct task_struct *)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
|
|
|
|
+ *
|
|
|
|
+ * Sets the alarm timer and sleeps until it is fired or interrupted.
|
|
|
|
+ */
|
|
|
|
+static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp)
|
|
|
|
+{
|
|
|
|
+ alarm->data = (void *)current;
|
|
|
|
+ do {
|
|
|
|
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
|
|
+ alarm_start(alarm, absexp, ktime_set(0, 0));
|
|
|
|
+ if (likely(alarm->data))
|
|
|
|
+ schedule();
|
|
|
|
+
|
|
|
|
+ alarm_cancel(alarm);
|
|
|
|
+ } while (alarm->data && !signal_pending(current));
|
|
|
|
+
|
|
|
|
+ __set_current_state(TASK_RUNNING);
|
|
|
|
+
|
|
|
|
+ return (alarm->data == NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * update_rmtp - Update remaining timespec value
|
|
|
|
+ * @exp: expiration time
|
|
|
|
+ * @type: timer type
|
|
|
|
+ * @rmtp: user pointer to remaining timepsec value
|
|
|
|
+ *
|
|
|
|
+ * Helper function that fills in rmtp value with time between
|
|
|
|
+ * now and the exp value
|
|
|
|
+ */
|
|
|
|
+static int update_rmtp(ktime_t exp, enum alarmtimer_type type,
|
|
|
|
+ struct timespec __user *rmtp)
|
|
|
|
+{
|
|
|
|
+ struct timespec rmt;
|
|
|
|
+ ktime_t rem;
|
|
|
|
+
|
|
|
|
+ rem = ktime_sub(exp, alarm_bases[type].gettime());
|
|
|
|
+
|
|
|
|
+ if (rem.tv64 <= 0)
|
|
|
|
+ return 0;
|
|
|
|
+ rmt = ktime_to_timespec(rem);
|
|
|
|
+
|
|
|
|
+ if (copy_to_user(rmtp, &rmt, sizeof(*rmtp)))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * alarm_timer_nsleep_restart - restartblock alarmtimer nsleep
|
|
|
|
+ * @restart: ptr to restart block
|
|
|
|
+ *
|
|
|
|
+ * Handles restarted clock_nanosleep calls
|
|
|
|
+ */
|
|
|
|
+static long __sched alarm_timer_nsleep_restart(struct restart_block *restart)
|
|
|
|
+{
|
|
|
|
+ enum alarmtimer_type type = restart->nanosleep.index;
|
|
|
|
+ ktime_t exp;
|
|
|
|
+ struct timespec __user *rmtp;
|
|
|
|
+ struct alarm alarm;
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ exp.tv64 = restart->nanosleep.expires;
|
|
|
|
+ alarm_init(&alarm, type, alarmtimer_nsleep_wakeup);
|
|
|
|
+
|
|
|
|
+ if (alarmtimer_do_nsleep(&alarm, exp))
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ if (freezing(current))
|
|
|
|
+ alarmtimer_freezerset(exp, type);
|
|
|
|
+
|
|
|
|
+ rmtp = restart->nanosleep.rmtp;
|
|
|
|
+ if (rmtp) {
|
|
|
|
+ ret = update_rmtp(exp, type, rmtp);
|
|
|
|
+ if (ret <= 0)
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /* The other values in restart are already filled in */
|
|
|
|
+ ret = -ERESTART_RESTARTBLOCK;
|
|
|
|
+out:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * alarm_timer_nsleep - alarmtimer nanosleep
|
|
|
|
+ * @which_clock: clockid
|
|
|
|
+ * @flags: determins abstime or relative
|
|
|
|
+ * @tsreq: requested sleep time (abs or rel)
|
|
|
|
+ * @rmtp: remaining sleep time saved
|
|
|
|
+ *
|
|
|
|
+ * Handles clock_nanosleep calls against _ALARM clockids
|
|
|
|
+ */
|
|
|
|
+static int alarm_timer_nsleep(const clockid_t which_clock, int flags,
|
|
|
|
+ struct timespec *tsreq, struct timespec __user *rmtp)
|
|
|
|
+{
|
|
|
|
+ enum alarmtimer_type type = clock2alarm(which_clock);
|
|
|
|
+ struct alarm alarm;
|
|
|
|
+ ktime_t exp;
|
|
|
|
+ int ret = 0;
|
|
|
|
+ struct restart_block *restart;
|
|
|
|
+
|
|
|
|
+ if (!capable(CAP_WAKE_ALARM))
|
|
|
|
+ return -EPERM;
|
|
|
|
+
|
|
|
|
+ alarm_init(&alarm, type, alarmtimer_nsleep_wakeup);
|
|
|
|
+
|
|
|
|
+ exp = timespec_to_ktime(*tsreq);
|
|
|
|
+ /* Convert (if necessary) to absolute time */
|
|
|
|
+ if (flags != TIMER_ABSTIME) {
|
|
|
|
+ ktime_t now = alarm_bases[type].gettime();
|
|
|
|
+ exp = ktime_add(now, exp);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (alarmtimer_do_nsleep(&alarm, exp))
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ if (freezing(current))
|
|
|
|
+ alarmtimer_freezerset(exp, type);
|
|
|
|
+
|
|
|
|
+ /* abs timers don't set remaining time or restart */
|
|
|
|
+ if (flags == TIMER_ABSTIME) {
|
|
|
|
+ ret = -ERESTARTNOHAND;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (rmtp) {
|
|
|
|
+ ret = update_rmtp(exp, type, rmtp);
|
|
|
|
+ if (ret <= 0)
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ restart = ¤t_thread_info()->restart_block;
|
|
|
|
+ restart->fn = alarm_timer_nsleep_restart;
|
|
|
|
+ restart->nanosleep.index = type;
|
|
|
|
+ restart->nanosleep.expires = exp.tv64;
|
|
|
|
+ restart->nanosleep.rmtp = rmtp;
|
|
|
|
+ ret = -ERESTART_RESTARTBLOCK;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
|
|
/**************************************************************************
|
|
/**************************************************************************
|
|
* alarmtimer initialization code
|
|
* alarmtimer initialization code
|
|
@@ -306,6 +624,18 @@ static int __init alarmtimer_init(void)
|
|
{
|
|
{
|
|
int error = 0;
|
|
int error = 0;
|
|
int i;
|
|
int i;
|
|
|
|
+ struct k_clock alarm_clock = {
|
|
|
|
+ .clock_getres = alarm_clock_getres,
|
|
|
|
+ .clock_get = alarm_clock_get,
|
|
|
|
+ .timer_create = alarm_timer_create,
|
|
|
|
+ .timer_set = alarm_timer_set,
|
|
|
|
+ .timer_del = alarm_timer_del,
|
|
|
|
+ .timer_get = alarm_timer_get,
|
|
|
|
+ .nsleep = alarm_timer_nsleep,
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ posix_timers_register_clock(CLOCK_REALTIME_ALARM, &alarm_clock);
|
|
|
|
+ posix_timers_register_clock(CLOCK_BOOTTIME_ALARM, &alarm_clock);
|
|
|
|
|
|
/* Initialize alarm bases */
|
|
/* Initialize alarm bases */
|
|
alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME;
|
|
alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME;
|