Let itimer store itimerspec instead of itimerval, so I don't have to
convert to or from timeval frequently. Introduce function itimer_accept() to ack a timer signal in signal acceptance code, this allows us to return more fresh overrun counter than at signal generating time. while POSIX says: "the value returned by timer_getoverrun() shall apply to the most recent expiration signal delivery or acceptance for the timer,.." I prefer returning it at acceptance time. Introduce SIGEV_THREAD_ID notification mode, it is used by thread libary to request kernel to deliver signal to a specified thread, and in turn, the thread library may use the mechanism to implement SIGEV_THREAD which is required by POSIX. Timer signal is managed by timer code, so it can not fail even if signal queue is full filled by sigqueue syscall.
This commit is contained in:
parent
01790d850d
commit
56c06c4b67
@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/sysent.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/sysproto.h>
|
||||
#include <sys/timers.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <vm/vm.h>
|
||||
@ -1252,6 +1253,8 @@ kern_sigtimedwait(struct thread *td, sigset_t waitset, ksiginfo_t *ksi,
|
||||
ksiginfo_init(ksi);
|
||||
sigqueue_get(&td->td_sigqueue, sig, ksi);
|
||||
ksi->ksi_signo = sig;
|
||||
if (ksi->ksi_code == SI_TIMER)
|
||||
itimer_accept(p, ksi->ksi_timerid, ksi);
|
||||
error = 0;
|
||||
mtx_lock(&ps->ps_mtx);
|
||||
action = ps->ps_sigact[_SIG_IDX(sig)];
|
||||
@ -2670,7 +2673,8 @@ postsig(sig)
|
||||
ksiginfo_init(&ksi);
|
||||
sigqueue_get(&td->td_sigqueue, sig, &ksi);
|
||||
ksi.ksi_signo = sig;
|
||||
|
||||
if (ksi.ksi_code == SI_TIMER)
|
||||
itimer_accept(p, ksi.ksi_timerid, &ksi);
|
||||
action = ps->ps_sigact[_SIG_IDX(sig)];
|
||||
#ifdef KTRACE
|
||||
if (KTRPOINT(td, KTR_PSIG))
|
||||
|
@ -88,7 +88,7 @@ static int realtimer_gettime(struct itimer *, struct itimerspec *);
|
||||
static int realtimer_settime(struct itimer *, int,
|
||||
struct itimerspec *, struct itimerspec *);
|
||||
static int realtimer_delete(struct itimer *);
|
||||
static void realtimer_clocktime(clockid_t, struct timeval *);
|
||||
static void realtimer_clocktime(clockid_t, struct timespec *);
|
||||
static void realtimer_expire(void *);
|
||||
static void realtimer_event_hook(struct proc *, clockid_t, int event);
|
||||
static int kern_timer_create(struct thread *, clockid_t,
|
||||
@ -97,6 +97,7 @@ static int kern_timer_delete(struct thread *, timer_t);
|
||||
|
||||
int register_posix_clock(int, struct kclock *);
|
||||
void itimer_fire(struct itimer *it);
|
||||
int itimespecfix(struct timespec *ts);
|
||||
|
||||
#define CLOCK_CALL(clock, call, arglist) \
|
||||
((*posix_clocks[clock].call) arglist)
|
||||
@ -957,9 +958,11 @@ kern_timer_create(struct thread *td, clockid_t clock_id,
|
||||
|
||||
if (evp != NULL) {
|
||||
if (evp->sigev_notify != SIGEV_NONE &&
|
||||
evp->sigev_notify != SIGEV_SIGNAL)
|
||||
evp->sigev_notify != SIGEV_SIGNAL &&
|
||||
evp->sigev_notify != SIGEV_THREAD_ID)
|
||||
return (EINVAL);
|
||||
if (evp->sigev_notify == SIGEV_SIGNAL &&
|
||||
if ((evp->sigev_notify == SIGEV_SIGNAL ||
|
||||
evp->sigev_notify == SIGEV_THREAD_ID) &&
|
||||
!_SIG_VALID(evp->sigev_signo))
|
||||
return (EINVAL);
|
||||
}
|
||||
@ -971,8 +974,8 @@ kern_timer_create(struct thread *td, clockid_t clock_id,
|
||||
it->it_flags = 0;
|
||||
it->it_usecount = 0;
|
||||
it->it_active = 0;
|
||||
timevalclear(&it->it_time.it_value);
|
||||
timevalclear(&it->it_time.it_interval);
|
||||
timespecclear(&it->it_time.it_value);
|
||||
timespecclear(&it->it_time.it_interval);
|
||||
it->it_overrun = 0;
|
||||
it->it_overrun_last = 0;
|
||||
it->it_clockid = clock_id;
|
||||
@ -1028,7 +1031,8 @@ kern_timer_create(struct thread *td, clockid_t clock_id,
|
||||
it->it_sigev.sigev_value.sigval_int = id;
|
||||
}
|
||||
|
||||
if (it->it_sigev.sigev_notify == SIGEV_SIGNAL) {
|
||||
if (it->it_sigev.sigev_notify == SIGEV_SIGNAL ||
|
||||
it->it_sigev.sigev_notify == SIGEV_THREAD_ID) {
|
||||
it->it_ksi.ksi_signo = it->it_sigev.sigev_signo;
|
||||
it->it_ksi.ksi_code = SI_TIMER;
|
||||
it->it_ksi.ksi_value = it->it_sigev.sigev_value;
|
||||
@ -1204,9 +1208,9 @@ timer_getoverrun(struct thread *td, struct timer_getoverrun_args *uap)
|
||||
PROC_UNLOCK(p);
|
||||
error = EINVAL;
|
||||
} else {
|
||||
PROC_UNLOCK(p);
|
||||
td->td_retval[0] = it->it_overrun_last;
|
||||
ITIMER_UNLOCK(it);
|
||||
PROC_UNLOCK(p);
|
||||
error = 0;
|
||||
}
|
||||
return (error);
|
||||
@ -1230,18 +1234,14 @@ realtimer_delete(struct itimer *it)
|
||||
static int
|
||||
realtimer_gettime(struct itimer *it, struct itimerspec *ovalue)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timespec cts;
|
||||
|
||||
mtx_assert(&it->it_mtx, MA_OWNED);
|
||||
|
||||
TIMEVAL_TO_TIMESPEC(&it->it_time.it_value, &ovalue->it_value);
|
||||
TIMEVAL_TO_TIMESPEC(&it->it_time.it_interval, &ovalue->it_interval);
|
||||
if (it->it_clockid == CLOCK_REALTIME)
|
||||
getnanotime(&ts);
|
||||
else /* CLOCK_MONOTONIC */
|
||||
getnanouptime(&ts);
|
||||
realtimer_clocktime(it->it_clockid, &cts);
|
||||
*ovalue = it->it_time;
|
||||
if (ovalue->it_value.tv_sec != 0 || ovalue->it_value.tv_nsec != 0) {
|
||||
timespecsub(&ovalue->it_value, &ts);
|
||||
timespecsub(&ovalue->it_value, &cts);
|
||||
if (ovalue->it_value.tv_sec < 0 ||
|
||||
(ovalue->it_value.tv_sec == 0 &&
|
||||
ovalue->it_value.tv_nsec == 0)) {
|
||||
@ -1256,42 +1256,42 @@ static int
|
||||
realtimer_settime(struct itimer *it, int flags,
|
||||
struct itimerspec *value, struct itimerspec *ovalue)
|
||||
{
|
||||
struct timeval tv, tv2;
|
||||
struct itimerval val;
|
||||
struct timespec cts, ts;
|
||||
struct timeval tv;
|
||||
struct itimerspec val;
|
||||
|
||||
mtx_assert(&it->it_mtx, MA_OWNED);
|
||||
|
||||
TIMESPEC_TO_TIMEVAL(&val.it_value, &value->it_value);
|
||||
TIMESPEC_TO_TIMEVAL(&val.it_interval, &value->it_interval);
|
||||
|
||||
if (itimerfix(&val.it_value))
|
||||
val = *value;
|
||||
if (itimespecfix(&val.it_value))
|
||||
return (EINVAL);
|
||||
|
||||
if (timevalisset(&val.it_value)) {
|
||||
if (itimerfix(&val.it_interval))
|
||||
if (timespecisset(&val.it_value)) {
|
||||
if (itimespecfix(&val.it_interval))
|
||||
return (EINVAL);
|
||||
} else {
|
||||
timevalclear(&val.it_interval);
|
||||
timespecclear(&val.it_interval);
|
||||
}
|
||||
|
||||
if (ovalue != NULL)
|
||||
realtimer_gettime(it, ovalue);
|
||||
|
||||
it->it_time = val;
|
||||
if (timevalisset(&val.it_value)) {
|
||||
realtimer_clocktime(it->it_clockid, &tv);
|
||||
tv2 = val.it_value;
|
||||
if (timespecisset(&val.it_value)) {
|
||||
realtimer_clocktime(it->it_clockid, &cts);
|
||||
ts = val.it_value;
|
||||
if ((flags & TIMER_ABSTIME) == 0) {
|
||||
/* Convert to absolute time. */
|
||||
timevaladd(&it->it_time.it_value, &tv);
|
||||
timespecadd(&it->it_time.it_value, &cts);
|
||||
} else {
|
||||
timevalsub(&tv2, &tv);
|
||||
timespecsub(&ts, &cts);
|
||||
/*
|
||||
* We don't care if tv2 is negative, tztohz will
|
||||
* We don't care if ts is negative, tztohz will
|
||||
* fix it.
|
||||
*/
|
||||
}
|
||||
callout_reset(&it->it_callout, tvtohz(&tv2),
|
||||
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
||||
callout_reset(&it->it_callout, tvtohz(&tv),
|
||||
realtimer_expire, it);
|
||||
} else {
|
||||
callout_stop(&it->it_callout);
|
||||
@ -1301,12 +1301,40 @@ realtimer_settime(struct itimer *it, int flags,
|
||||
}
|
||||
|
||||
static void
|
||||
realtimer_clocktime(clockid_t id, struct timeval *tv)
|
||||
realtimer_clocktime(clockid_t id, struct timespec *ts)
|
||||
{
|
||||
if (id == CLOCK_REALTIME)
|
||||
getmicrotime(tv);
|
||||
getnanotime(ts);
|
||||
else /* CLOCK_MONOTONIC */
|
||||
getmicrouptime(tv);
|
||||
getnanouptime(ts);
|
||||
}
|
||||
|
||||
int
|
||||
itimer_accept(struct proc *p, timer_t timerid, ksiginfo_t *ksi)
|
||||
{
|
||||
struct itimer *it;
|
||||
|
||||
PROC_LOCK_ASSERT(p, MA_OWNED);
|
||||
it = itimer_find(p, timerid, 0);
|
||||
if (it != NULL) {
|
||||
ksi->ksi_overrun = it->it_overrun;
|
||||
it->it_overrun_last = it->it_overrun;
|
||||
it->it_overrun = 0;
|
||||
ITIMER_UNLOCK(it);
|
||||
return (0);
|
||||
}
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
int
|
||||
itimespecfix(struct timespec *ts)
|
||||
{
|
||||
|
||||
if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000)
|
||||
return (EINVAL);
|
||||
if (ts->tv_sec == 0 && ts->tv_nsec != 0 && ts->tv_nsec < tick * 1000)
|
||||
ts->tv_nsec = tick * 1000;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1339,41 +1367,44 @@ realtimer_event_hook(struct proc *p, clockid_t clock_id, int event)
|
||||
static void
|
||||
realtimer_expire(void *arg)
|
||||
{
|
||||
struct timeval tv, tv2;
|
||||
struct timespec cts, ts;
|
||||
struct timeval tv;
|
||||
struct itimer *it;
|
||||
struct proc *p;
|
||||
|
||||
it = (struct itimer *)arg;
|
||||
p = it->it_proc;
|
||||
|
||||
realtimer_clocktime(it->it_clockid, &tv);
|
||||
realtimer_clocktime(it->it_clockid, &cts);
|
||||
/* Only fire if time is reached. */
|
||||
if (timevalcmp(&it->it_time.it_value, &tv, <=)) {
|
||||
if (timevalisset(&it->it_time.it_interval)) {
|
||||
timevaladd(&it->it_time.it_value,
|
||||
if (timespeccmp(&cts, &it->it_time.it_value, >=)) {
|
||||
if (timespecisset(&it->it_time.it_interval)) {
|
||||
timespecadd(&it->it_time.it_value,
|
||||
&it->it_time.it_interval);
|
||||
while (timevalcmp(&it->it_time.it_value, &tv, <=)) {
|
||||
while (timespeccmp(&cts, &it->it_time.it_value, >=)) {
|
||||
it->it_overrun++;
|
||||
timevaladd(&it->it_time.it_value,
|
||||
timespecadd(&it->it_time.it_value,
|
||||
&it->it_time.it_interval);
|
||||
}
|
||||
} else {
|
||||
/* single shot timer ? */
|
||||
timevalclear(&it->it_time.it_value);
|
||||
timespecclear(&it->it_time.it_value);
|
||||
}
|
||||
if (timevalisset(&it->it_time.it_value)) {
|
||||
tv2 = it->it_time.it_value;
|
||||
timevalsub(&tv2, &tv);
|
||||
callout_reset(&it->it_callout, tvtohz(&tv2),
|
||||
if (timespecisset(&it->it_time.it_value)) {
|
||||
ts = it->it_time.it_value;
|
||||
timespecsub(&ts, &cts);
|
||||
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
||||
callout_reset(&it->it_callout, tvtohz(&tv),
|
||||
realtimer_expire, it);
|
||||
}
|
||||
ITIMER_UNLOCK(it);
|
||||
itimer_fire(it);
|
||||
ITIMER_LOCK(it);
|
||||
} else if (timevalisset(&it->it_time.it_value)) {
|
||||
tv2 = it->it_time.it_value;
|
||||
timevalsub(&tv2, &tv);
|
||||
callout_reset(&it->it_callout, tvtohz(&tv2), realtimer_expire,
|
||||
} else if (timespecisset(&it->it_time.it_value)) {
|
||||
ts = it->it_time.it_value;
|
||||
timespecsub(&ts, &cts);
|
||||
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
||||
callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire,
|
||||
it);
|
||||
}
|
||||
}
|
||||
@ -1382,20 +1413,48 @@ void
|
||||
itimer_fire(struct itimer *it)
|
||||
{
|
||||
struct proc *p = it->it_proc;
|
||||
struct thread *td;
|
||||
|
||||
if (it->it_sigev.sigev_notify == SIGEV_SIGNAL) {
|
||||
if (it->it_sigev.sigev_notify == SIGEV_SIGNAL ||
|
||||
it->it_sigev.sigev_notify == SIGEV_THREAD_ID) {
|
||||
PROC_LOCK(p);
|
||||
ITIMER_LOCK(it);
|
||||
if (KSI_ONQ(&it->it_ksi)) {
|
||||
it->it_overrun++;
|
||||
} else {
|
||||
it->it_ksi.ksi_overrun = it->it_overrun;
|
||||
it->it_overrun_last = it->it_overrun;
|
||||
it->it_overrun = 0;
|
||||
if (it->it_sigev.sigev_notify == SIGEV_THREAD_ID) {
|
||||
/* XXX
|
||||
* This is too slow if there are many threads,
|
||||
* why the world don't have a thread hash table,
|
||||
* sigh.
|
||||
*/
|
||||
FOREACH_THREAD_IN_PROC(p, td) {
|
||||
if (td->td_tid ==
|
||||
it->it_sigev.sigev_notify_thread_id)
|
||||
break;
|
||||
}
|
||||
if (td != NULL)
|
||||
tdsignal(td, it->it_ksi.ksi_signo,
|
||||
&it->it_ksi, SIGTARGET_TD);
|
||||
else {
|
||||
/*
|
||||
* Broken userland code, thread went
|
||||
* away, disarm the timer.
|
||||
*/
|
||||
#if 0
|
||||
it->it_overrun++;
|
||||
#else
|
||||
ITIMER_LOCK(it);
|
||||
timespecclear(&it->it_time.it_value);
|
||||
timespecclear(&it->it_time.it_interval);
|
||||
callout_stop(&it->it_callout);
|
||||
ITIMER_UNLOCK(it);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
psignal_info(p, &it->it_ksi);
|
||||
}
|
||||
}
|
||||
PROC_UNLOCK(p);
|
||||
ITIMER_UNLOCK(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
||||
struct itimer {
|
||||
struct mtx it_mtx;
|
||||
struct sigevent it_sigev;
|
||||
struct itimerval it_time;
|
||||
struct itimerspec it_time;
|
||||
struct proc *it_proc;
|
||||
int it_flags;
|
||||
int it_usecount;
|
||||
@ -113,6 +113,6 @@ struct kclock {
|
||||
#define ITIMER_EV_EXIT 1
|
||||
|
||||
void itimers_event_hook(struct proc *p, int event);
|
||||
|
||||
int itimer_accept(struct proc *p, timer_t tid, ksiginfo_t *ksi);
|
||||
#endif
|
||||
#endif /* !_SYS_TIMERS_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user