The timer_slack_ns value in the task struct is currently a
unsigned long. This means that on 32bit applications, the
maximum slack is just over 4 seconds. However, on 64bit
machines, its much much larger (~500 years).

This disparity could make application development a little
difficult, so this patch extends the timer_slack_ns entry
(as well as the default_slack) to a u64. This means both 32bit
and 64bit systems have the same effective internal slack range.

Now the existing ABI via PR_GET_TIMERSLACK and PR_SET_TIMERSLACK
specify the interface as a unsigned long, so we preserve that
limitation on 32bit systems, where SET_TIMERSLACK can only set
the slack to a unsigned long value, and GET_TIMERSLACK will
return ULONG_MAX if the slack is actually larger then what can
be stored by an unsigned long.

This patch also modifies hrtimer functions which specified the
slack delta as a unsigned long.

Cc: Arjan van de Ven <ar...@linux.intel.com>
Cc: Thomas Gleixner <t...@linutronix.de>
Cc: Oren Laadan <or...@cellrox.com>
Cc: Ruchi Kandoi <kandoiru...@google.com>
Cc: Rom Lemarchand <rom...@android.com>
Cc: Kees Cook <keesc...@chromium.org>
Cc: Andrew Morton <a...@linux-foundation.org>
Cc: Android Kernel Team <kernel-t...@android.com>
Signed-off-by: John Stultz <john.stu...@linaro.org>
---
 fs/eventpoll.c          |  2 +-
 fs/select.c             |  8 ++++----
 include/linux/freezer.h |  2 +-
 include/linux/hrtimer.h | 12 +++++++-----
 include/linux/poll.h    |  2 +-
 include/linux/sched.h   |  4 ++--
 kernel/sys.c            |  5 ++++-
 kernel/time/hrtimer.c   |  8 ++++----
 kernel/time/timer.c     |  4 ++--
 9 files changed, 26 insertions(+), 21 deletions(-)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index cde6074..8a74a2a 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1616,7 +1616,7 @@ static int ep_poll(struct eventpoll *ep, struct 
epoll_event __user *events,
 {
        int res = 0, eavail, timed_out = 0;
        unsigned long flags;
-       long slack = 0;
+       u64 slack = 0;
        wait_queue_t wait;
        ktime_t expires, *to = NULL;
 
diff --git a/fs/select.c b/fs/select.c
index 79d0d49..8692939 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -70,9 +70,9 @@ static long __estimate_accuracy(struct timespec *tv)
        return slack;
 }
 
-long select_estimate_accuracy(struct timespec *tv)
+u64 select_estimate_accuracy(struct timespec *tv)
 {
-       unsigned long ret;
+       u64 ret;
        struct timespec now;
 
        /*
@@ -402,7 +402,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec 
*end_time)
        struct poll_wqueues table;
        poll_table *wait;
        int retval, i, timed_out = 0;
-       unsigned long slack = 0;
+       u64 slack = 0;
        unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;
        unsigned long busy_end = 0;
 
@@ -784,7 +784,7 @@ static int do_poll(struct poll_list *list, struct 
poll_wqueues *wait,
        poll_table* pt = &wait->pt;
        ktime_t expire, *to = NULL;
        int timed_out = 0, count = 0;
-       unsigned long slack = 0;
+       u64 slack = 0;
        unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;
        unsigned long busy_end = 0;
 
diff --git a/include/linux/freezer.h b/include/linux/freezer.h
index 6b7fd9c..dd03e83 100644
--- a/include/linux/freezer.h
+++ b/include/linux/freezer.h
@@ -231,7 +231,7 @@ static inline long 
freezable_schedule_timeout_killable_unsafe(long timeout)
  * call this with locks held.
  */
 static inline int freezable_schedule_hrtimeout_range(ktime_t *expires,
-               unsigned long delta, const enum hrtimer_mode mode)
+               u64 delta, const enum hrtimer_mode mode)
 {
        int __retval;
        freezer_do_not_count();
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 2ead22d..c98c653 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -220,7 +220,7 @@ static inline void hrtimer_set_expires_range(struct hrtimer 
*timer, ktime_t time
        timer->node.expires = ktime_add_safe(time, delta);
 }
 
-static inline void hrtimer_set_expires_range_ns(struct hrtimer *timer, ktime_t 
time, unsigned long delta)
+static inline void hrtimer_set_expires_range_ns(struct hrtimer *timer, ktime_t 
time, u64 delta)
 {
        timer->_softexpires = time;
        timer->node.expires = ktime_add_safe(time, ns_to_ktime(delta));
@@ -378,7 +378,7 @@ static inline void destroy_hrtimer_on_stack(struct hrtimer 
*timer) { }
 
 /* Basic timer operations: */
 extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
-                       unsigned long range_ns, const enum hrtimer_mode mode);
+                                  u64 range_ns, const enum hrtimer_mode mode);
 
 /**
  * hrtimer_start - (re)start an hrtimer on the current CPU
@@ -399,7 +399,7 @@ extern int hrtimer_try_to_cancel(struct hrtimer *timer);
 static inline void hrtimer_start_expires(struct hrtimer *timer,
                                         enum hrtimer_mode mode)
 {
-       unsigned long delta;
+       u64 delta;
        ktime_t soft, hard;
        soft = hrtimer_get_softexpires(timer);
        hard = hrtimer_get_expires(timer);
@@ -477,10 +477,12 @@ 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,
+extern int schedule_hrtimeout_range(ktime_t *expires, u64 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);
+                                         u64 delta,
+                                         const enum hrtimer_mode mode,
+                                         int clock);
 extern int schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode);
 
 /* Soft interrupt function to run the hrtimer queues: */
diff --git a/include/linux/poll.h b/include/linux/poll.h
index c08386f..9fb4f40 100644
--- a/include/linux/poll.h
+++ b/include/linux/poll.h
@@ -96,7 +96,7 @@ extern void poll_initwait(struct poll_wqueues *pwq);
 extern void poll_freewait(struct poll_wqueues *pwq);
 extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
                                 ktime_t *expires, unsigned long slack);
