The kqueue_register() function assumes that it is called from the top of

the syscall code and acquires various event subsystem locks as needed.
The handling of the NOTE_TRACK for EVFILT_PROC is currently done by
calling the kqueue_register() from filt_proc() filter, causing recursive
entrance of the kqueue code. This results in the LORs and recursive
acquisition of the locks.

Implement the variant of the knote() function designed to only handle
the fork() event. It mostly copies the knote() body, but also handles
the NOTE_TRACK, removing the handling from the filt_proc(), where it
causes problems described above. The function is called from the fork1()
instead of knote().

When encountering NOTE_TRACK knote, it marks the knote as influx
and drops the knlist and kqueue lock. In this context call to
kqueue_register is safe from the problems.

An error from the kqueue_register() is reported to the observer as
NOTE_TRACKERR fflag.

PR:	108201
Reviewed by:	jhb, Pramod Srinivasan <pramod juniper net> (previous version)
Discussed with:	jmg
Tested by:	pho
MFC after:	2 weeks
This commit is contained in:
Konstantin Belousov 2008-07-07 09:30:11 +00:00
parent 0d1532e0ef
commit 7054ee4e38
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=180340
3 changed files with 70 additions and 19 deletions

View File

@ -403,30 +403,82 @@ filt_proc(struct knote *kn, long hint)
return (1);
}
/*
* process forked, and user wants to track the new process,
* so attach a new knote to it, and immediately report an
* event with the parent's pid.
*/
if ((event == NOTE_FORK) && (kn->kn_sfflags & NOTE_TRACK)) {
struct kevent kev;
int error;
return (kn->kn_fflags != 0);
}
/*
* Called when the process forked. It mostly does the same as the
* knote(), activating all knotes registered to be activated when the
* process forked. Additionally, for each knote attached to the
* parent, check whether user wants to track the new process. If so
* attach a new knote to it, and immediately report an event with the
* child's pid.
*/
void
knote_fork(struct knlist *list, int pid)
{
struct kqueue *kq;
struct knote *kn;
struct kevent kev;
int error;
if (list == NULL)
return;
list->kl_lock(list->kl_lockarg);
SLIST_FOREACH(kn, &list->kl_list, kn_selnext) {
if ((kn->kn_status & KN_INFLUX) == KN_INFLUX)
continue;
kq = kn->kn_kq;
KQ_LOCK(kq);
if ((kn->kn_status & KN_INFLUX) == KN_INFLUX) {
KQ_UNLOCK(kq);
continue;
}
/*
* register knote with new process.
* The same as knote(), activate the event.
*/
kev.ident = hint & NOTE_PDATAMASK; /* pid */
if ((kn->kn_sfflags & NOTE_TRACK) == 0) {
kn->kn_status |= KN_HASKQLOCK;
if (kn->kn_fop->f_event(kn, NOTE_FORK | pid))
KNOTE_ACTIVATE(kn, 1);
kn->kn_status &= ~KN_HASKQLOCK;
KQ_UNLOCK(kq);
continue;
}
/*
* The NOTE_TRACK case. In addition to the activation
* of the event, we need to register new event to
* track the child. Drop the locks in preparation for
* the call to kqueue_register().
*/
kn->kn_status |= KN_INFLUX;
KQ_UNLOCK(kq);
list->kl_unlock(list->kl_lockarg);
/*
* Activate existing knote and register a knote with
* new process.
*/
kev.ident = pid;
kev.filter = kn->kn_filter;
kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_FLAG1;
kev.fflags = kn->kn_sfflags;
kev.data = kn->kn_id; /* parent */
kev.udata = kn->kn_kevent.udata; /* preserve udata */
error = kqueue_register(kn->kn_kq, &kev, NULL, 0);
kev.data = kn->kn_id; /* parent */
kev.udata = kn->kn_kevent.udata;/* preserve udata */
error = kqueue_register(kq, &kev, NULL, 0);
if (kn->kn_fop->f_event(kn, NOTE_FORK | pid))
KNOTE_ACTIVATE(kn, 0);
if (error)
kn->kn_fflags |= NOTE_TRACKERR;
KQ_LOCK(kq);
kn->kn_status &= ~KN_INFLUX;
KQ_UNLOCK_FLUX(kq);
list->kl_lock(list->kl_lockarg);
}
return (kn->kn_fflags != 0);
list->kl_unlock(list->kl_lockarg);
}
static int

View File

@ -728,14 +728,12 @@ fork1(td, flags, pages, procp)
*/
PROC_LOCK(p1);
_PRELE(p1);
PROC_UNLOCK(p1);
/*
* Tell any interested parties about the new process.
*/
KNOTE_LOCKED(&p1->p_klist, NOTE_FORK | p2->p_pid);
PROC_UNLOCK(p1);
knote_fork(&p1->p_klist, p2->p_pid);
SDT_PROBE(proc, kernel, , create, p2, p1, flags, 0, 0);
/*

View File

@ -205,6 +205,7 @@ struct proc;
struct knlist;
extern void knote(struct knlist *list, long hint, int islocked);
extern void knote_fork(struct knlist *list, int pid);
extern void knlist_add(struct knlist *knl, struct knote *kn, int islocked);
extern void knlist_remove(struct knlist *knl, struct knote *kn, int islocked);
extern void knlist_remove_inevent(struct knlist *knl, struct knote *kn);