[tip:timers/core] timers: Fix usleep_range() in the context of wake_up_process()
From: tip-bot for Douglas Anderson
Date: Wed Oct 26 2016 - 07:22:49 EST
Commit-ID: 6c5e9059692567740a4ee51530dffe51a4b9584d
Gitweb: http://git.kernel.org/tip/6c5e9059692567740a4ee51530dffe51a4b9584d
Author: Douglas Anderson <dianders@xxxxxxxxxxxx>
AuthorDate: Fri, 21 Oct 2016 08:58:50 -0700
Committer: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
CommitDate: Wed, 26 Oct 2016 13:14:46 +0200
timers: Fix usleep_range() in the context of wake_up_process()
Users of usleep_range() expect that it will _never_ return in less time
than the minimum passed parameter. However, nothing in the code ensures
this, when the sleeping task is woken by wake_up_process() or any other
mechanism which can wake a task from uninterruptible state.
Neither usleep_range() nor schedule_hrtimeout_range*() have any protection
against wakeups. schedule_hrtimeout_range*() is designed this way despite
the fact that the API documentation does not mention it.
msleep() already has code to handle this case since it will loop as long
as there was still time left. usleep_range() has no such loop, add it.
Presumably this problem was not detected before because usleep_range() is
only used in a few places and the function is mostly used in contexts which
are not exposed to wakeups of any form.
An effort was made to look for users relying on the old behavior by
looking for usleep_range() in the same file as wake_up_process().
No problems were found by this search, though it is conceivable that
someone could have put the sleep and wakeup in two different files.
An effort was made to ask several upstream maintainers if they were aware
of people relying on wake_up_process() to wake up usleep_range(). No
maintainers were aware of that but they were aware of many people relying
on usleep_range() never returning before the minimum.
Reported-by: Tao Huang <huangtao@xxxxxxxxxxxxxx>
Signed-off-by: Douglas Anderson <dianders@xxxxxxxxxxxx>
Cc: heiko@xxxxxxxxx
Cc: broonie@xxxxxxxxxx
Cc: briannorris@xxxxxxxxxxxx
Cc: Andreas Mohr <andi@xxxxxxxx>
Cc: linux-rockchip@xxxxxxxxxxxxxxxxxxx
Cc: tony.xie@xxxxxxxxxxxxxx
Cc: John Stultz <john.stultz@xxxxxxxxxx>
Cc: djkurtz@xxxxxxxxxxxx
Cc: linux@xxxxxxxxxxxx
Cc: tskd08@xxxxxxxxx
Link: http://lkml.kernel.org/r/1477065531-30342-1-git-send-email-dianders@xxxxxxxxxxxx
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
---
kernel/time/timer.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 2d47980..12681c9 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1896,16 +1896,6 @@ unsigned long msleep_interruptible(unsigned int msecs)
EXPORT_SYMBOL(msleep_interruptible);
-static void __sched do_usleep_range(unsigned long min, unsigned long max)
-{
- ktime_t kmin;
- u64 delta;
-
- kmin = ktime_set(0, min * NSEC_PER_USEC);
- delta = (u64)(max - min) * NSEC_PER_USEC;
- schedule_hrtimeout_range(&kmin, delta, HRTIMER_MODE_REL);
-}
-
/**
* usleep_range - Sleep for an approximate time
* @min: Minimum time in usecs to sleep
@@ -1919,7 +1909,14 @@ static void __sched do_usleep_range(unsigned long min, unsigned long max)
*/
void __sched usleep_range(unsigned long min, unsigned long max)
{
- __set_current_state(TASK_UNINTERRUPTIBLE);
- do_usleep_range(min, max);
+ ktime_t exp = ktime_add_us(ktime_get(), min);
+ u64 delta = (u64)(max - min) * NSEC_PER_USEC;
+
+ for (;;) {
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ /* Do not return before the requested sleep time has elapsed */
+ if (!schedule_hrtimeout_range(&exp, delta, HRTIMER_MODE_ABS))
+ break;
+ }
}
EXPORT_SYMBOL(usleep_range);