summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2016-03-17 17:20:51 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-17 18:09:34 -0400
commitda8b44d5a9f8bf26da637b7336508ca534d6b319 (patch)
treef8ce464a2adfa25d255ada3bd4ec762e0af9628a
parent0a687aace3b8e215e08eed8a67014f5b8f133ab0 (diff)
timer: convert timer_slack_ns from unsigned long to u64
This patchset introduces a /proc/<pid>/timerslack_ns interface which would allow controlling processes to be able to set the timerslack value on other processes in order to save power by avoiding wakeups (Something Android currently does via out-of-tree patches). The first patch tries to fix the internal timer_slack_ns usage which was defined as a long, which limits the slack range to ~4 seconds on 32bit systems. It converts it to a u64, which provides the same basically unlimited slack (500 years) on both 32bit and 64bit machines. The second patch introduces the /proc/<pid>/timerslack_ns interface which allows the full 64bit slack range for a task to be read or set on both 32bit and 64bit machines. With these two patches, on a 32bit machine, after setting the slack on bash to 10 seconds: $ time sleep 1 real 0m10.747s user 0m0.001s sys 0m0.005s The first patch is a little ugly, since I had to chase the slack delta arguments through a number of functions converting them to u64s. Let me know if it makes sense to break that up more or not. Other than that things are fairly straightforward. This patch (of 2): 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 (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. Signed-off-by: John Stultz <john.stultz@linaro.org> Cc: Arjan van de Ven <arjan@linux.intel.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Oren Laadan <orenl@cellrox.com> Cc: Ruchi Kandoi <kandoiruchi@google.com> Cc: Rom Lemarchand <romlem@android.com> Cc: Kees Cook <keescook@chromium.org> Cc: Android Kernel Team <kernel-team@android.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/eventpoll.c2
-rw-r--r--fs/select.c8
-rw-r--r--include/linux/freezer.h2
-rw-r--r--include/linux/hrtimer.h12
-rw-r--r--include/linux/poll.h2
-rw-r--r--include/linux/sched.h4
-rw-r--r--kernel/sys.c5
-rw-r--r--kernel/time/hrtimer.c8
-rw-r--r--kernel/time/timer.c4
9 files changed, 26 insertions, 21 deletions
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index cde60741cad2..8a74a2a52e0f 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,
1616{ 1616{
1617 int res = 0, eavail, timed_out = 0; 1617 int res = 0, eavail, timed_out = 0;
1618 unsigned long flags; 1618 unsigned long flags;
1619 long slack = 0; 1619 u64 slack = 0;
1620 wait_queue_t wait; 1620 wait_queue_t wait;
1621 ktime_t expires, *to = NULL; 1621 ktime_t expires, *to = NULL;
1622 1622
diff --git a/fs/select.c b/fs/select.c
index 79d0d4953cad..869293988c2a 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -70,9 +70,9 @@ static long __estimate_accuracy(struct timespec *tv)
70 return slack; 70 return slack;
71} 71}
72 72
73long select_estimate_accuracy(struct timespec *tv) 73u64 select_estimate_accuracy(struct timespec *tv)
74{ 74{
75 unsigned long ret; 75 u64 ret;
76 struct timespec now; 76 struct timespec now;
77 77
78 /* 78 /*
@@ -402,7 +402,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
402 struct poll_wqueues table; 402 struct poll_wqueues table;
403 poll_table *wait; 403 poll_table *wait;
404 int retval, i, timed_out = 0; 404 int retval, i, timed_out = 0;
405 unsigned long slack = 0; 405 u64 slack = 0;
406 unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0; 406 unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;
407 unsigned long busy_end = 0; 407 unsigned long busy_end = 0;
408 408
@@ -784,7 +784,7 @@ static int do_poll(struct poll_list *list, struct poll_wqueues *wait,
784 poll_table* pt = &wait->pt; 784 poll_table* pt = &wait->pt;
785 ktime_t expire, *to = NULL; 785 ktime_t expire, *to = NULL;
786 int timed_out = 0, count = 0; 786 int timed_out = 0, count = 0;
787 unsigned long slack = 0; 787 u64 slack = 0;
788 unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0; 788 unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;
789 unsigned long busy_end = 0; 789 unsigned long busy_end = 0;
790 790
diff --git a/include/linux/freezer.h b/include/linux/freezer.h
index 6b7fd9cf5ea2..dd03e837ebb7 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)
231 * call this with locks held. 231 * call this with locks held.
232 */ 232 */
233static inline int freezable_schedule_hrtimeout_range(ktime_t *expires, 233static inline int freezable_schedule_hrtimeout_range(ktime_t *expires,
234 unsigned long delta, const enum hrtimer_mode mode) 234 u64 delta, const enum hrtimer_mode mode)
235{ 235{
236 int __retval; 236 int __retval;
237 freezer_do_not_count(); 237 freezer_do_not_count();
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 2ead22dd74a0..c98c6539e2c2 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
220 timer->node.expires = ktime_add_safe(time, delta); 220 timer->node.expires = ktime_add_safe(time, delta);
221} 221}
222 222
223static inline void hrtimer_set_expires_range_ns(struct hrtimer *timer, ktime_t time, unsigned long delta) 223static inline void hrtimer_set_expires_range_ns(struct hrtimer *timer, ktime_t time, u64 delta)
224{ 224{
225 timer->_softexpires = time; 225 timer->_softexpires = time;
226 timer->node.expires = ktime_add_safe(time, ns_to_ktime(delta)); 226 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) { }
378 378
379/* Basic timer operations: */ 379/* Basic timer operations: */
380extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, 380extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
381 unsigned long range_ns, const enum hrtimer_mode mode); 381 u64 range_ns, const enum hrtimer_mode mode);
382 382
383/** 383/**
384 * hrtimer_start - (re)start an hrtimer on the current CPU 384 * hrtimer_start - (re)start an hrtimer on the current CPU
@@ -399,7 +399,7 @@ extern int hrtimer_try_to_cancel(struct hrtimer *timer);
399static inline void hrtimer_start_expires(struct hrtimer *timer, 399static inline void hrtimer_start_expires(struct hrtimer *timer,
400 enum hrtimer_mode mode) 400 enum hrtimer_mode mode)
401{ 401{
402 unsigned long delta; 402 u64 delta;
403 ktime_t soft, hard; 403 ktime_t soft, hard;
404 soft = hrtimer_get_softexpires(timer); 404 soft = hrtimer_get_softexpires(timer);
405 hard = hrtimer_get_expires(timer); 405 hard = hrtimer_get_expires(timer);
@@ -477,10 +477,12 @@ extern long hrtimer_nanosleep_restart(struct restart_block *restart_block);
477extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, 477extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
478 struct task_struct *tsk); 478 struct task_struct *tsk);
479 479
480extern int schedule_hrtimeout_range(ktime_t *expires, unsigned long delta, 480extern int schedule_hrtimeout_range(ktime_t *expires, u64 delta,
481 const enum hrtimer_mode mode); 481 const enum hrtimer_mode mode);
482extern int schedule_hrtimeout_range_clock(ktime_t *expires, 482extern int schedule_hrtimeout_range_clock(ktime_t *expires,
483 unsigned long delta, const enum hrtimer_mode mode, int clock); 483 u64 delta,
484 const enum hrtimer_mode mode,
485 int clock);
484extern int schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode); 486extern int schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode);
485 487
486/* Soft interrupt function to run the hrtimer queues: */ 488/* Soft interrupt function to run the hrtimer queues: */
diff --git a/include/linux/poll.h b/include/linux/poll.h
index c08386fb3e08..9fb4f40d9a26 100644
--- a/include/linux/poll.h
+++ b/include/linux/poll.h
@@ -96,7 +96,7 @@ extern void poll_initwait(struct poll_wqueues *pwq);
96extern void poll_freewait(struct poll_wqueues *pwq); 96extern void poll_freewait(struct poll_wqueues *pwq);
97extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state, 97extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
98 ktime_t *expires, unsigned long slack); 98 ktime_t *expires, unsigned long slack);
99extern long select_estimate_accuracy(struct timespec *tv); 99extern u64 select_estimate_accuracy(struct timespec *tv);
100 100
101 101
102static inline int poll_schedule(struct poll_wqueues *pwq, int state) 102static inline int poll_schedule(struct poll_wqueues *pwq, int state)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index eb7f2f84009b..3284d07edec7 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1792,8 +1792,8 @@ struct task_struct {
1792 * time slack values; these are used to round up poll() and 1792 * time slack values; these are used to round up poll() and
1793 * select() etc timeout values. These are in nanoseconds. 1793 * select() etc timeout values. These are in nanoseconds.
1794 */ 1794 */
1795 unsigned long timer_slack_ns; 1795 u64 timer_slack_ns;
1796 unsigned long default_timer_slack_ns; 1796 u64 default_timer_slack_ns;
1797 1797
1798#ifdef CONFIG_KASAN 1798#ifdef CONFIG_KASAN
1799 unsigned int kasan_depth; 1799 unsigned int kasan_depth;
diff --git a/kernel/sys.c b/kernel/sys.c
index 78947de6f969..cf8ba545c7d3 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2169,7 +2169,10 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
2169 error = perf_event_task_enable(); 2169 error = perf_event_task_enable();
2170 break; 2170 break;
2171 case PR_GET_TIMERSLACK: 2171 case PR_GET_TIMERSLACK:
2172 error = current->timer_slack_ns; 2172 if (current->timer_slack_ns > ULONG_MAX)
2173 error = ULONG_MAX;
2174 else
2175 error = current->timer_slack_ns;
2173 break; 2176 break;
2174 case PR_SET_TIMERSLACK: 2177 case PR_SET_TIMERSLACK:
2175 if (arg2 <= 0) 2178 if (arg2 <= 0)
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index fa909f9fd559..58a321c34cfb 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,
979 * relative (HRTIMER_MODE_REL) 979 * relative (HRTIMER_MODE_REL)
980 */ 980 */
981void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, 981void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
982 unsigned long delta_ns, const enum hrtimer_mode mode) 982 u64 delta_ns, const enum hrtimer_mode mode)
983{ 983{
984 struct hrtimer_clock_base *base, *new_base; 984 struct hrtimer_clock_base *base, *new_base;
985 unsigned long flags; 985 unsigned long flags;
@@ -1548,7 +1548,7 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
1548 struct restart_block *restart; 1548 struct restart_block *restart;
1549 struct hrtimer_sleeper t; 1549 struct hrtimer_sleeper t;
1550 int ret = 0; 1550 int ret = 0;
1551 unsigned long slack; 1551 u64 slack;
1552 1552
1553 slack = current->timer_slack_ns; 1553 slack = current->timer_slack_ns;
1554 if (dl_task(current) || rt_task(current)) 1554 if (dl_task(current) || rt_task(current))
@@ -1724,7 +1724,7 @@ void __init hrtimers_init(void)
1724 * @clock: timer clock, CLOCK_MONOTONIC or CLOCK_REALTIME 1724 * @clock: timer clock, CLOCK_MONOTONIC or CLOCK_REALTIME
1725 */ 1725 */
1726int __sched 1726int __sched
1727schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta, 1727schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
1728 const enum hrtimer_mode mode, int clock) 1728 const enum hrtimer_mode mode, int clock)
1729{ 1729{
1730 struct hrtimer_sleeper t; 1730 struct hrtimer_sleeper t;
@@ -1792,7 +1792,7 @@ schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
1792 * 1792 *
1793 * Returns 0 when the timer has expired otherwise -EINTR 1793 * Returns 0 when the timer has expired otherwise -EINTR
1794 */ 1794 */
1795int __sched schedule_hrtimeout_range(ktime_t *expires, unsigned long delta, 1795int __sched schedule_hrtimeout_range(ktime_t *expires, u64 delta,
1796 const enum hrtimer_mode mode) 1796 const enum hrtimer_mode mode)
1797{ 1797{
1798 return schedule_hrtimeout_range_clock(expires, delta, mode, 1798 return schedule_hrtimeout_range_clock(expires, delta, mode,
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index bbc5d1114583..d1798fa0c743 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1698,10 +1698,10 @@ EXPORT_SYMBOL(msleep_interruptible);
1698static void __sched do_usleep_range(unsigned long min, unsigned long max) 1698static void __sched do_usleep_range(unsigned long min, unsigned long max)
1699{ 1699{
1700 ktime_t kmin; 1700 ktime_t kmin;
1701 unsigned long delta; 1701 u64 delta;
1702 1702
1703 kmin = ktime_set(0, min * NSEC_PER_USEC); 1703 kmin = ktime_set(0, min * NSEC_PER_USEC);
1704 delta = (max - min) * NSEC_PER_USEC; 1704 delta = (u64)(max - min) * NSEC_PER_USEC;
1705 schedule_hrtimeout_range(&kmin, delta, HRTIMER_MODE_REL); 1705 schedule_hrtimeout_range(&kmin, delta, HRTIMER_MODE_REL);
1706} 1706}
1707 1707