*** kern/kern_time.c.orig Tue Jul 18 06:04:08 1995 --- kern/kern_time.c Tue Jul 18 06:04:46 1995 *************** *** 185,191 **** --- 185,594 ---- return (0); } + #ifdef MRITIMER + #define STATIC /* I thought these functions ought to be static, but */ + /* that was causing problems. I don't recall why. */ + /* + * Dereference an mrtimer handle. Return a pointer to the mrtimer + * structure, or NULL if the handle is invalid or out of date. Should + * only be called with interrupt level splclock or higher. + */ + STATIC struct mrtimer * + derefmrthandle(p, handle) + struct proc *p; /* Process using the handle. */ + struct timeval *handle; /* The handle itself. */ + { + struct mrtimer *mrt; + + if (handle->tv_usec < 0 || handle->tv_usec >= nmrtimer) + return 0; + mrt = mrtimer + handle->tv_usec; + if (mrt->p != p || mrt->usecount != handle->tv_sec) + return 0; + return mrt; + } + + /* + * Link an mrtimer into the process' running timer list. This is not a + * general-purpose function; it is code coincidentally common to both + * startmrtimer() and enqueuemrtimer(), and has been factored out. + * Should be called with interrupt level splclock or higher. + */ + STATIC void + linkmrtimer(p, mrt) + struct proc *p; /* Process owning the timer. */ + struct mrtimer *mrt; /* Timer to be linked. */ + { + if ((mrt->prev = p->p_mrt_tail) == NULL) { + if ((mrt->next = p->p_mrt_head) != NULL) + mrt->next->prev = mrt; + p->p_mrt_head = mrt; + } + else { + if ((mrt->next = mrt->prev->next) != NULL) + mrt->next->prev = mrt; + mrt->prev->next = mrt; + } + + } + + /* + * Unlink a running or expired timer from a process' lists. Should only + * be called with interrupt level splclock or higher. + */ + STATIC void + unlinkmrtimer(p, mrt) + struct proc *p; /* Process owning the timer. */ + struct mrtimer *mrt; /* The timer to be unlinked. */ + { + if (mrt->prev) mrt->prev->next = mrt->next; + if (mrt->next) mrt->next->prev = mrt->prev; + if (mrt == p->p_mrt_tail) p->p_mrt_tail = mrt->prev; + if (mrt == p->p_mrt_head) p->p_mrt_head = mrt->next; + } + + /* + * Put a timer back on the free list. It must already be unlinked from + * any process' running list or expired queue. Should be called with + * interrupt level splclock or higher. + */ + STATIC void + freemrtimer(mrt) + struct mrtimer *mrt; /* The timer to be freed. */ + { + ++mrt->usecount; + mrt->p = NULL; + mrt->next = mrfree; + mrfree = mrt; + } + + /* + * Choose an arbitrary mrtimer from a process' set of running timers. + * Returns NULL if there are none. Should only be called with interrupt + * level splclock or higher. + */ + STATIC struct mrtimer * + runningmrtimer(p) + struct proc *p; /* Process owning the timer. */ + { + return p->p_mrt_tail ? p->p_mrt_tail->next : p->p_mrt_head; + } + + /* + * Enqueue an mrtimer on a process' expired timer queue. Should only be + * called with interrupt level splclock or higher. + */ + STATIC void + enqueuemrtimer(p, mrt) + struct proc *p; /* Process owning the timer. */ + struct mrtimer *mrt; /* Timer to be enqueued. */ + { + linkmrtimer(p,mrt); + p->p_mrt_tail = mrt; + } + + /* + * Dequeue an mrtimer from a process' expired timer queue. Returns NULL + * if the queue is empty. Should only be called with interrupt level + * splclock or higher. + */ + STATIC struct mrtimer * + dequeuemrtimer(p) + struct proc *p; /* Process owning the queue. */ + { + struct mrtimer *mrt; + + if (p->p_mrt_tail == NULL) return NULL; + mrt = p->p_mrt_head; + unlinkmrtimer(p,mrt); + return mrt; + } + + /* + * Multi-Real interval timer expired: + * Move it from the running list to the expired queue, and send a SIGALRM. + */ + STATIC void + mritexpire(arg) + void *arg; + { + register struct mrtimer *mrt; + register struct proc *p; + int s; + + s = splclock(); + + mrt = (struct mrtimer *) arg; + p = mrt->p; + if (!p) panic("mritexpire"); + + unlinkmrtimer(p,mrt); + enqueuemrtimer(p,mrt); + + splx(s); + + psignal(p,SIGALRM); + } + + /* + * Read the value of an mrtimer into a struct itimerval. Should only be + * called with interrupt level splclock or higher. + */ + STATIC void + readmrtimer(mrt, value) + struct mrtimer *mrt; /* Timer to be read. */ + struct itimerval *value; /* Space in which to store value. */ + { + value->it_interval = mrt->expiretime.it_interval; + + if (timercmp(&mrt->expiretime.it_value, &time, <)) + timerclear(&value->it_value); + else + timersub(&mrt->expiretime.it_value, &time, &value->it_value); + } + + /* + * Start an mrtimer. Adds it to the process' running timer list, and + * sets up a callout. Should only be called with interrupt level + * splclock or higher. + */ + STATIC void + startmrtimer(p, mrt) + struct proc *p; /* Process owning the timer. */ + struct mrtimer *mrt; /* Timer to be started. */ + { + linkmrtimer(p,mrt); + + #ifndef MRIT_USE_SETCALLOUT + timeout( + #else /* MRIT_USE_SETCALLOUT is defined */ + mrt->callout = setcallout( + #endif /* MRIT_USE_SETCALLOUT */ + mritexpire, mrt, hzto(&mrt->expiretime.it_value)); + } + + /* + * Stop an mrtimer, given an ovalue containing a handle for it. Unlinks + * the timer, cancels its callout, overwrites the ovalue with the time + * remaining on the timer, and returns a pointer to the mrtimer. If + * the handle is invalid, returns NULL. Should only be called with + * interrupt level splclock or higher. + */ + STATIC struct mrtimer * + stopmrtimer(p, ovalue) + struct proc *p; /* Process owning the timer. */ + struct itimerval *ovalue; /* specifies timer to be canceled. */ + { + struct mrtimer *mrt; + + mrt = derefmrthandle(p, &ovalue->it_value); + if (!mrt) return NULL; + + unlinkmrtimer(p,mrt); + + #ifndef MRIT_USE_SETCALLOUT + untimeout(mritexpire,mrt); + #else /* MRIT_USE_SETCALLOUT is defined */ + unsetcallout(mrt->callout); + #endif /* MRIT_USE_SETCALLOUT */ + + readmrtimer(mrt,ovalue); + + return mrt; + } + + /* + * Reap an expired timer. Returns 0 on success, EAGAIN on empty + * queue. On success, writes handle and label of expired timer into + * the supplied itimerval structure. Frees or reloads the timer as + * appropriate. + */ + STATIC int + reapmrtimer(p, id) + struct proc *p; /* Process owning the timer. */ + struct itimerval *id; /* Space for handle, label, in kernel space. */ + { + struct mrtimer *mrt; + int s; + + s = splclock(); + + mrt = dequeuemrtimer(p); + if (!mrt) { + splx(s); + return EAGAIN; + } + + id->it_value.tv_sec = mrt->usecount; + id->it_value.tv_usec = mrt - mrtimer; + id->it_interval = mrt->label; + + if (!timerisset(&mrt->expiretime.it_interval)) { + freemrtimer(mrt); + splx(s); + return 0; + } + + for (;;) { + timeradd(&mrt->expiretime.it_value, + &mrt->expiretime.it_interval, + &mrt->expiretime.it_value); + if (timercmp(&mrt->expiretime.it_value, &time, >)) + break; + splx(s); + (void) splclock(); + } + + startmrtimer(p,mrt); + + splx(s); + + return 0; + } + + /* + * Take care of the case in getitimer() where which == ITIMER_MULTIREAL. + */ + STATIC int + getmritimer(p, uvalue) + struct proc *p; /* Process. */ + struct itimerval *uvalue; /* value parameter, in user space. */ + { + int error; + struct mrtimer *mrt; + struct itimerval value; + int s; + + error = copyin((caddr_t) uvalue, (caddr_t) &value, + sizeof(struct itimerval)); + if (error) return error; + + if (value.it_value.tv_sec == -1 && value.it_value.tv_usec == -1) { + error = reapmrtimer(p, &value); + if (error) return error; + } + else { + s = splclock(); + mrt = derefmrthandle(p, &value.it_value); + if (!mrt) { + splx(s); + return EINVAL; + } + readmrtimer(mrt, &value); + splx(s); + } + + return copyout((caddr_t) &value, (caddr_t) uvalue, + sizeof (struct itimerval)); + } + + /* + * Take care of the case in setitimer() where which == ITIMER_MULTIREAL. + */ + STATIC int + setmritimer(p, value, uovalue) + struct proc *p; /* Process. */ + struct itimerval *value; /* value parameter, in kernel space. */ + struct itimerval *uovalue; /* ovalue parameter, in user space. */ + { + struct itimerval ovalue; + struct mrtimer *mrt, *t; + int s, error; + + if (!uovalue) return EINVAL; + error = copyin((caddr_t) uovalue, (caddr_t) &ovalue, + sizeof(struct itimerval)); + if (error) return error; + + s = splclock(); + + if (!timerisset(&value->it_value)) { + /* Cancel an existing timer. */ + + mrt = stopmrtimer(p, &ovalue); + if (!mrt) { + splx(s); + return EINVAL; + } + freemrtimer(mrt); + } else { + /* Set a new or existing timer. */ + + if ( ovalue.it_value.tv_sec == -1 + && ovalue.it_value.tv_usec == -1) { + mrt = mrfree; + if (!mrt) { + splx(s); + return EAGAIN; + } + mrfree = mrt->next; + + mrt->p = p; + mrt->label = ovalue.it_interval; + ovalue.it_value.tv_sec = mrt->usecount; + ovalue.it_value.tv_usec = mrt - mrtimer; + } + else { + struct timeval label = ovalue.it_interval; + + mrt = stopmrtimer(p, &ovalue); + if (!mrt) { + splx(s); + return EINVAL; + } + + mrt->label = label; + + } + + mrt->expiretime.it_interval = value->it_interval; + timeradd(&value->it_value, &time, &mrt->expiretime.it_value); + + startmrtimer(p,mrt); + } + + splx(s); + + return copyout((caddr_t) &ovalue, (caddr_t) uovalue, + sizeof (struct itimerval)); + } + + /* + * Destroy all mrtimers owned by a process (used as the process dies). + */ + void + mritexit(p) + struct proc *p; + { + struct mrtimer *mrt; + int s; + + s = splclock(); + + while ((mrt = dequeuemrtimer(p)) != NULL) { + freemrtimer(mrt); + splx(s); + (void) splclock(); + } + + while ((mrt = runningmrtimer(p)) != NULL) { + unlinkmrtimer(p,mrt); + #ifndef MRIT_USE_SETCALLOUT + untimeout(mritexpire,mrt); + #else /* MRIT_USE_SETCALLOUT is defined */ + unsetcallout(mrt->callout); + #endif /* MRIT_USE_SETCALLOUT */ + freemrtimer(mrt); + splx(s); + (void) splclock(); + } + + splx(s); + } + #endif /* MRITIMER */ + + /* * Get value of an interval timer. The process virtual and * profiling virtual time timers are kept in the p_stats area, since * they can be swapped out. These are kept internally in the *************** *** 219,226 **** struct itimerval aitv; int s; ! if (SCARG(uap, which) > ITIMER_PROF) return (EINVAL); s = splclock(); if (SCARG(uap, which) == ITIMER_REAL) { /* --- 622,636 ---- struct itimerval aitv; int s; ! #ifndef MRITIMER ! if (SCARG(uap, which) > ITIMER_REAL) return (EINVAL); + #else /* MRITIMER is defined */ + if (SCARG(uap, which) > ITIMER_MULTIREAL) + return (EINVAL); + if (SCARG(uap, which) == ITIMER_MULTIREAL) + return getmritimer(p, SCARG(uap,itv)); + #endif /* MRITIMER */ s = splclock(); if (SCARG(uap, which) == ITIMER_REAL) { /* *************** *** 257,268 **** register struct itimerval *itvp; int s, error; ! if (SCARG(uap, which) > ITIMER_PROF) return (EINVAL); itvp = SCARG(uap, itv); if (itvp && (error = copyin((caddr_t)itvp, (caddr_t)&aitv, sizeof(struct itimerval)))) return (error); if ((SCARG(uap, itv) = SCARG(uap, oitv)) && (error = getitimer(p, uap, retval))) return (error); --- 667,686 ---- register struct itimerval *itvp; int s, error; ! #ifndef MRITIMER ! if (SCARG(uap, which) > ITIMER_REAL) ! #else /* MRITIMER is defined */ ! if (SCARG(uap, which) > ITIMER_MULTIREAL) ! #endif /* MRITIMER */ return (EINVAL); itvp = SCARG(uap, itv); if (itvp && (error = copyin((caddr_t)itvp, (caddr_t)&aitv, sizeof(struct itimerval)))) return (error); + #ifdef MRITIMER + if (SCARG(uap,which) != ITIMER_MULTIREAL) + /* The next if statement will be inside this one. */ + #endif /* MRITIMER */ if ((SCARG(uap, itv) = SCARG(uap, oitv)) && (error = getitimer(p, uap, retval))) return (error); *************** *** 270,275 **** --- 688,697 ---- return (0); if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval)) return (EINVAL); + #ifdef MRITIMER + if (SCARG(uap,which) == ITIMER_MULTIREAL) + return setmritimer(p, &aitv, SCARG(uap,oitv)); + #endif /* MRITIMER */ s = splclock(); if (SCARG(uap, which) == ITIMER_REAL) { untimeout(realitexpire, p);