MFcalloutng:

Fix kern_select() and sys_poll() so that they can handle sub-tick
precision for timeouts (in the same fashion it was done for nanosleep()
in r247797).

Sponsored by:	Google Summer of Code 2012, iXsystems inc.
Tested by:	flo, marius, ian, markj, Fabian Keil
This commit is contained in:
Davide Italiano 2013-03-04 16:41:27 +00:00
parent 4601bab1fb
commit cf5e4fe6bb

View File

@ -103,7 +103,7 @@ static int dofilewrite(struct thread *, int, struct file *, struct uio *,
off_t, int); off_t, int);
static void doselwakeup(struct selinfo *, int); static void doselwakeup(struct selinfo *, int);
static void seltdinit(struct thread *); static void seltdinit(struct thread *);
static int seltdwait(struct thread *, int); static int seltdwait(struct thread *, sbintime_t, sbintime_t);
static void seltdclear(struct thread *); static void seltdclear(struct thread *);
/* /*
@ -950,9 +950,10 @@ kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou,
*/ */
fd_mask s_selbits[howmany(2048, NFDBITS)]; fd_mask s_selbits[howmany(2048, NFDBITS)];
fd_mask *ibits[3], *obits[3], *selbits, *sbp; fd_mask *ibits[3], *obits[3], *selbits, *sbp;
struct timeval atv, rtv, ttv; struct timeval rtv;
int error, lf, ndu, timo; sbintime_t asbt, precision, rsbt;
u_int nbufbytes, ncpbytes, ncpubytes, nfdbits; u_int nbufbytes, ncpbytes, ncpubytes, nfdbits;
int error, lf, ndu;
if (nd < 0) if (nd < 0)
return (EINVAL); return (EINVAL);
@ -1042,35 +1043,29 @@ kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou,
if (nbufbytes != 0) if (nbufbytes != 0)
bzero(selbits, nbufbytes / 2); bzero(selbits, nbufbytes / 2);
precision = 0;
if (tvp != NULL) { if (tvp != NULL) {
atv = *tvp; rtv = *tvp;
if (itimerfix(&atv)) { if (rtv.tv_sec < 0 || rtv.tv_usec < 0 ||
rtv.tv_usec >= 1000000) {
error = EINVAL; error = EINVAL;
goto done; goto done;
} }
getmicrouptime(&rtv); rsbt = tvtosbt(rtv);
timevaladd(&atv, &rtv); precision = rsbt;
} else { precision >>= tc_precexp;
atv.tv_sec = 0; if (TIMESEL(&asbt, rsbt))
atv.tv_usec = 0; asbt += tc_tick_sbt;
} asbt += rsbt;
timo = 0; } else
asbt = -1;
seltdinit(td); seltdinit(td);
/* Iterate until the timeout expires or descriptors become ready. */ /* Iterate until the timeout expires or descriptors become ready. */
for (;;) { for (;;) {
error = selscan(td, ibits, obits, nd); error = selscan(td, ibits, obits, nd);
if (error || td->td_retval[0] != 0) if (error || td->td_retval[0] != 0)
break; break;
if (atv.tv_sec || atv.tv_usec) { error = seltdwait(td, asbt, precision);
getmicrouptime(&rtv);
if (timevalcmp(&rtv, &atv, >=))
break;
ttv = atv;
timevalsub(&ttv, &rtv);
timo = ttv.tv_sec > 24 * 60 * 60 ?
24 * 60 * 60 * hz : tvtohz(&ttv);
}
error = seltdwait(td, timo);
if (error) if (error)
break; break;
error = selrescan(td, ibits, obits); error = selrescan(td, ibits, obits);
@ -1278,9 +1273,9 @@ sys_poll(td, uap)
{ {
struct pollfd *bits; struct pollfd *bits;
struct pollfd smallbits[32]; struct pollfd smallbits[32];
struct timeval atv, rtv, ttv; sbintime_t asbt, precision, rsbt;
int error, timo;
u_int nfds; u_int nfds;
int error;
size_t ni; size_t ni;
nfds = uap->nfds; nfds = uap->nfds;
@ -1294,36 +1289,27 @@ sys_poll(td, uap)
error = copyin(uap->fds, bits, ni); error = copyin(uap->fds, bits, ni);
if (error) if (error)
goto done; goto done;
precision = 0;
if (uap->timeout != INFTIM) { if (uap->timeout != INFTIM) {
atv.tv_sec = uap->timeout / 1000; if (uap->timeout < 0) {
atv.tv_usec = (uap->timeout % 1000) * 1000;
if (itimerfix(&atv)) {
error = EINVAL; error = EINVAL;
goto done; goto done;
} }
getmicrouptime(&rtv); rsbt = SBT_1MS * uap->timeout;
timevaladd(&atv, &rtv); precision = rsbt;
} else { precision >>= tc_precexp;
atv.tv_sec = 0; if (TIMESEL(&asbt, rsbt))
atv.tv_usec = 0; asbt += tc_tick_sbt;
} asbt += rsbt;
timo = 0; } else
asbt = -1;
seltdinit(td); seltdinit(td);
/* Iterate until the timeout expires or descriptors become ready. */ /* Iterate until the timeout expires or descriptors become ready. */
for (;;) { for (;;) {
error = pollscan(td, bits, nfds); error = pollscan(td, bits, nfds);
if (error || td->td_retval[0] != 0) if (error || td->td_retval[0] != 0)
break; break;
if (atv.tv_sec || atv.tv_usec) { error = seltdwait(td, asbt, precision);
getmicrouptime(&rtv);
if (timevalcmp(&rtv, &atv, >=))
break;
ttv = atv;
timevalsub(&ttv, &rtv);
timo = ttv.tv_sec > 24 * 60 * 60 ?
24 * 60 * 60 * hz : tvtohz(&ttv);
}
error = seltdwait(td, timo);
if (error) if (error)
break; break;
error = pollrescan(td); error = pollrescan(td);
@ -1667,7 +1653,7 @@ out:
} }
static int static int
seltdwait(struct thread *td, int timo) seltdwait(struct thread *td, sbintime_t sbt, sbintime_t precision)
{ {
struct seltd *stp; struct seltd *stp;
int error; int error;
@ -1686,8 +1672,11 @@ seltdwait(struct thread *td, int timo)
mtx_unlock(&stp->st_mtx); mtx_unlock(&stp->st_mtx);
return (0); return (0);
} }
if (timo > 0) if (sbt == 0)
error = cv_timedwait_sig(&stp->st_wait, &stp->st_mtx, timo); error = EWOULDBLOCK;
else if (sbt != -1)
error = cv_timedwait_sig_sbt(&stp->st_wait, &stp->st_mtx,
sbt, precision, C_ABSOLUTE);
else else
error = cv_wait_sig(&stp->st_wait, &stp->st_mtx); error = cv_wait_sig(&stp->st_wait, &stp->st_mtx);
mtx_unlock(&stp->st_mtx); mtx_unlock(&stp->st_mtx);