Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 245634
b: refs/heads/master
c: 9a7adcf
h: refs/heads/master
v: v3
  • Loading branch information
John Stultz committed Apr 26, 2011
1 parent 45d8f47 commit d2fc2cd
Show file tree
Hide file tree
Showing 5 changed files with 341 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: ff3ead96d17f47ee70c294a5cc2cce9b61e82f0f
refs/heads/master: 9a7adcf5c6dea63d2e47e6f6d2f7a6c9f48b9337
7 changes: 6 additions & 1 deletion trunk/include/linux/capability.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,12 @@ struct cpu_vfs_cap_data {

#define CAP_SYSLOG 34

#define CAP_LAST_CAP CAP_SYSLOG
/* Allow triggering something that will wake the system */

#define CAP_WAKE_ALARM 35


#define CAP_LAST_CAP CAP_WAKE_ALARM

#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)

Expand Down
2 changes: 2 additions & 0 deletions trunk/include/linux/posix-timers.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/timex.h>
#include <linux/alarmtimer.h>

union cpu_time_count {
cputime_t cpu;
Expand Down Expand Up @@ -80,6 +81,7 @@ struct k_itimer {
unsigned long incr;
unsigned long expires;
} mmtimer;
struct alarm alarmtimer;
} it;
};

Expand Down
2 changes: 2 additions & 0 deletions trunk/include/linux/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ struct itimerval {
#define CLOCK_REALTIME_COARSE 5
#define CLOCK_MONOTONIC_COARSE 6
#define CLOCK_BOOTTIME 7
#define CLOCK_REALTIME_ALARM 8
#define CLOCK_BOOTTIME_ALARM 9

/*
* The IDs of various hardware clocks:
Expand Down
330 changes: 330 additions & 0 deletions trunk/kernel/time/alarmtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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 = &current_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
Expand Down Expand Up @@ -306,6 +624,18 @@ static int __init alarmtimer_init(void)
{
int error = 0;
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 */
alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME;
Expand Down

0 comments on commit d2fc2cd

Please sign in to comment.