hrtimer: make select() and poll() use the hrtimer range feature
This patch makes the select() and poll() hrtimers use the new range
feature and settings from the task struct.
In addition, this includes the estimate_accuracy() function that Linus
posted to lkml, but changed entirely based on other peoples lkml feedback.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
diff --git a/fs/select.c b/fs/select.c
index f6dceb5..5e61b43 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -28,6 +28,58 @@
#include <asm/uaccess.h>
+
+/*
+ * Estimate expected accuracy in ns from a timeval.
+ *
+ * After quite a bit of churning around, we've settled on
+ * a simple thing of taking 0.1% of the timeout as the
+ * slack, with a cap of 100 msec.
+ * "nice" tasks get a 0.5% slack instead.
+ *
+ * Consider this comment an open invitation to come up with even
+ * better solutions..
+ */
+
+static unsigned long __estimate_accuracy(struct timespec *tv)
+{
+ unsigned long slack;
+ int divfactor = 1000;
+
+ if (task_nice(current))
+ divfactor = divfactor / 5;
+
+ slack = tv->tv_nsec / divfactor;
+ slack += tv->tv_sec * (NSEC_PER_SEC/divfactor);
+
+ if (slack > 100 * NSEC_PER_MSEC)
+ slack = 100 * NSEC_PER_MSEC;
+ return slack;
+}
+
+static unsigned long estimate_accuracy(struct timespec *tv)
+{
+ unsigned long ret;
+ struct timespec now;
+
+ /*
+ * Realtime tasks get a slack of 0 for obvious reasons.
+ */
+
+ if (current->policy == SCHED_FIFO ||
+ current->policy == SCHED_RR)
+ return 0;
+
+ ktime_get_ts(&now);
+ now = timespec_sub(*tv, now);
+ ret = __estimate_accuracy(&now);
+ if (ret < current->timer_slack_ns)
+ return current->timer_slack_ns;
+ return ret;
+}
+
+
+
struct poll_table_page {
struct poll_table_page * next;
struct poll_table_entry * entry;
@@ -262,6 +314,7 @@
struct poll_wqueues table;
poll_table *wait;
int retval, i, timed_out = 0;
+ unsigned long slack = 0;
rcu_read_lock();
retval = max_select_fd(n, fds);
@@ -278,6 +331,9 @@
timed_out = 1;
}
+ if (end_time)
+ slack = estimate_accuracy(end_time);
+
retval = 0;
for (;;) {
unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
@@ -353,7 +409,7 @@
to = &expire;
}
- if (!schedule_hrtimeout(to, HRTIMER_MODE_ABS))
+ if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
timed_out = 1;
}
__set_current_state(TASK_RUNNING);
@@ -593,6 +649,7 @@
poll_table* pt = &wait->pt;
ktime_t expire, *to = NULL;
int timed_out = 0, count = 0;
+ unsigned long slack = 0;
/* Optimise the no-wait case */
if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
@@ -600,6 +657,9 @@
timed_out = 1;
}
+ if (end_time)
+ slack = estimate_accuracy(end_time);
+
for (;;) {
struct poll_list *walk;
@@ -646,7 +706,7 @@
to = &expire;
}
- if (!schedule_hrtimeout(to, HRTIMER_MODE_ABS))
+ if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
timed_out = 1;
}
__set_current_state(TASK_RUNNING);