summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_umtx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_umtx.c')
-rw-r--r--sys/kern/kern_umtx.c66
1 files changed, 52 insertions, 14 deletions
diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
index ae2dfcd..5accaf7 100644
--- a/sys/kern/kern_umtx.c
+++ b/sys/kern/kern_umtx.c
@@ -3217,10 +3217,16 @@ do_sem2_wait(struct thread *td, struct _usem2 *sem, struct _umtx_time *timeout)
error = 0;
else {
umtxq_remove(uq);
- /* A relative timeout cannot be restarted. */
- if (error == ERESTART && timeout != NULL &&
- (timeout->_flags & UMTX_ABSTIME) == 0)
- error = EINTR;
+ if (timeout != NULL && (timeout->_flags & UMTX_ABSTIME) == 0) {
+ /* A relative timeout cannot be restarted. */
+ if (error == ERESTART)
+ error = EINTR;
+ if (error == EINTR) {
+ abs_timeout_update(&timo);
+ timeout->_timeout = timo.end;
+ timespecsub(&timeout->_timeout, &timo.cur);
+ }
+ }
}
umtxq_unlock(&uq->uq_key);
umtx_key_release(&uq->uq_key);
@@ -3583,19 +3589,33 @@ static int
__umtx_op_sem2_wait(struct thread *td, struct _umtx_op_args *uap)
{
struct _umtx_time *tm_p, timeout;
+ size_t uasize;
int error;
/* Allow a null timespec (wait forever). */
- if (uap->uaddr2 == NULL)
+ if (uap->uaddr2 == NULL) {
+ uasize = 0;
tm_p = NULL;
- else {
- error = umtx_copyin_umtx_time(
- uap->uaddr2, (size_t)uap->uaddr1, &timeout);
+ } else {
+ uasize = (size_t)uap->uaddr1;
+ error = umtx_copyin_umtx_time(uap->uaddr2, uasize, &timeout);
if (error != 0)
return (error);
tm_p = &timeout;
}
- return (do_sem2_wait(td, uap->obj, tm_p));
+ error = do_sem2_wait(td, uap->obj, tm_p);
+ if (error == EINTR && uap->uaddr2 != NULL &&
+ (timeout._flags & UMTX_ABSTIME) == 0 &&
+ uasize >= sizeof(struct _umtx_time) + sizeof(struct timespec)) {
+ error = copyout(&timeout._timeout,
+ (struct _umtx_time *)uap->uaddr2 + 1,
+ sizeof(struct timespec));
+ if (error == 0) {
+ error = EINTR;
+ }
+ }
+
+ return (error);
}
static int
@@ -4192,19 +4212,37 @@ static int
__umtx_op_sem2_wait_compat32(struct thread *td, struct _umtx_op_args *uap)
{
struct _umtx_time *tm_p, timeout;
+ size_t uasize;
int error;
/* Allow a null timespec (wait forever). */
- if (uap->uaddr2 == NULL)
+ if (uap->uaddr2 == NULL) {
+ uasize = 0;
tm_p = NULL;
- else {
- error = umtx_copyin_umtx_time32(uap->uaddr2,
- (size_t)uap->uaddr1, &timeout);
+ } else {
+ uasize = (size_t)uap->uaddr1;
+ error = umtx_copyin_umtx_time32(uap->uaddr2, uasize, &timeout);
if (error != 0)
return (error);
tm_p = &timeout;
}
- return (do_sem2_wait(td, uap->obj, tm_p));
+ error = do_sem2_wait(td, uap->obj, tm_p);
+ if (error == EINTR && uap->uaddr2 != NULL &&
+ (timeout._flags & UMTX_ABSTIME) == 0 &&
+ uasize >= sizeof(struct umtx_time32) + sizeof(struct timespec32)) {
+ struct timespec32 remain32 = {
+ .tv_sec = timeout._timeout.tv_sec,
+ .tv_nsec = timeout._timeout.tv_nsec
+ };
+ error = copyout(&remain32,
+ (struct umtx_time32 *)uap->uaddr2 + 1,
+ sizeof(struct timespec32));
+ if (error == 0) {
+ error = EINTR;
+ }
+ }
+
+ return (error);
}
static int
OpenPOWER on IntegriCloud