-extern long select_estimate_accuracy(struct timespec *tv);
+extern u64 select_estimate_accuracy(struct timespec *tv);
 
 
 static inline int poll_schedule(struct poll_wqueues *pwq, int state)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index a10494a..1cc4a4f 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1784,8 +1784,8 @@ struct task_struct {
         * time slack values; these are used to round up poll() and
         * select() etc timeout values. These are in nanoseconds.
         */
-       unsigned long timer_slack_ns;
-       unsigned long default_timer_slack_ns;
+       u64 timer_slack_ns;
+       u64 default_timer_slack_ns;
 
 #ifdef CONFIG_KASAN
        unsigned int kasan_depth;
diff --git a/kernel/sys.c b/kernel/sys.c
index 78947de..cf8ba54 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2169,7 +2169,10 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, 
unsigned long, arg3,
                error = perf_event_task_enable();
                break;
        case PR_GET_TIMERSLACK:
-               error = current->timer_slack_ns;
+               if (current->timer_slack_ns > ULONG_MAX)
+                       error = ULONG_MAX;
+               else
+                       error = current->timer_slack_ns;
                break;
        case PR_SET_TIMERSLACK:
                if (arg2 <= 0)
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index fa909f9..58a321c 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -979,7 +979,7 @@ static inline ktime_t hrtimer_update_lowres(struct hrtimer 
*timer, ktime_t tim,
  *             relative (HRTIMER_MODE_REL)
  */
 void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
-                           unsigned long delta_ns, const enum hrtimer_mode 
mode)
+                           u64 delta_ns, const enum hrtimer_mode mode)
 {
        struct hrtimer_clock_base *base, *new_base;
        unsigned long flags;
@@ -1548,7 +1548,7 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct 
timespec __user *rmtp,
        struct restart_block *restart;
        struct hrtimer_sleeper t;
        int ret = 0;
-       unsigned long slack;
+       u64 slack;
 
        slack = current->timer_slack_ns;
        if (dl_task(current) || rt_task(current))
@@ -1724,7 +1724,7 @@ void __init hrtimers_init(void)
  * @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, u64 delta,
                               const enum hrtimer_mode mode, int clock)
 {
        struct hrtimer_sleeper t;
@@ -1792,7 +1792,7 @@ 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, u64 delta,
                                     const enum hrtimer_mode mode)
 {
        return schedule_hrtimeout_range_clock(expires, delta, mode,
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index bbc5d11..d1798fa 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1698,10 +1698,10 @@ EXPORT_SYMBOL(msleep_interruptible);
 static void __sched do_usleep_range(unsigned long min, unsigned long max)
 {
        ktime_t kmin;
-       unsigned long delta;
+       u64 delta;
 
        kmin = ktime_set(0, min * NSEC_PER_USEC);
-       delta = (max - min) * NSEC_PER_USEC;
+       delta = (u64)(max - min) * NSEC_PER_USEC;
        schedule_hrtimeout_range(&kmin, delta, HRTIMER_MODE_REL);
 }
 
-- 
1.9.1

Reply via email to