diff mbox

hrtimers: teach usleep_range() to return how many usecs was slept

Message ID 1326718403-28156-1-git-send-email-dmitry.antipov@linaro.org
State New
Headers show

Commit Message

Dmitry Antipov Jan. 16, 2012, 12:53 p.m. UTC
Teach usleep_range() to return how many usecs was actually spent
in sleep. The rationale beyond this is to convert jiffies-based
wait-for-hardware loops like:

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(1000, 2000);
}

Signed-off-by: Dmitry Antipov <dmitry.antipov@linaro.org>
---
 fs/eventpoll.c          |    2 +-
 fs/select.c             |    2 +-
 include/linux/delay.h   |    2 +-
 include/linux/hrtimer.h |    9 ++++++---
 ipc/mqueue.c            |    2 +-
 kernel/hrtimer.c        |   28 ++++++++++++++++++++++++----
 kernel/timer.c          |   12 ++++++++----
 7 files changed, 42 insertions(+), 15 deletions(-)

Comments

Andrew Morton Jan. 26, 2012, 9:05 a.m. UTC | #1
On Mon, 16 Jan 2012 16:53:23 +0400 Dmitry Antipov <dmitry.antipov@linaro.org> wrote:

> Teach usleep_range() to return how many usecs was actually spent
> in sleep. The rationale beyond this is to convert jiffies-based
> wait-for-hardware loops like:
> 
> 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(1000, 2000);
> }
> 

Is that useful enough to justify making the change?

>
>  int __sched schedule_hrtimeout_range(ktime_t *expires, unsigned long delta,
> -				     const enum hrtimer_mode mode)
> +				     const enum hrtimer_mode mode, unsigned long *elapsed)

Rather than adding another argument, I suggest you change the return
type to long and use return value semantics similar to schedule_timeout().

schedule_timeout() never returns -ve numbers and it returns jiffies,
but it will be close(r).

Returning usecs is odd.  One would expect it to return a ktime_t.  That
might inflict some code-size cost in callers.
diff mbox

Patch

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index aabdfc3..7a957fd 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1349,7 +1349,7 @@  fetch_events:
 			}
 
 			spin_unlock_irqrestore(&ep->lock, flags);
-			if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
+			if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS, NULL))
 				timed_out = 1;
 
 			spin_lock_irqsave(&ep->lock, flags);
diff --git a/fs/select.c b/fs/select.c
index d33418f..eceb573 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -236,7 +236,7 @@  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, slack, HRTIMER_MODE_ABS, NULL);
 	__set_current_state(TASK_RUNNING);
 
 	/*
diff --git a/include/linux/delay.h b/include/linux/delay.h
index a6ecb34..e512a50 100644
--- a/include/linux/delay.h
+++ b/include/linux/delay.h
@@ -45,7 +45,7 @@  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);
 
 static inline void ssleep(unsigned int seconds)
 {
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index fd0dc30..661dd25 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -128,6 +128,7 @@  struct hrtimer {
 struct hrtimer_sleeper {
 	struct hrtimer timer;
 	struct task_struct *task;
+	ktime_t elapsed;
 };
 
 /**
@@ -429,9 +430,11 @@  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,
-		unsigned long delta, const enum hrtimer_mode mode, int clock);
+				    const enum hrtimer_mode mode,
+				    unsigned long *elapsed);
+extern int schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
+					  const enum hrtimer_mode mode, int clock,
+					  unsigned long *elapsed);
 extern int schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode);
 
 /* Soft interrupt function to run the hrtimer queues: */
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 9b7c8ab..578cce7 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -450,7 +450,7 @@  static int wq_sleep(struct mqueue_inode_info *info, int sr,
 
 		spin_unlock(&info->lock);
 		time = schedule_hrtimeout_range_clock(timeout, 0,
-			HRTIMER_MODE_ABS, CLOCK_REALTIME);
+		        HRTIMER_MODE_ABS, CLOCK_REALTIME, NULL);
 
 		while (ewp->state == STATE_PENDING)
 			cpu_relax();
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index ae34bf5..75c9c5c 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->elapsed = ktime_sub(base->get_time(), t->elapsed);
+	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->elapsed = base->get_time();
+	unlock_hrtimer_base(&sl->timer, &flags);
+
 	sl->timer.function = hrtimer_wakeup;
 	sl->task = task;
 }
@@ -1750,10 +1763,13 @@  void __init hrtimers_init(void)
  * @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
+ * @elapsed:	pointer to unsigned long variable where to store
+ * 		the time actually slept in timeout (usecs)
  */
 int __sched
 schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
-			       const enum hrtimer_mode mode, int clock)
+			       const enum hrtimer_mode mode, int clock,
+			       unsigned long *elapsed)
 {
 	struct hrtimer_sleeper t;
 
@@ -1787,6 +1803,8 @@  schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
 	if (likely(t.task))
 		schedule();
 
+	if (elapsed)
+		*elapsed = ktime_to_us(t.elapsed);
 	hrtimer_cancel(&t.timer);
 	destroy_hrtimer_on_stack(&t.timer);
 
@@ -1800,6 +1818,8 @@  schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
  * @expires:	timeout value (ktime_t)
  * @delta:	slack in expires timeout (ktime_t)
  * @mode:	timer mode, HRTIMER_MODE_ABS or HRTIMER_MODE_REL
+ * @elapsed:	pointer to unsigned long variable where to store
+ *		the time actually slept in timeout (usecs)
  *
  * Make the current task sleep until the given expiry time has
  * elapsed. The routine will return immediately unless
@@ -1824,10 +1844,10 @@  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,
-				     const enum hrtimer_mode mode)
+				     const enum hrtimer_mode mode, unsigned long *elapsed)
 {
 	return schedule_hrtimeout_range_clock(expires, delta, mode,
-					      CLOCK_MONOTONIC);
+					      CLOCK_MONOTONIC, elapsed);
 }
 EXPORT_SYMBOL_GPL(schedule_hrtimeout_range);
 
@@ -1856,6 +1876,6 @@  EXPORT_SYMBOL_GPL(schedule_hrtimeout_range);
 int __sched schedule_hrtimeout(ktime_t *expires,
 			       const enum hrtimer_mode mode)
 {
-	return schedule_hrtimeout_range(expires, 0, mode);
+	return schedule_hrtimeout_range(expires, 0, mode, NULL);
 }
 EXPORT_SYMBOL_GPL(schedule_hrtimeout);
diff --git a/kernel/timer.c b/kernel/timer.c
index a297ffc..aaad2b8 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1819,14 +1819,15 @@  unsigned long msleep_interruptible(unsigned int msecs)
 
 EXPORT_SYMBOL(msleep_interruptible);
 
-static int __sched do_usleep_range(unsigned long min, unsigned long max)
+static int __sched do_usleep_range(unsigned long min, unsigned long max,
+				   unsigned long *elapsed)
 {
 	ktime_t kmin;
 	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, delta, HRTIMER_MODE_REL, elapsed);
 }
 
 /**
@@ -1834,9 +1835,12 @@  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)
 {
+	unsigned long elapsed;
+
 	__set_current_state(TASK_UNINTERRUPTIBLE);
-	do_usleep_range(min, max);
+	do_usleep_range(min, max, &elapsed);
+	return elapsed;
 }
 EXPORT_SYMBOL(usleep_range);