diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c --- a/sys/kern/sysv_sem.c +++ b/sys/kern/sysv_sem.c @@ -1097,11 +1097,18 @@ #endif int sys_semop(struct thread *td, struct semop_args *uap) +{ + + return (kern_semop(td, uap->semid, uap->sops, uap->nsops, NULL)); +} + +int +kern_semop(struct thread *td, int usemid, struct sembuf *usops, + size_t nsops, struct timespec *timeout) { #define SMALL_SOPS 8 struct sembuf small_sops[SMALL_SOPS]; - int semid = uap->semid; - size_t nsops = uap->nsops; + int semid; struct prison *rpr; struct sembuf *sops; struct semid_kernel *semakptr; @@ -1109,6 +1116,7 @@ struct sem *semptr = NULL; struct sem_undo *suptr; struct mtx *sema_mtxp; + sbintime_t sbt, precision; size_t i, j, k; int error; int do_wakeup, do_undos; @@ -1117,18 +1125,35 @@ #ifdef SEM_DEBUG sops = NULL; #endif - DPRINTF(("call to semop(%d, %p, %u)\n", semid, sops, nsops)); + DPRINTF(("call to semop(%d, %p, %u)\n", usemid, usops, nsops)); - AUDIT_ARG_SVIPC_ID(semid); + AUDIT_ARG_SVIPC_ID(usemid); rpr = sem_find_prison(td->td_ucred); if (sem == NULL) return (ENOSYS); - semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ + semid = IPCID_TO_IX(usemid); /* Convert back to zero origin */ if (semid < 0 || semid >= seminfo.semmni) return (EINVAL); + if (timeout != NULL) { + if (!timespecvalid_interval(timeout)) + return (EINVAL); + precision = 0; + if (timespecisset(timeout)) { + if (timeout->tv_sec < INT32_MAX / 2) { + precision = tstosbt(*timeout); + if (TIMESEL(&sbt, precision)) + sbt += tc_tick_sbt; + sbt += precision; + precision >>= tc_precexp; + } else + sbt = 0; + } else + sbt = -1; + } else + precision = sbt = 0; /* Allocate memory for sem_ops */ if (nsops <= SMALL_SOPS) @@ -1152,9 +1177,9 @@ sops = malloc(nsops * sizeof(*sops), M_TEMP, M_WAITOK); } - if ((error = copyin(uap->sops, sops, nsops * sizeof(sops[0]))) != 0) { + if ((error = copyin(usops, sops, nsops * sizeof(sops[0]))) != 0) { DPRINTF(("error = %d from copyin(%p, %p, %d)\n", error, - uap->sops, sops, nsops * sizeof(sops[0]))); + usops, sops, nsops * sizeof(sops[0]))); if (sops != small_sops) free(sops, M_TEMP); return (error); @@ -1168,7 +1193,7 @@ goto done2; } seq = semakptr->u.sem_perm.seq; - if (seq != IPCID_TO_SEQ(uap->semid)) { + if (seq != IPCID_TO_SEQ(usemid)) { error = EINVAL; goto done2; } @@ -1286,8 +1311,8 @@ semptr->semncnt++; DPRINTF(("semop: good night!\n")); - error = msleep(semakptr, sema_mtxp, (PZERO - 4) | PCATCH, - "semwait", 0); + error = msleep_sbt(semakptr, sema_mtxp, (PZERO - 4) | PCATCH, + "semwait", sbt, precision, C_ABSOLUTE); DPRINTF(("semop: good morning (error=%d)!\n", error)); /* return code is checked below, after sem[nz]cnt-- */ @@ -1296,7 +1321,7 @@ */ seq = semakptr->u.sem_perm.seq; if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || - seq != IPCID_TO_SEQ(uap->semid)) { + seq != IPCID_TO_SEQ(usemid)) { error = EIDRM; goto done2; } @@ -1323,7 +1348,8 @@ * need to decrement sem[nz]cnt either way.) */ if (error != 0) { - error = EINTR; + if (error == ERESTART) + error = EINTR; goto done2; } DPRINTF(("semop: good morning!\n")); diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -57,6 +57,7 @@ struct rlimit; struct rusage; struct sched_param; +struct sembuf; union semun; struct sockaddr; struct spacectl_range; @@ -328,6 +329,8 @@ int kern_ktimer_gettime(struct thread *td, int timer_id, struct itimerspec *val); int kern_ktimer_getoverrun(struct thread *td, int timer_id); +int kern_semop(struct thread *td, int usemid, struct sembuf *usops, + size_t nsops, struct timespec *timeout); int kern_thr_alloc(struct proc *, int pages, struct thread **); int kern_thr_exit(struct thread *td); int kern_thr_new(struct thread *td, struct thr_param *param);