Stop arming kqueue timers on knote owner suspend or terminate
This way, even if the process specified very tight reschedule intervals, it should be stoppable/killable. Reported and reviewed by: markj Tested by: markj, pho Sponsored by: The FreeBSD Foundation MFC after: 2 weeks Differential revision: https://reviews.freebsd.org/D29106
This commit is contained in:
parent
533e5057ed
commit
2fd1ffefaa
@ -524,6 +524,7 @@ proc0_init(void *dummy __unused)
|
||||
callout_init_mtx(&p->p_itcallout, &p->p_mtx, 0);
|
||||
callout_init_mtx(&p->p_limco, &p->p_mtx, 0);
|
||||
callout_init(&td->td_slpcallout, 1);
|
||||
TAILQ_INIT(&p->p_kqtim_stop);
|
||||
|
||||
/* Create credentials. */
|
||||
newcred = crget();
|
||||
|
@ -676,8 +676,10 @@ timer2sbintime(int64_t data, int flags)
|
||||
|
||||
struct kq_timer_cb_data {
|
||||
struct callout c;
|
||||
struct proc *p;
|
||||
struct knote *kn;
|
||||
int cpuid;
|
||||
TAILQ_ENTRY(kq_timer_cb_data) link;
|
||||
sbintime_t next; /* next timer event fires at */
|
||||
sbintime_t to; /* precalculated timer period, 0 for abs */
|
||||
};
|
||||
@ -689,22 +691,65 @@ kqtimer_sched_callout(struct kq_timer_cb_data *kc)
|
||||
kc->cpuid, C_ABSOLUTE);
|
||||
}
|
||||
|
||||
void
|
||||
kqtimer_proc_continue(struct proc *p)
|
||||
{
|
||||
struct kq_timer_cb_data *kc, *kc1;
|
||||
struct bintime bt;
|
||||
sbintime_t now;
|
||||
|
||||
PROC_LOCK_ASSERT(p, MA_OWNED);
|
||||
|
||||
getboottimebin(&bt);
|
||||
now = bttosbt(bt);
|
||||
|
||||
TAILQ_FOREACH_SAFE(kc, &p->p_kqtim_stop, link, kc1) {
|
||||
TAILQ_REMOVE(&p->p_kqtim_stop, kc, link);
|
||||
if (kc->next <= now)
|
||||
filt_timerexpire(kc->kn);
|
||||
else
|
||||
kqtimer_sched_callout(kc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
filt_timerexpire(void *knx)
|
||||
{
|
||||
struct knote *kn;
|
||||
struct kq_timer_cb_data *kc;
|
||||
struct proc *p;
|
||||
sbintime_t now;
|
||||
|
||||
kn = knx;
|
||||
kn->kn_data++;
|
||||
kc = kn->kn_ptr.p_v;
|
||||
|
||||
if ((kn->kn_flags & EV_ONESHOT) != 0 || kc->to == 0) {
|
||||
kn->kn_data++;
|
||||
KNOTE_ACTIVATE(kn, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (now = sbinuptime(); kc->next <= now; kc->next += kc->to)
|
||||
kn->kn_data++;
|
||||
KNOTE_ACTIVATE(kn, 0); /* XXX - handle locking */
|
||||
|
||||
if ((kn->kn_flags & EV_ONESHOT) != 0)
|
||||
return;
|
||||
kc = kn->kn_ptr.p_v;
|
||||
if (kc->to == 0)
|
||||
return;
|
||||
kc->next += kc->to;
|
||||
/*
|
||||
* Initial check for stopped kc->p is racy. It is fine to
|
||||
* miss the set of the stop flags, at worst we would schedule
|
||||
* one more callout. On the other hand, it is not fine to not
|
||||
* schedule when we we missed clearing of the flags, we
|
||||
* recheck them under the lock and observe consistent state.
|
||||
*/
|
||||
p = kc->p;
|
||||
if (P_SHOULDSTOP(p) || P_KILLED(p)) {
|
||||
PROC_LOCK(p);
|
||||
if (P_SHOULDSTOP(p) || P_KILLED(p)) {
|
||||
TAILQ_INSERT_TAIL(&p->p_kqtim_stop, kc, link);
|
||||
PROC_UNLOCK(p);
|
||||
return;
|
||||
}
|
||||
PROC_UNLOCK(p);
|
||||
}
|
||||
kqtimer_sched_callout(kc);
|
||||
}
|
||||
|
||||
@ -762,6 +807,7 @@ filt_timerattach(struct knote *kn)
|
||||
kn->kn_status &= ~KN_DETACHED; /* knlist_add clears it */
|
||||
kn->kn_ptr.p_v = kc = malloc(sizeof(*kc), M_KQUEUE, M_WAITOK);
|
||||
kc->kn = kn;
|
||||
kc->p = curproc;
|
||||
kc->cpuid = PCPU_GET(cpuid);
|
||||
callout_init(&kc->c, 1);
|
||||
filt_timerstart(kn, to);
|
||||
|
@ -606,6 +606,7 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
|
||||
LIST_INIT(&p2->p_orphans);
|
||||
|
||||
callout_init_mtx(&p2->p_itcallout, &p2->p_mtx, 0);
|
||||
TAILQ_INIT(&p2->p_kqtim_stop);
|
||||
|
||||
/*
|
||||
* This begins the section where we must prevent the parent
|
||||
|
@ -2424,6 +2424,7 @@ runfast:
|
||||
PROC_SUNLOCK(p);
|
||||
out_cont:
|
||||
itimer_proc_continue(p);
|
||||
kqtimer_proc_continue(p);
|
||||
out:
|
||||
/* If we jump here, proc slock should not be owned. */
|
||||
PROC_SLOCK_ASSERT(p, MA_NOTOWNED);
|
||||
|
@ -1095,6 +1095,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
|
||||
thread_unsuspend(p);
|
||||
PROC_SUNLOCK(p);
|
||||
itimer_proc_continue(p);
|
||||
kqtimer_proc_continue(p);
|
||||
break;
|
||||
|
||||
case PT_WRITE_I:
|
||||
|
@ -183,6 +183,7 @@ struct kaudit_record;
|
||||
struct kcov_info;
|
||||
struct kdtrace_proc;
|
||||
struct kdtrace_thread;
|
||||
struct kq_timer_cb_data;
|
||||
struct mqueue_notifier;
|
||||
struct p_sched;
|
||||
struct proc;
|
||||
@ -727,6 +728,8 @@ struct proc {
|
||||
*/
|
||||
LIST_ENTRY(proc) p_orphan; /* (e) List of orphan processes. */
|
||||
LIST_HEAD(, proc) p_orphans; /* (e) Pointer to list of orphans. */
|
||||
|
||||
TAILQ_HEAD(, kq_timer_cb_data) p_kqtim_stop; /* (c) */
|
||||
};
|
||||
|
||||
#define p_session p_pgrp->pg_session
|
||||
@ -1093,6 +1096,7 @@ void fork_exit(void (*)(void *, struct trapframe *), void *,
|
||||
void fork_return(struct thread *, struct trapframe *);
|
||||
int inferior(struct proc *p);
|
||||
void itimer_proc_continue(struct proc *p);
|
||||
void kqtimer_proc_continue(struct proc *p);
|
||||
void kern_proc_vmmap_resident(struct vm_map *map, struct vm_map_entry *entry,
|
||||
int *resident_count, bool *super);
|
||||
void kern_yield(int);
|
||||
|
Loading…
x
Reference in New Issue
Block a user