aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2008-08-31 11:16:57 -0400
committerArjan van de Ven <arjan@linux.intel.com>2008-09-06 00:34:59 -0400
commitb773ad40aca5bd755ba886620842f16e8fef6d75 (patch)
tree7065661f6559aff70244f2805ced48f894c9b38c
parentdf0cc0539b4127bd02f64de2c335b4af1fdb3845 (diff)
select: add poll_select_set_timeout() and poll_select_copy_remaining() helpers
This patch adds 2 helpers that will be used for the hrtimer based select/poll: poll_select_set_timeout() is a helper that takes a timeout (as a second, nanosecond pair) and turns that into a "struct timespec" that represents the absolute end time. This is a common operation in the many select() and poll() variants and needs various, common, sanity checks. poll_select_copy_remaining() is a helper that takes care of copying the remaining time to userspace, as select(), pselect() and ppoll() do. This function comes in both a natural and a compat implementation (due to datastructure differences). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
-rw-r--r--fs/compat.c51
-rw-r--r--fs/select.c75
-rw-r--r--include/linux/poll.h2
3 files changed, 128 insertions, 0 deletions
diff --git a/fs/compat.c b/fs/compat.c
index 075d0509970d..424767c954a0 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1436,6 +1436,57 @@ out_ret:
1436 1436
1437#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t)) 1437#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
1438 1438
1439static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
1440 int timeval, int ret)
1441{
1442 struct timespec ts;
1443
1444 if (!p)
1445 return ret;
1446
1447 if (current->personality & STICKY_TIMEOUTS)
1448 goto sticky;
1449
1450 /* No update for zero timeout */
1451 if (!end_time->tv_sec && !end_time->tv_nsec)
1452 return ret;
1453
1454 ktime_get_ts(&ts);
1455 ts = timespec_sub(*end_time, ts);
1456 if (ts.tv_sec < 0)
1457 ts.tv_sec = ts.tv_nsec = 0;
1458
1459 if (timeval) {
1460 struct compat_timeval rtv;
1461
1462 rtv.tv_sec = ts.tv_sec;
1463 rtv.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
1464
1465 if (!copy_to_user(p, &rtv, sizeof(rtv)))
1466 return ret;
1467 } else {
1468 struct compat_timespec rts;
1469
1470 rts.tv_sec = ts.tv_sec;
1471 rts.tv_nsec = ts.tv_nsec;
1472
1473 if (!copy_to_user(p, &rts, sizeof(rts)))
1474 return ret;
1475 }
1476 /*
1477 * If an application puts its timeval in read-only memory, we
1478 * don't want the Linux-specific update to the timeval to
1479 * cause a fault after the select has completed
1480 * successfully. However, because we're not updating the
1481 * timeval, we can't restart the system call.
1482 */
1483
1484sticky:
1485 if (ret == -ERESTARTNOHAND)
1486 ret = -EINTR;
1487 return ret;
1488}
1489
1439/* 1490/*
1440 * Ooo, nasty. We need here to frob 32-bit unsigned longs to 1491 * Ooo, nasty. We need here to frob 32-bit unsigned longs to
1441 * 64-bit unsigned longs. 1492 * 64-bit unsigned longs.
diff --git a/fs/select.c b/fs/select.c
index da0e88201c3a..1180a6207789 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -130,6 +130,81 @@ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
130 add_wait_queue(wait_address, &entry->wait); 130 add_wait_queue(wait_address, &entry->wait);
131} 131}
132 132
133/**
134 * poll_select_set_timeout - helper function to setup the timeout value
135 * @to: pointer to timespec variable for the final timeout
136 * @sec: seconds (from user space)
137 * @nsec: nanoseconds (from user space)
138 *
139 * Note, we do not use a timespec for the user space value here, That
140 * way we can use the function for timeval and compat interfaces as well.
141 *
142 * Returns -EINVAL if sec/nsec are not normalized. Otherwise 0.
143 */
144int poll_select_set_timeout(struct timespec *to, long sec, long nsec)
145{
146 struct timespec ts = {.tv_sec = sec, .tv_nsec = nsec};
147
148 if (!timespec_valid(&ts))
149 return -EINVAL;
150
151 /* Optimize for the zero timeout value here */
152 if (!sec && !nsec) {
153 to->tv_sec = to->tv_nsec = 0;
154 } else {
155 ktime_get_ts(to);
156 *to = timespec_add_safe(*to, ts);
157 }
158 return 0;
159}
160
161static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
162 int timeval, int ret)
163{
164 struct timespec rts;
165 struct timeval rtv;
166
167 if (!p)
168 return ret;
169
170 if (current->personality & STICKY_TIMEOUTS)
171 goto sticky;
172
173 /* No update for zero timeout */
174 if (!end_time->tv_sec && !end_time->tv_nsec)
175 return ret;
176
177 ktime_get_ts(&rts);
178 rts = timespec_sub(*end_time, rts);
179 if (rts.tv_sec < 0)
180 rts.tv_sec = rts.tv_nsec = 0;
181
182 if (timeval) {
183 rtv.tv_sec = rts.tv_sec;
184 rtv.tv_usec = rts.tv_nsec / NSEC_PER_USEC;
185
186 if (!copy_to_user(p, &rtv, sizeof(rtv)))
187 return ret;
188
189 } else if (!copy_to_user(p, &rts, sizeof(rts)))
190 return ret;
191
192 /*
193 * If an application puts its timeval in read-only memory, we
194 * don't want the Linux-specific update to the timeval to
195 * cause a fault after the select has completed
196 * successfully. However, because we're not updating the
197 * timeval, we can't restart the system call.
198 */
199
200sticky:
201 if (ret == -ERESTARTNOHAND)
202 ret = -EINTR;
203 return ret;
204}
205
206
207
133#define FDS_IN(fds, n) (fds->in + n) 208#define FDS_IN(fds, n) (fds->in + n)
134#define FDS_OUT(fds, n) (fds->out + n) 209#define FDS_OUT(fds, n) (fds->out + n)
135#define FDS_EX(fds, n) (fds->ex + n) 210#define FDS_EX(fds, n) (fds->ex + n)
diff --git a/include/linux/poll.h b/include/linux/poll.h
index ef453828877a..f65de5128a9e 100644
--- a/include/linux/poll.h
+++ b/include/linux/poll.h
@@ -120,6 +120,8 @@ extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds,
120extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, 120extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
121 fd_set __user *exp, s64 *timeout); 121 fd_set __user *exp, s64 *timeout);
122 122
123extern int poll_select_set_timeout(struct timespec *to, long sec, long nsec);
124
123#endif /* KERNEL */ 125#endif /* KERNEL */
124 126
125#endif /* _LINUX_POLL_H */ 127#endif /* _LINUX_POLL_H */