diff options
| author | Eric van Gyzen <vangyzen@FreeBSD.org> | 2017-03-19 00:51:12 +0000 |
|---|---|---|
| committer | Eric van Gyzen <vangyzen@FreeBSD.org> | 2017-03-19 00:51:12 +0000 |
| commit | 3f8455b0905a05552b1155e09165787e1d098060 (patch) | |
| tree | 7e464dbfe7b8012fa2a68d9d0a7b2d3a07b11df3 /sys | |
| parent | a776eeafdcaae027734542d69612d35971b55c1c (diff) | |
Add clock_nanosleep()
Add a clock_nanosleep() syscall, as specified by POSIX.
Make nanosleep() a wrapper around it.
Attach the clock_nanosleep test from NetBSD. Adjust it for the
FreeBSD behavior of updating rmtp only when interrupted by a signal.
I believe this to be POSIX-compliant, since POSIX mentions the rmtp
parameter only in the paragraph about EINTR. This is also what
Linux does. (NetBSD updates rmtp unconditionally.)
Copy the whole nanosleep.2 man page from NetBSD because it is complete
and closely resembles the POSIX description. Edit, polish, and reword it
a bit, being sure to keep any relevant text from the FreeBSD page.
Reviewed by: kib, ngie, jilles
MFC after: 3 weeks
Relnotes: yes
Sponsored by: Dell EMC
Differential Revision: https://reviews.freebsd.org/D10020
Notes
svn path=/head/; revision=315526
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/compat/freebsd32/freebsd32_misc.c | 34 | ||||
| -rw-r--r-- | sys/compat/freebsd32/syscalls.master | 5 | ||||
| -rw-r--r-- | sys/kern/kern_time.c | 138 | ||||
| -rw-r--r-- | sys/kern/syscalls.master | 4 | ||||
| -rw-r--r-- | sys/sys/syscallsubr.h | 2 |
5 files changed, 145 insertions, 38 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 86b1399a98c0..08a072fbfa20 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -129,6 +129,8 @@ CTASSERT(sizeof(struct sigaction32) == 24); static int freebsd32_kevent_copyout(void *arg, struct kevent *kevp, int count); static int freebsd32_kevent_copyin(void *arg, struct kevent *kevp, int count); +static int freebsd32_user_clock_nanosleep(struct thread *td, clockid_t clock_id, + int flags, const struct timespec32 *ua_rqtp, struct timespec32 *ua_rmtp); void freebsd32_rusage_out(const struct rusage *s, struct rusage32 *s32) @@ -2226,28 +2228,48 @@ ofreebsd32_sigstack(struct thread *td, int freebsd32_nanosleep(struct thread *td, struct freebsd32_nanosleep_args *uap) { + + return (freebsd32_user_clock_nanosleep(td, CLOCK_REALTIME, + TIMER_RELTIME, uap->rqtp, uap->rmtp)); +} + +int +freebsd32_clock_nanosleep(struct thread *td, + struct freebsd32_clock_nanosleep_args *uap) +{ + int error; + + error = freebsd32_user_clock_nanosleep(td, uap->clock_id, uap->flags, + uap->rqtp, uap->rmtp); + return (kern_posix_error(td, error)); +} + +static int +freebsd32_user_clock_nanosleep(struct thread *td, clockid_t clock_id, + int flags, const struct timespec32 *ua_rqtp, struct timespec32 *ua_rmtp) +{ struct timespec32 rmt32, rqt32; struct timespec rmt, rqt; int error; - error = copyin(uap->rqtp, &rqt32, sizeof(rqt32)); + error = copyin(ua_rqtp, &rqt32, sizeof(rqt32)); if (error) return (error); CP(rqt32, rqt, tv_sec); CP(rqt32, rqt, tv_nsec); - if (uap->rmtp && - !useracc((caddr_t)uap->rmtp, sizeof(rmt), VM_PROT_WRITE)) + if (ua_rmtp != NULL && (flags & TIMER_ABSTIME) == 0 && + !useracc(ua_rmtp, sizeof(rmt32), VM_PROT_WRITE)) return (EFAULT); - error = kern_nanosleep(td, &rqt, &rmt); - if (error == EINTR && uap->rmtp) { + error = kern_clock_nanosleep(td, clock_id, flags, &rqt, &rmt); + if (error == EINTR && ua_rmtp != NULL && (flags & TIMER_ABSTIME) == 0) { int error2; CP(rmt, rmt32, tv_sec); CP(rmt, rmt32, tv_nsec); - error2 = copyout(&rmt32, uap->rmtp, sizeof(rmt32)); + error2 = copyout(&rmt32, ua_rmtp, sizeof(rmt32)); if (error2) error = error2; } diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 99a5a4e84098..723fbe6e4b67 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -462,7 +462,10 @@ struct ffclock_estimate *cest); } 243 AUE_NULL NOPROTO { int ffclock_getestimate( \ struct ffclock_estimate *cest); } -244 AUE_NULL UNIMPL nosys +244 AUE_NULL STD { int freebsd32_clock_nanosleep( \ + clockid_t clock_id, int flags, \ + const struct timespec32 *rqtp, \ + struct timespec32 *rmtp); } 245 AUE_NULL UNIMPL nosys 246 AUE_NULL UNIMPL nosys 247 AUE_NULL STD { int freebsd32_clock_getcpuclockid2(\ diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c index d31c12168fc9..92672438253a 100644 --- a/sys/kern/kern_time.c +++ b/sys/kern/kern_time.c @@ -86,6 +86,9 @@ static uma_zone_t itimer_zone = NULL; static int settime(struct thread *, struct timeval *); static void timevalfix(struct timeval *); +static int user_clock_nanosleep(struct thread *td, clockid_t clock_id, + int flags, const struct timespec *ua_rqtp, + struct timespec *ua_rmtp); static void itimer_start(void); static int itimer_init(void *, int, int); @@ -481,47 +484,95 @@ kern_clock_getres(struct thread *td, clockid_t clock_id, struct timespec *ts) return (0); } +int +kern_nanosleep(struct thread *td, struct timespec *rqt, struct timespec *rmt) +{ + + return (kern_clock_nanosleep(td, CLOCK_REALTIME, TIMER_RELTIME, rqt, + rmt)); +} + static uint8_t nanowait[MAXCPU]; int -kern_nanosleep(struct thread *td, struct timespec *rqt, struct timespec *rmt) +kern_clock_nanosleep(struct thread *td, clockid_t clock_id, int flags, + const struct timespec *rqt, struct timespec *rmt) { - struct timespec ts; + struct timespec ts, now; sbintime_t sbt, sbtt, prec, tmp; time_t over; int error; + bool is_abs_real; if (rqt->tv_nsec < 0 || rqt->tv_nsec >= 1000000000) return (EINVAL); - if (rqt->tv_sec < 0 || (rqt->tv_sec == 0 && rqt->tv_nsec == 0)) - return (0); - ts = *rqt; - if (ts.tv_sec > INT32_MAX / 2) { - over = ts.tv_sec - INT32_MAX / 2; - ts.tv_sec -= over; - } else - over = 0; - tmp = tstosbt(ts); - prec = tmp; - prec >>= tc_precexp; - if (TIMESEL(&sbt, tmp)) - sbt += tc_tick_sbt; - sbt += tmp; - error = tsleep_sbt(&nanowait[curcpu], PWAIT | PCATCH, "nanslp", - sbt, prec, C_ABSOLUTE); + if ((flags & ~TIMER_ABSTIME) != 0) + return (EINVAL); + switch (clock_id) { + case CLOCK_REALTIME: + case CLOCK_REALTIME_PRECISE: + case CLOCK_REALTIME_FAST: + case CLOCK_SECOND: + is_abs_real = (flags & TIMER_ABSTIME) != 0; + break; + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_PRECISE: + case CLOCK_MONOTONIC_FAST: + case CLOCK_UPTIME: + case CLOCK_UPTIME_PRECISE: + case CLOCK_UPTIME_FAST: + is_abs_real = false; + break; + case CLOCK_VIRTUAL: + case CLOCK_PROF: + case CLOCK_PROCESS_CPUTIME_ID: + return (ENOTSUP); + case CLOCK_THREAD_CPUTIME_ID: + default: + return (EINVAL); + } + do { + ts = *rqt; + if ((flags & TIMER_ABSTIME) != 0) { + if (is_abs_real) + td->td_rtcgen = + atomic_load_acq_int(&rtc_generation); + error = kern_clock_gettime(td, clock_id, &now); + KASSERT(error == 0, ("kern_clock_gettime: %d", error)); + timespecsub(&ts, &now); + } + if (ts.tv_sec < 0 || (ts.tv_sec == 0 && ts.tv_nsec == 0)) { + error = EWOULDBLOCK; + break; + } + if (ts.tv_sec > INT32_MAX / 2) { + over = ts.tv_sec - INT32_MAX / 2; + ts.tv_sec -= over; + } else + over = 0; + tmp = tstosbt(ts); + prec = tmp; + prec >>= tc_precexp; + if (TIMESEL(&sbt, tmp)) + sbt += tc_tick_sbt; + sbt += tmp; + error = tsleep_sbt(&nanowait[curcpu], PWAIT | PCATCH, "nanslp", + sbt, prec, C_ABSOLUTE); + } while (error == 0 && is_abs_real && td->td_rtcgen == 0); + td->td_rtcgen = 0; if (error != EWOULDBLOCK) { + TIMESEL(&sbtt, tmp); + if (sbtt >= sbt) + return (0); if (error == ERESTART) error = EINTR; - TIMESEL(&sbtt, tmp); - if (rmt != NULL) { + if ((flags & TIMER_ABSTIME) == 0 && rmt != NULL) { ts = sbttots(sbt - sbtt); ts.tv_sec += over; if (ts.tv_sec < 0) timespecclear(&ts); *rmt = ts; } - if (sbtt >= sbt) - return (0); return (error); } return (0); @@ -537,21 +588,48 @@ struct nanosleep_args { int sys_nanosleep(struct thread *td, struct nanosleep_args *uap) { + + return (user_clock_nanosleep(td, CLOCK_REALTIME, TIMER_RELTIME, + uap->rqtp, uap->rmtp)); +} + +#ifndef _SYS_SYSPROTO_H_ +struct clock_nanosleep_args { + clockid_t clock_id; + int flags; + struct timespec *rqtp; + struct timespec *rmtp; +}; +#endif +/* ARGSUSED */ +int +sys_clock_nanosleep(struct thread *td, struct clock_nanosleep_args *uap) +{ + int error; + + error = user_clock_nanosleep(td, uap->clock_id, uap->flags, uap->rqtp, + uap->rmtp); + return (kern_posix_error(td, error)); +} + +static int +user_clock_nanosleep(struct thread *td, clockid_t clock_id, int flags, + const struct timespec *ua_rqtp, struct timespec *ua_rmtp) +{ struct timespec rmt, rqt; int error; - error = copyin(uap->rqtp, &rqt, sizeof(rqt)); + error = copyin(ua_rqtp, &rqt, sizeof(rqt)); if (error) return (error); - - if (uap->rmtp && - !useracc((caddr_t)uap->rmtp, sizeof(rmt), VM_PROT_WRITE)) - return (EFAULT); - error = kern_nanosleep(td, &rqt, &rmt); - if (error == EINTR && uap->rmtp) { + if (ua_rmtp != NULL && (flags & TIMER_ABSTIME) == 0 && + !useracc(ua_rmtp, sizeof(rmt), VM_PROT_WRITE)) + return (EFAULT); + error = kern_clock_nanosleep(td, clock_id, flags, &rqt, &rmt); + if (error == EINTR && ua_rmtp != NULL && (flags & TIMER_ABSTIME) == 0) { int error2; - error2 = copyout(&rmt, uap->rmtp, sizeof(rmt)); + error2 = copyout(&rmt, ua_rmtp, sizeof(rmt)); if (error2) error = error2; } diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 61b3ffac614f..75e91f36c950 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -461,7 +461,9 @@ struct ffclock_estimate *cest); } 243 AUE_NULL STD { int ffclock_getestimate( \ struct ffclock_estimate *cest); } -244 AUE_NULL UNIMPL nosys +244 AUE_NULL STD { int clock_nanosleep(clockid_t clock_id, \ + int flags, const struct timespec *rqtp, \ + struct timespec *rmtp); } 245 AUE_NULL UNIMPL nosys 246 AUE_NULL UNIMPL nosys 247 AUE_NULL STD { int clock_getcpuclockid2(id_t id,\ diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index ef8326e01148..9a9dbe7d4247 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -82,6 +82,8 @@ int kern_clock_getres(struct thread *td, clockid_t clock_id, struct timespec *ts); int kern_clock_gettime(struct thread *td, clockid_t clock_id, struct timespec *ats); +int kern_clock_nanosleep(struct thread *td, clockid_t clock_id, int flags, + const struct timespec *rqtp, struct timespec *rmtp); int kern_clock_settime(struct thread *td, clockid_t clock_id, struct timespec *ats); int kern_close(struct thread *td, int fd); |
