[PATCH] hrtimers, timers: eliminate some jiffies-backed code

From: Dmitry Antipov
Date: Mon Jan 23 2012 - 10:40:27 EST


This patch provides an attempt to get away from jiffies in msleep()
and msleep_interruptible() to hrtimers-backed usleep_range() and
usleep_range_interruptible(). Both of the latter now returns an amount
of microseconds really spent in sleep; another rationale for this
was to convert msleep()-based wait-for-hardware loops to usleep_range(),
like this:

unsigned long timeout = jiffies + msecs_to_jiffies(1000);
while (hw_is_not_ready()) {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
msleep(1);
}

to:

unsigned long timeout = 0;
while (hw_is_not_ready()) {
if (timeout > USEC_PER_SEC)
return -ETIMEDOUT;
timeout += usleep_range(500, 1500);
}

Signed-off-by: Dmitry Antipov <dmitry.antipov@xxxxxxxxxx>
---
fs/eventpoll.c | 3 ++-
fs/select.c | 3 ++-
include/linux/delay.h | 3 ++-
include/linux/hrtimer.h | 8 +++++---
ipc/mqueue.c | 4 ++--
kernel/hrtimer.c | 38 +++++++++++++++++++++++++++++++-------
kernel/timer.c | 39 +++++++++++++++++++++++++--------------
7 files changed, 69 insertions(+), 29 deletions(-)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index aabdfc3..a374944 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1349,7 +1349,8 @@ fetch_events:
}

spin_unlock_irqrestore(&ep->lock, flags);
- if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
+ if (!schedule_hrtimeout_range(to, NULL, slack,
+ HRTIMER_MODE_ABS))
timed_out = 1;

spin_lock_irqsave(&ep->lock, flags);
diff --git a/fs/select.c b/fs/select.c
index d33418f..b4354b2 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -236,7 +236,8 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state,

set_current_state(state);
if (!pwq->triggered)
- rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS);
+ rc = schedule_hrtimeout_range(expires, NULL, slack,
+ HRTIMER_MODE_ABS);
__set_current_state(TASK_RUNNING);

/*
diff --git a/include/linux/delay.h b/include/linux/delay.h
index a6ecb34..bad6d49 100644
--- a/include/linux/delay.h
+++ b/include/linux/delay.h
@@ -45,7 +45,8 @@ extern unsigned long lpj_fine;
void calibrate_delay(void);
void msleep(unsigned int msecs);
unsigned long msleep_interruptible(unsigned int msecs);
-void usleep_range(unsigned long min, unsigned long max);
+unsigned long usleep_range(unsigned long min, unsigned long max);
+unsigned long usleep_range_interruptible(unsigned long min, unsigned long max);

static inline void ssleep(unsigned int seconds)
{
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index fd0dc30..01d782b 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -126,6 +126,7 @@ struct hrtimer {
* task is set to NULL, when the timer expires.
*/
struct hrtimer_sleeper {
+ ktime_t kt;
struct hrtimer timer;
struct task_struct *task;
};
@@ -428,9 +429,10 @@ extern long hrtimer_nanosleep_restart(struct restart_block *restart_block);
extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
struct task_struct *tsk);

-extern int schedule_hrtimeout_range(ktime_t *expires, unsigned long delta,
- const enum hrtimer_mode mode);
-extern int schedule_hrtimeout_range_clock(ktime_t *expires,
+extern int schedule_hrtimeout_range(ktime_t *expires, ktime_t *elapsed,
+ unsigned long delta,
+ const enum hrtimer_mode mode);
+extern int schedule_hrtimeout_range_clock(ktime_t *expires, ktime_t *elapsed,
unsigned long delta, const enum hrtimer_mode mode, int clock);
extern int schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode);

diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 9b7c8ab..e57d7c1 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -449,8 +449,8 @@ static int wq_sleep(struct mqueue_inode_info *info, int sr,
set_current_state(TASK_INTERRUPTIBLE);

spin_unlock(&info->lock);
- time = schedule_hrtimeout_range_clock(timeout, 0,
- HRTIMER_MODE_ABS, CLOCK_REALTIME);
+ time = schedule_hrtimeout_range_clock(timeout, NULL,
+ 0, HRTIMER_MODE_ABS, CLOCK_REALTIME);

while (ewp->state == STATE_PENDING)
cpu_relax();
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index ae34bf5..8642c3f 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -1475,6 +1475,12 @@ static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)
struct hrtimer_sleeper *t =
container_of(timer, struct hrtimer_sleeper, timer);
struct task_struct *task = t->task;
+ struct hrtimer_clock_base *base;
+ unsigned long flags;
+
+ base = lock_hrtimer_base(timer, &flags);
+ t->kt = ktime_sub(base->get_time(), t->kt);
+ unlock_hrtimer_base(timer, &flags);

t->task = NULL;
if (task)
@@ -1485,6 +1491,13 @@ static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)

void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
{
+ struct hrtimer_clock_base *base;
+ unsigned long flags;
+
+ base = lock_hrtimer_base(&sl->timer, &flags);
+ sl->kt = base->get_time();
+ unlock_hrtimer_base(&sl->timer, &flags);
+
sl->timer.function = hrtimer_wakeup;
sl->task = task;
}
@@ -1747,12 +1760,14 @@ void __init hrtimers_init(void)
/**
* schedule_hrtimeout_range_clock - sleep until timeout
* @expires: timeout value (ktime_t)
+ * @elapsed: pointer to ktime_t used to save time spent in sleep
* @delta: slack in expires timeout (ktime_t)
* @mode: timer mode, HRTIMER_MODE_ABS or HRTIMER_MODE_REL
* @clock: timer clock, CLOCK_MONOTONIC or CLOCK_REALTIME
*/
int __sched
-schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
+schedule_hrtimeout_range_clock(ktime_t *expires, ktime_t *elapsed,
+ unsigned long delta,
const enum hrtimer_mode mode, int clock)
{
struct hrtimer_sleeper t;
@@ -1763,6 +1778,8 @@ schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
*/
if (expires && !expires->tv64) {
__set_current_state(TASK_RUNNING);
+ if (elapsed)
+ *elapsed = ktime_set(0, 0);
return 0;
}

@@ -1772,6 +1789,8 @@ schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
if (!expires) {
schedule();
__set_current_state(TASK_RUNNING);
+ if (elapsed)
+ *elapsed = ktime_set(KTIME_SEC_MAX, 0);
return -EINTR;
}

@@ -1787,6 +1806,9 @@ schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
if (likely(t.task))
schedule();

+ if (elapsed)
+ *elapsed = t.kt;
+
hrtimer_cancel(&t.timer);
destroy_hrtimer_on_stack(&t.timer);

@@ -1798,6 +1820,7 @@ schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
/**
* schedule_hrtimeout_range - sleep until timeout
* @expires: timeout value (ktime_t)
+ * @elapsed: pointer to ktime_t used to save time spent in sleep
* @delta: slack in expires timeout (ktime_t)
* @mode: timer mode, HRTIMER_MODE_ABS or HRTIMER_MODE_REL
*
@@ -1823,17 +1846,19 @@ schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
*
* Returns 0 when the timer has expired otherwise -EINTR
*/
-int __sched schedule_hrtimeout_range(ktime_t *expires, unsigned long delta,
+int __sched schedule_hrtimeout_range(ktime_t *expires, ktime_t *elapsed,
+ unsigned long delta,
const enum hrtimer_mode mode)
{
- return schedule_hrtimeout_range_clock(expires, delta, mode,
- CLOCK_MONOTONIC);
+ return schedule_hrtimeout_range_clock(expires, elapsed, delta,
+ mode, CLOCK_MONOTONIC);
}
EXPORT_SYMBOL_GPL(schedule_hrtimeout_range);

/**
* schedule_hrtimeout - sleep until timeout
* @expires: timeout value (ktime_t)
+ * @elapsed: pointer to ktime_t used to save time spent in sleep
* @mode: timer mode, HRTIMER_MODE_ABS or HRTIMER_MODE_REL
*
* Make the current task sleep until the given expiry time has
@@ -1853,9 +1878,8 @@ EXPORT_SYMBOL_GPL(schedule_hrtimeout_range);
*
* Returns 0 when the timer has expired otherwise -EINTR
*/
-int __sched schedule_hrtimeout(ktime_t *expires,
- const enum hrtimer_mode mode)
+int __sched schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode)
{
- return schedule_hrtimeout_range(expires, 0, mode);
+ return schedule_hrtimeout_range(expires, NULL, 0, mode);
}
EXPORT_SYMBOL_GPL(schedule_hrtimeout);
diff --git a/kernel/timer.c b/kernel/timer.c
index a297ffc..00acab6 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1796,12 +1796,12 @@ void __init init_timers(void)
*/
void msleep(unsigned int msecs)
{
- unsigned long timeout = msecs_to_jiffies(msecs) + 1;
+ unsigned long usecs = msecs * USEC_PER_MSEC;
+ unsigned long slack = rt_task(current) ? 0 :
+ current->timer_slack_ns / NSEC_PER_USEC;

- while (timeout)
- timeout = schedule_timeout_uninterruptible(timeout);
+ usleep_range(usecs, usecs + slack);
}
-
EXPORT_SYMBOL(msleep);

/**
@@ -1810,23 +1810,27 @@ EXPORT_SYMBOL(msleep);
*/
unsigned long msleep_interruptible(unsigned int msecs)
{
- unsigned long timeout = msecs_to_jiffies(msecs) + 1;
+ unsigned long timeout, usecs, slack;

- while (timeout && !signal_pending(current))
- timeout = schedule_timeout_interruptible(timeout);
- return jiffies_to_msecs(timeout);
-}
+ timeout = 0;
+ usecs = msecs * USEC_PER_MSEC;
+ slack = rt_task(current) ? 0 : current->timer_slack_ns / NSEC_PER_USEC;

+ while (timeout < usecs && !signal_pending(current))
+ timeout += usleep_range_interruptible(usecs, usecs + slack);
+ return timeout / USEC_PER_MSEC;
+}
EXPORT_SYMBOL(msleep_interruptible);

