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/sysent.h>
|
||||||
#include <sys/syslog.h>
|
#include <sys/syslog.h>
|
||||||
#include <sys/sysproto.h>
|
#include <sys/sysproto.h>
|
||||||
|
#include <sys/timers.h>
|
||||||
#include <sys/unistd.h>
|
#include <sys/unistd.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <vm/vm.h>
|
#include <vm/vm.h>
|
||||||
@ -1252,6 +1253,8 @@ kern_sigtimedwait(struct thread *td, sigset_t waitset, ksiginfo_t *ksi,
|
|||||||
ksiginfo_init(ksi);
|
ksiginfo_init(ksi);
|
||||||
sigqueue_get(&td->td_sigqueue, sig, ksi);
|
sigqueue_get(&td->td_sigqueue, sig, ksi);
|
||||||
ksi->ksi_signo = sig;
|
ksi->ksi_signo = sig;
|
||||||
|
if (ksi->ksi_code == SI_TIMER)
|
||||||
|
itimer_accept(p, ksi->ksi_timerid, ksi);
|
||||||
error = 0;
|
error = 0;
|
||||||
mtx_lock(&ps->ps_mtx);
|
mtx_lock(&ps->ps_mtx);
|
||||||
action = ps->ps_sigact[_SIG_IDX(sig)];
|
action = ps->ps_sigact[_SIG_IDX(sig)];
|
||||||
@ -2670,7 +2673,8 @@ postsig(sig)
|
|||||||
ksiginfo_init(&ksi);
|
ksiginfo_init(&ksi);
|
||||||
sigqueue_get(&td->td_sigqueue, sig, &ksi);
|
sigqueue_get(&td->td_sigqueue, sig, &ksi);
|
||||||
ksi.ksi_signo = sig;
|
ksi.ksi_signo = sig;
|
||||||
|
if (ksi.ksi_code == SI_TIMER)
|
||||||
|
itimer_accept(p, ksi.ksi_timerid, &ksi);
|
||||||
action = ps->ps_sigact[_SIG_IDX(sig)];
|
action = ps->ps_sigact[_SIG_IDX(sig)];
|
||||||
#ifdef KTRACE
|
#ifdef KTRACE
|
||||||
if (KTRPOINT(td, KTR_PSIG))
|
if (KTRPOINT(td, KTR_PSIG))
|
||||||
|
@ -88,7 +88,7 @@ static int realtimer_gettime(struct itimer *, struct itimerspec *);
|
|||||||
static int realtimer_settime(struct itimer *, int,
|
static int realtimer_settime(struct itimer *, int,
|
||||||
struct itimerspec *, struct itimerspec *);
|
struct itimerspec *, struct itimerspec *);
|
||||||
static int realtimer_delete(struct itimer *);
|
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_expire(void *);
|
||||||
static void realtimer_event_hook(struct proc *, clockid_t, int event);
|
static void realtimer_event_hook(struct proc *, clockid_t, int event);
|
||||||
static int kern_timer_create(struct thread *, clockid_t,
|
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 *);
|
int register_posix_clock(int, struct kclock *);
|
||||||
void itimer_fire(struct itimer *it);
|
void itimer_fire(struct itimer *it);
|
||||||
|
int itimespecfix(struct timespec *ts);
|
||||||
|
|
||||||
#define CLOCK_CALL(clock, call, arglist) \
|
#define CLOCK_CALL(clock, call, arglist) \
|
||||||
((*posix_clocks[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 != NULL) {
|
||||||
if (evp->sigev_notify != SIGEV_NONE &&
|
if (evp->sigev_notify != SIGEV_NONE &&
|
||||||
evp->sigev_notify != SIGEV_SIGNAL)
|
evp->sigev_notify != SIGEV_SIGNAL &&
|
||||||
|
evp->sigev_notify != SIGEV_THREAD_ID)
|
||||||
return (EINVAL);
|
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))
|
!_SIG_VALID(evp->sigev_signo))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
@ -971,8 +974,8 @@ kern_timer_create(struct thread *td, clockid_t clock_id,
|
|||||||
it->it_flags = 0;
|
it->it_flags = 0;
|
||||||
it->it_usecount = 0;
|
it->it_usecount = 0;
|
||||||
it->it_active = 0;
|
it->it_active = 0;
|
||||||
timevalclear(&it->it_time.it_value);
|
timespecclear(&it->it_time.it_value);
|
||||||
timevalclear(&it->it_time.it_interval);
|
timespecclear(&it->it_time.it_interval);
|
||||||
it->it_overrun = 0;
|
it->it_overrun = 0;
|
||||||
it->it_overrun_last = 0;
|
it->it_overrun_last = 0;
|
||||||
it->it_clockid = clock_id;
|
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;
|
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_signo = it->it_sigev.sigev_signo;
|
||||||
it->it_ksi.ksi_code = SI_TIMER;
|
it->it_ksi.ksi_code = SI_TIMER;
|
||||||
it->it_ksi.ksi_value = it->it_sigev.sigev_value;
|
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);
|
PROC_UNLOCK(p);
|
||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
} else {
|
} else {
|
||||||
PROC_UNLOCK(p);
|
|
||||||
td->td_retval[0] = it->it_overrun_last;
|
td->td_retval[0] = it->it_overrun_last;
|
||||||
ITIMER_UNLOCK(it);
|
ITIMER_UNLOCK(it);
|
||||||
|
PROC_UNLOCK(p);
|
||||||
error = 0;
|
error = 0;
|
||||||
}
|
}
|
||||||
return (error);
|
return (error);
|
||||||
@ -1230,18 +1234,14 @@ realtimer_delete(struct itimer *it)
|
|||||||
static int
|
static int
|
||||||
realtimer_gettime(struct itimer *it, struct itimerspec *ovalue)
|
realtimer_gettime(struct itimer *it, struct itimerspec *ovalue)
|
||||||
{
|
{
|
||||||
struct timespec ts;
|
struct timespec cts;
|
||||||
|
|
||||||
mtx_assert(&it->it_mtx, MA_OWNED);
|
mtx_assert(&it->it_mtx, MA_OWNED);
|
||||||
|
|
||||||
TIMEVAL_TO_TIMESPEC(&it->it_time.it_value, &ovalue->it_value);
|
realtimer_clocktime(it->it_clockid, &cts);
|
||||||
TIMEVAL_TO_TIMESPEC(&it->it_time.it_interval, &ovalue->it_interval);
|
*ovalue = it->it_time;
|
||||||
if (it->it_clockid == CLOCK_REALTIME)
|
|
||||||
getnanotime(&ts);
|
|
||||||
else /* CLOCK_MONOTONIC */
|
|
||||||
getnanouptime(&ts);
|
|
||||||
if (ovalue->it_value.tv_sec != 0 || ovalue->it_value.tv_nsec != 0) {
|
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 ||
|
if (ovalue->it_value.tv_sec < 0 ||
|
||||||
(ovalue->it_value.tv_sec == 0 &&
|
(ovalue->it_value.tv_sec == 0 &&
|
||||||
ovalue->it_value.tv_nsec == 0)) {
|
ovalue->it_value.tv_nsec == 0)) {
|
||||||
@ -1256,42 +1256,42 @@ static int
|
|||||||
realtimer_settime(struct itimer *it, int flags,
|
realtimer_settime(struct itimer *it, int flags,
|
||||||
struct itimerspec *value, struct itimerspec *ovalue)
|
struct itimerspec *value, struct itimerspec *ovalue)
|
||||||
{
|
{
|
||||||
struct timeval tv, tv2;
|
struct timespec cts, ts;
|
||||||
struct itimerval val;
|
struct timeval tv;
|
||||||
|
struct itimerspec val;
|
||||||
|
|
||||||
mtx_assert(&it->it_mtx, MA_OWNED);
|
mtx_assert(&it->it_mtx, MA_OWNED);
|
||||||
|
|
||||||
TIMESPEC_TO_TIMEVAL(&val.it_value, &value->it_value);
|
val = *value;
|
||||||
TIMESPEC_TO_TIMEVAL(&val.it_interval, &value->it_interval);
|
if (itimespecfix(&val.it_value))
|
||||||
|
|
||||||
if (itimerfix(&val.it_value))
|
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
if (timevalisset(&val.it_value)) {
|
if (timespecisset(&val.it_value)) {
|
||||||
if (itimerfix(&val.it_interval))
|
if (itimespecfix(&val.it_interval))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
} else {
|
} else {
|
||||||
timevalclear(&val.it_interval);
|
timespecclear(&val.it_interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ovalue != NULL)
|
if (ovalue != NULL)
|
||||||
realtimer_gettime(it, ovalue);
|
realtimer_gettime(it, ovalue);
|
||||||
|
|
||||||
it->it_time = val;
|
it->it_time = val;
|
||||||
if (timevalisset(&val.it_value)) {
|
if (timespecisset(&val.it_value)) {
|
||||||
realtimer_clocktime(it->it_clockid, &tv);
|
realtimer_clocktime(it->it_clockid, &cts);
|
||||||
tv2 = val.it_value;
|
ts = val.it_value;
|
||||||
if ((flags & TIMER_ABSTIME) == 0) {
|
if ((flags & TIMER_ABSTIME) == 0) {
|
||||||
/* Convert to absolute time. */
|
/* Convert to absolute time. */
|
||||||
timevaladd(&it->it_time.it_value, &tv);
|
timespecadd(&it->it_time.it_value, &cts);
|
||||||
} else {
|
} 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.
|
* fix it.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
callout_reset(&it->it_callout, tvtohz(&tv2),
|
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
||||||
|
callout_reset(&it->it_callout, tvtohz(&tv),
|
||||||
realtimer_expire, it);
|
realtimer_expire, it);
|
||||||
} else {
|
} else {
|
||||||
callout_stop(&it->it_callout);
|
callout_stop(&it->it_callout);
|
||||||
@ -1301,12 +1301,40 @@ realtimer_settime(struct itimer *it, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
realtimer_clocktime(clockid_t id, struct timeval *tv)
|
realtimer_clocktime(clockid_t id, struct timespec *ts)
|
||||||
{
|
{
|
||||||
if (id == CLOCK_REALTIME)
|
if (id == CLOCK_REALTIME)
|
||||||
getmicrotime(tv);
|
getnanotime(ts);
|
||||||
else /* CLOCK_MONOTONIC */
|
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
|
static void
|
||||||
@ -1339,41 +1367,44 @@ realtimer_event_hook(struct proc *p, clockid_t clock_id, int event)
|
|||||||
static void
|
static void
|
||||||
realtimer_expire(void *arg)
|
realtimer_expire(void *arg)
|
||||||
{
|
{
|
||||||
struct timeval tv, tv2;
|
struct timespec cts, ts;
|
||||||
|
struct timeval tv;
|
||||||
struct itimer *it;
|
struct itimer *it;
|
||||||
struct proc *p;
|
struct proc *p;
|
||||||
|
|
||||||
it = (struct itimer *)arg;
|
it = (struct itimer *)arg;
|
||||||
p = it->it_proc;
|
p = it->it_proc;
|
||||||
|
|
||||||
realtimer_clocktime(it->it_clockid, &tv);
|
realtimer_clocktime(it->it_clockid, &cts);
|
||||||
/* Only fire if time is reached. */
|
/* Only fire if time is reached. */
|
||||||
if (timevalcmp(&it->it_time.it_value, &tv, <=)) {
|
if (timespeccmp(&cts, &it->it_time.it_value, >=)) {
|
||||||
if (timevalisset(&it->it_time.it_interval)) {
|
if (timespecisset(&it->it_time.it_interval)) {
|
||||||
timevaladd(&it->it_time.it_value,
|
timespecadd(&it->it_time.it_value,
|
||||||
&it->it_time.it_interval);
|
&it->it_time.it_interval);
|
||||||
while (timevalcmp(&it->it_time.it_value, &tv, <=)) {
|
while (timespeccmp(&cts, &it->it_time.it_value, >=)) {
|
||||||
it->it_overrun++;
|
it->it_overrun++;
|
||||||
timevaladd(&it->it_time.it_value,
|
timespecadd(&it->it_time.it_value,
|
||||||
&it->it_time.it_interval);
|
&it->it_time.it_interval);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* single shot timer ? */
|
/* single shot timer ? */
|
||||||
timevalclear(&it->it_time.it_value);
|
timespecclear(&it->it_time.it_value);
|
||||||
}
|
}
|
||||||
if (timevalisset(&it->it_time.it_value)) {
|
if (timespecisset(&it->it_time.it_value)) {
|
||||||
tv2 = it->it_time.it_value;
|
ts = it->it_time.it_value;
|
||||||
timevalsub(&tv2, &tv);
|
timespecsub(&ts, &cts);
|
||||||
callout_reset(&it->it_callout, tvtohz(&tv2),
|
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
||||||
|
callout_reset(&it->it_callout, tvtohz(&tv),
|
||||||
realtimer_expire, it);
|
realtimer_expire, it);
|
||||||
}
|
}
|
||||||
ITIMER_UNLOCK(it);
|
ITIMER_UNLOCK(it);
|
||||||
itimer_fire(it);
|
itimer_fire(it);
|
||||||
ITIMER_LOCK(it);
|
ITIMER_LOCK(it);
|
||||||
} else if (timevalisset(&it->it_time.it_value)) {
|
} else if (timespecisset(&it->it_time.it_value)) {
|
||||||
tv2 = it->it_time.it_value;
|
ts = it->it_time.it_value;
|
||||||
timevalsub(&tv2, &tv);
|
timespecsub(&ts, &cts);
|
||||||
callout_reset(&it->it_callout, tvtohz(&tv2), realtimer_expire,
|
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
||||||
|
callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire,
|
||||||
it);
|
it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1382,20 +1413,48 @@ void
|
|||||||
itimer_fire(struct itimer *it)
|
itimer_fire(struct itimer *it)
|
||||||
{
|
{
|
||||||
struct proc *p = it->it_proc;
|
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);
|
PROC_LOCK(p);
|
||||||
ITIMER_LOCK(it);
|
|
||||||
if (KSI_ONQ(&it->it_ksi)) {
|
if (KSI_ONQ(&it->it_ksi)) {
|
||||||
it->it_overrun++;
|
it->it_overrun++;
|
||||||
} else {
|
} else {
|
||||||
it->it_ksi.ksi_overrun = it->it_overrun;
|
if (it->it_sigev.sigev_notify == SIGEV_THREAD_ID) {
|
||||||
it->it_overrun_last = it->it_overrun;
|
/* XXX
|
||||||
it->it_overrun = 0;
|
* This is too slow if there are many threads,
|
||||||
psignal_info(p, &it->it_ksi);
|
* 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);
|
PROC_UNLOCK(p);
|
||||||
ITIMER_UNLOCK(it);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
struct itimer {
|
struct itimer {
|
||||||
struct mtx it_mtx;
|
struct mtx it_mtx;
|
||||||
struct sigevent it_sigev;
|
struct sigevent it_sigev;
|
||||||
struct itimerval it_time;
|
struct itimerspec it_time;
|
||||||
struct proc *it_proc;
|
struct proc *it_proc;
|
||||||
int it_flags;
|
int it_flags;
|
||||||
int it_usecount;
|
int it_usecount;
|
||||||
@ -113,6 +113,6 @@ struct kclock {
|
|||||||
#define ITIMER_EV_EXIT 1
|
#define ITIMER_EV_EXIT 1
|
||||||
|
|
||||||
void itimers_event_hook(struct proc *p, int event);
|
void itimers_event_hook(struct proc *p, int event);
|
||||||
|
int itimer_accept(struct proc *p, timer_t tid, ksiginfo_t *ksi);
|
||||||
#endif
|
#endif
|
||||||
#endif /* !_SYS_TIMERS_H_ */
|
#endif /* !_SYS_TIMERS_H_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user