Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_time.c
Show First 20 Lines • Show All 875 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
#ifdef KTRACE | #ifdef KTRACE | ||||
if (KTRPOINT(td, KTR_STRUCT)) | if (KTRPOINT(td, KTR_STRUCT)) | ||||
ktritimerval(oitv); | ktritimerval(oitv); | ||||
#endif | #endif | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | |||||
realitexpire_reset_callout(struct proc *p, sbintime_t *isbtp) | |||||
{ | |||||
sbintime_t prec; | |||||
prec = isbtp == NULL ? tvtosbt(p->p_realtimer.it_interval) : *isbtp; | |||||
callout_reset_sbt(&p->p_itcallout, tvtosbt(p->p_realtimer.it_value), | |||||
prec >> tc_precexp, realitexpire, p, C_ABSOLUTE); | |||||
} | |||||
void | |||||
itimer_proc_continue(struct proc *p) | |||||
{ | |||||
struct timeval ctv; | |||||
struct itimer *it; | |||||
int id; | |||||
PROC_LOCK_ASSERT(p, MA_OWNED); | |||||
if ((p->p_flag2 & P2_ITSTOPPED) != 0) { | |||||
p->p_flag2 &= ~P2_ITSTOPPED; | |||||
microuptime(&ctv); | |||||
if (timevalcmp(&p->p_realtimer.it_value, &ctv, >=)) | |||||
realitexpire(p); | |||||
else | |||||
markj: This will use `it_interval` to set the precision, but suppose it_interval is not set, and the… | |||||
Done Inline ActionsIf it_interval is not set, then P2_ITSTOPPED is not set, and itimer_proc_continue() is not called. I only stop periodic (rescheduling) timers, one-shot timers are left alone for stopped process. And for periodic, I do not cancel scheduled callout, I let it execute and observe the process state, then decide if it needs to reschedule. Cancellation is simply not reliable, esp. while network people do not even consider other users and make incompatible KBI changes without handling all callers. kib: If it_interval is not set, then P2_ITSTOPPED is not set, and itimer_proc_continue() is not… | |||||
realitexpire_reset_callout(p, NULL); | |||||
} | |||||
for (id = 3; id < TIMER_MAX; id++) { | |||||
it = p->p_itimers->its_timers[id]; | |||||
markjUnsubmitted Done Inline ActionsI think we need to verify that p->p_itimers != NULL first. markj: I think we need to verify that `p->p_itimers != NULL` first. | |||||
if (it == NULL) | |||||
continue; | |||||
if ((it->it_flags & ITF_PSTOPPED) != 0) { | |||||
ITIMER_LOCK(it); | |||||
if ((it->it_flags & ITF_PSTOPPED) != 0) { | |||||
it->it_flags &= ~ITF_PSTOPPED; | |||||
if ((it->it_flags & ITF_DELETING) == 0) | |||||
realtimer_expire(it); | |||||
} | |||||
ITIMER_UNLOCK(it); | |||||
} | |||||
} | |||||
} | |||||
/* | /* | ||||
* Real interval timer expired: | * Real interval timer expired: | ||||
* send process whose timer expired an alarm signal. | * send process whose timer expired an alarm signal. | ||||
* If time is not set up to reload, then just return. | * If time is not set up to reload, then just return. | ||||
* Else compute next time timer should go off which is > current time. | * Else compute next time timer should go off which is > current time. | ||||
* This is where delay in processing this timeout causes multiple | * This is where delay in processing this timeout causes multiple | ||||
* SIGALRM calls to be compressed into one. | * SIGALRM calls to be compressed into one. | ||||
* tvtohz() always adds 1 to allow for the time until the next clock | * tvtohz() always adds 1 to allow for the time until the next clock | ||||
Show All 11 Lines | realitexpire(void *arg) | ||||
p = (struct proc *)arg; | p = (struct proc *)arg; | ||||
kern_psignal(p, SIGALRM); | kern_psignal(p, SIGALRM); | ||||
if (!timevalisset(&p->p_realtimer.it_interval)) { | if (!timevalisset(&p->p_realtimer.it_interval)) { | ||||
timevalclear(&p->p_realtimer.it_value); | timevalclear(&p->p_realtimer.it_value); | ||||
if (p->p_flag & P_WEXIT) | if (p->p_flag & P_WEXIT) | ||||
wakeup(&p->p_itcallout); | wakeup(&p->p_itcallout); | ||||
return; | return; | ||||
} | } | ||||
isbt = tvtosbt(p->p_realtimer.it_interval); | isbt = tvtosbt(p->p_realtimer.it_interval); | ||||
if (isbt >= sbt_timethreshold) | if (isbt >= sbt_timethreshold) | ||||
getmicrouptime(&ctv); | getmicrouptime(&ctv); | ||||
else | else | ||||
microuptime(&ctv); | microuptime(&ctv); | ||||
do { | do { | ||||
timevaladd(&p->p_realtimer.it_value, | timevaladd(&p->p_realtimer.it_value, | ||||
&p->p_realtimer.it_interval); | &p->p_realtimer.it_interval); | ||||
} while (timevalcmp(&p->p_realtimer.it_value, &ctv, <=)); | } while (timevalcmp(&p->p_realtimer.it_value, &ctv, <=)); | ||||
callout_reset_sbt(&p->p_itcallout, tvtosbt(p->p_realtimer.it_value), | |||||
isbt >> tc_precexp, realitexpire, p, C_ABSOLUTE); | if (P_SHOULDSTOP(p) || P_KILLED(p)) { | ||||
p->p_flag2 |= P2_ITSTOPPED; | |||||
return; | |||||
} | } | ||||
p->p_flag2 &= ~P2_ITSTOPPED; | |||||
realitexpire_reset_callout(p, &isbt); | |||||
} | |||||
/* | /* | ||||
* Check that a proposed value to load into the .it_value or | * Check that a proposed value to load into the .it_value or | ||||
* .it_interval part of an interval timer is acceptable, and | * .it_interval part of an interval timer is acceptable, and | ||||
* fix it to have at least minimal value (i.e. if it is less | * fix it to have at least minimal value (i.e. if it is less | ||||
* than the resolution of the clock, round it up.) | * than the resolution of the clock, round it up.) | ||||
*/ | */ | ||||
int | int | ||||
itimerfix(struct timeval *tv) | itimerfix(struct timeval *tv) | ||||
▲ Show 20 Lines • Show All 680 Lines • ▼ Show 20 Lines | |||||
/* Timeout callback for realtime timer */ | /* Timeout callback for realtime timer */ | ||||
static void | static void | ||||
realtimer_expire(void *arg) | realtimer_expire(void *arg) | ||||
{ | { | ||||
struct timespec cts, ts; | struct timespec cts, ts; | ||||
struct timeval tv; | struct timeval tv; | ||||
struct itimer *it; | struct itimer *it; | ||||
struct proc *p; | |||||
uint64_t interval, now, overruns, value; | uint64_t interval, now, overruns, value; | ||||
it = (struct itimer *)arg; | it = (struct itimer *)arg; | ||||
realtimer_clocktime(it->it_clockid, &cts); | realtimer_clocktime(it->it_clockid, &cts); | ||||
/* Only fire if time is reached. */ | /* Only fire if time is reached. */ | ||||
if (timespeccmp(&cts, &it->it_time.it_value, >=)) { | if (timespeccmp(&cts, &it->it_time.it_value, >=)) { | ||||
if (timespecisset(&it->it_time.it_interval)) { | if (timespecisset(&it->it_time.it_interval)) { | ||||
Show All 22 Lines | if (timespecisset(&it->it_time.it_interval)) { | ||||
now + interval - (now - value) % interval; | now + interval - (now - value) % interval; | ||||
it->it_time.it_value = timespecfromns(value); | it->it_time.it_value = timespecfromns(value); | ||||
} | } | ||||
} else { | } else { | ||||
/* single shot timer ? */ | /* single shot timer ? */ | ||||
timespecclear(&it->it_time.it_value); | timespecclear(&it->it_time.it_value); | ||||
} | } | ||||
if (timespecisset(&it->it_time.it_value)) { | if (timespecisset(&it->it_time.it_value)) { | ||||
p = it->it_proc; | |||||
if (P_SHOULDSTOP(p) || P_KILLED(p)) { | |||||
it->it_flags |= ITF_PSTOPPED; | |||||
} else { | |||||
timespecsub(&it->it_time.it_value, &cts, &ts); | timespecsub(&it->it_time.it_value, &cts, &ts); | ||||
TIMESPEC_TO_TIMEVAL(&tv, &ts); | TIMESPEC_TO_TIMEVAL(&tv, &ts); | ||||
callout_reset(&it->it_callout, tvtohz(&tv), | callout_reset(&it->it_callout, tvtohz(&tv), | ||||
realtimer_expire, it); | realtimer_expire, it); | ||||
} | } | ||||
} | |||||
itimer_enter(it); | itimer_enter(it); | ||||
ITIMER_UNLOCK(it); | ITIMER_UNLOCK(it); | ||||
itimer_fire(it); | itimer_fire(it); | ||||
ITIMER_LOCK(it); | ITIMER_LOCK(it); | ||||
itimer_leave(it); | itimer_leave(it); | ||||
} else if (timespecisset(&it->it_time.it_value)) { | } else if (timespecisset(&it->it_time.it_value)) { | ||||
p = it->it_proc; | |||||
if (P_SHOULDSTOP(p) || P_KILLED(p)) { | |||||
it->it_flags |= ITF_PSTOPPED; | |||||
} else { | |||||
ts = it->it_time.it_value; | ts = it->it_time.it_value; | ||||
timespecsub(&ts, &cts, &ts); | timespecsub(&ts, &cts, &ts); | ||||
TIMESPEC_TO_TIMEVAL(&tv, &ts); | TIMESPEC_TO_TIMEVAL(&tv, &ts); | ||||
callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, | callout_reset(&it->it_callout, tvtohz(&tv), | ||||
it); | realtimer_expire, it); | ||||
} | |||||
} | } | ||||
} | } | ||||
static void | static 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; | struct thread *td; | ||||
▲ Show 20 Lines • Show All 86 Lines • Show Last 20 Lines |
This will use it_interval to set the precision, but suppose it_interval is not set, and the process was stopped and started again before the timeout expires. Then we will use a zero-valued it_interval to calculate the precision for the callout. Really I think we should not be scheduling a callout at all if one is still pending at this point.