-static int __sched do_usleep_range(unsigned long min, unsigned long max)
+static inline unsigned long do_usleep_range(unsigned long min, unsigned long max)
{
- ktime_t kmin;
+ ktime_t kmin, elapsed;
unsigned long delta;

kmin = ktime_set(0, min * NSEC_PER_USEC);
delta = (max - min) * NSEC_PER_USEC;
- return schedule_hrtimeout_range(&kmin, delta, HRTIMER_MODE_REL);
+ return schedule_hrtimeout_range(&kmin, &elapsed, delta, HRTIMER_MODE_REL)
+ ? 0 : ktime_to_us(elapsed);
}

/**
@@ -1834,9 +1838,16 @@ static int __sched do_usleep_range(unsigned long min, unsigned long max)
* @min: Minimum time in usecs to sleep
* @max: Maximum time in usecs to sleep
*/
-void usleep_range(unsigned long min, unsigned long max)
+unsigned long usleep_range(unsigned long min, unsigned long max)
{
__set_current_state(TASK_UNINTERRUPTIBLE);
- do_usleep_range(min, max);
+ return do_usleep_range(min, max);
}
EXPORT_SYMBOL(usleep_range);
+
+unsigned long usleep_range_interruptible(unsigned long min, unsigned long max)
+{
+ __set_current_state(TASK_INTERRUPTIBLE);
+ return do_usleep_range(min, max);
+}
+EXPORT_SYMBOL(usleep_range_interruptible);
--
1.7.7.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/