diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2008-08-31 11:16:57 -0400 |
---|---|---|
committer | Arjan van de Ven <arjan@linux.intel.com> | 2008-09-06 00:34:59 -0400 |
commit | b773ad40aca5bd755ba886620842f16e8fef6d75 (patch) | |
tree | 7065661f6559aff70244f2805ced48f894c9b38c | |
parent | df0cc0539b4127bd02f64de2c335b4af1fdb3845 (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.c | 51 | ||||
-rw-r--r-- | fs/select.c | 75 | ||||
-rw-r--r-- | include/linux/poll.h | 2 |
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 | ||
1439 | static 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 | |||
1484 | sticky: | ||
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 | */ | ||
144 | int 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 | |||
161 | static 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 | |||
200 | sticky: | ||
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, | |||
120 | extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, | 120 | extern 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 | ||
123 | extern 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 */ |