Implement kqueue(2) for procdesc(4).

kqueue(2) already supports EVFILT_PROC. Add an EVFILT_PROCDESC that
behaves the same, but operates on a procdesc(4) instead. Only implement
NOTE_EXIT for now. The nice thing about NOTE_EXIT is that it also
returns the exit status of the process, meaning that we can now obtain
this value, even if pdwait4(2) is still unimplemented.

Notes:

- Simply reuse EVFILT_NETDEV for EVFILT_PROCDESC. As both of these will
  be used on totally different descriptor types, this should not clash.

- Let procdesc_kqops_event() reuse the same structure as filt_proc().
  The only difference is that procdesc_kqops_event() should also be able
  to deal with the case where the process was already terminated after
  registration. Simply test this when hint == 0.

- Fix some style(9) issues in filt_proc() to keep it consistent with the
  newly added procdesc_kqops_event().

- Save the exit status of the process in pd->pd_xstat, as we cannot pick
  up the proctree_lock from within procdesc_kqops_event().

Discussed on:	arch@
Reviewed by:	kib@
This commit is contained in:
Ed Schouten 2014-04-07 18:10:49 +00:00
parent 29aa8ff1dd
commit 38219d6acd
6 changed files with 102 additions and 17 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd November 4, 2013
.Dd April 7, 2014
.Dt KQUEUE 2
.Os
.Sh NAME
@ -412,6 +412,24 @@ and the child process will not signal a NOTE_CHILD event.
On return,
.Va fflags
contains the events which triggered the filter.
.It EVFILT_PROCDESC
Takes the process descriptor created by
.Xr pdfork 2
to monitor as the identifier and the events to watch for in
.Va fflags ,
and returns when the associated process performs one or more of the
requested events.
The events to monitor are:
.Bl -tag -width XXNOTE_EXIT
.It NOTE_EXIT
The process has exited.
The exit status will be stored in
.Va data .
.El
.Pp
On return,
.Va fflags
contains the events which triggered the filter.
.It EVFILT_SIGNAL
Takes the signal number to monitor as the identifier and returns
when the given signal is delivered to the process.

View File

@ -32,7 +32,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 4, 2014
.Dd April 7, 2014
.Dt PDFORK 2
.Os
.Sh NAME
@ -117,6 +117,13 @@ and
allow waiting for process state transitions; currently only
.Dv POLLHUP
is defined, and will be raised when the process dies.
Process state transitions can also be monitored using
.Xr kqueue 2
filter
.Dv EVFILT_PROCDESC ;
currently only
.Dv NOTE_EXIT
is implemented.
.Pp
.Xr close 2
will close the process descriptor unless

View File

@ -290,7 +290,7 @@ static struct {
{ &proc_filtops }, /* EVFILT_PROC */
{ &sig_filtops }, /* EVFILT_SIGNAL */
{ &timer_filtops }, /* EVFILT_TIMER */
{ &null_filtops }, /* former EVFILT_NETDEV */
{ &file_filtops }, /* EVFILT_PROCDESC */
{ &fs_filtops }, /* EVFILT_FS */
{ &null_filtops }, /* EVFILT_LIO */
{ &user_filtops }, /* EVFILT_USER */
@ -417,27 +417,22 @@ filt_procdetach(struct knote *kn)
static int
filt_proc(struct knote *kn, long hint)
{
struct proc *p = kn->kn_ptr.p_proc;
struct proc *p;
u_int event;
/*
* mask off extra data
*/
p = kn->kn_ptr.p_proc;
/* Mask off extra data. */
event = (u_int)hint & NOTE_PCTRLMASK;
/*
* if the user is interested in this event, record it.
*/
/* If the user is interested in this event, record it. */
if (kn->kn_sfflags & event)
kn->kn_fflags |= event;
/*
* process is gone, so flag the event as finished.
*/
/* Process is gone, so flag the event as finished. */
if (event == NOTE_EXIT) {
if (!(kn->kn_status & KN_DETACHED))
knlist_remove_inevent(&p->p_klist, kn);
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
kn->kn_flags |= EV_EOF | EV_ONESHOT;
kn->kn_ptr.p_proc = NULL;
if (kn->kn_fflags & NOTE_EXIT)
kn->kn_data = p->p_xstat;

View File

@ -236,6 +236,7 @@ procdesc_new(struct proc *p, int flags)
if (flags & PD_DAEMON)
pd->pd_flags |= PDF_DAEMON;
PROCDESC_LOCK_INIT(pd);
knlist_init_mtx(&pd->pd_selinfo.si_note, &pd->pd_lock);
/*
* Process descriptors start out with two references: one from their
@ -270,6 +271,7 @@ procdesc_free(struct procdesc *pd)
KASSERT((pd->pd_flags & PDF_CLOSED),
("procdesc_free: !PDF_CLOSED"));
knlist_destroy(&pd->pd_selinfo.si_note);
PROCDESC_LOCK_DESTROY(pd);
uma_zfree(procdesc_zone, pd);
}
@ -296,6 +298,7 @@ procdesc_exit(struct proc *p)
("procdesc_exit: closed && parent not init"));
pd->pd_flags |= PDF_EXITED;
pd->pd_xstat = p->p_xstat;
/*
* If the process descriptor has been closed, then we have nothing
@ -314,6 +317,7 @@ procdesc_exit(struct proc *p)
pd->pd_flags &= ~PDF_SELECTED;
selwakeup(&pd->pd_selinfo);
}
KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_EXIT);
PROCDESC_UNLOCK(pd);
return (0);
}
@ -460,11 +464,71 @@ procdesc_poll(struct file *fp, int events, struct ucred *active_cred,
return (revents);
}
static void
procdesc_kqops_detach(struct knote *kn)
{
struct procdesc *pd;
pd = kn->kn_fp->f_data;
knlist_remove(&pd->pd_selinfo.si_note, kn, 0);
}
static int
procdesc_kqops_event(struct knote *kn, long hint)
{
struct procdesc *pd;
u_int event;
pd = kn->kn_fp->f_data;
if (hint == 0) {
/*
* Initial test after registration. Generate a NOTE_EXIT in
* case the process already terminated before registration.
*/
event = pd->pd_flags & PDF_EXITED ? NOTE_EXIT : 0;
} else {
/* Mask off extra data. */
event = (u_int)hint & NOTE_PCTRLMASK;
}
/* If the user is interested in this event, record it. */
if (kn->kn_sfflags & event)
kn->kn_fflags |= event;
/* Process is gone, so flag the event as finished. */
if (event == NOTE_EXIT) {
kn->kn_flags |= EV_EOF | EV_ONESHOT;
if (kn->kn_fflags & NOTE_EXIT)
kn->kn_data = pd->pd_xstat;
if (kn->kn_fflags == 0)
kn->kn_flags |= EV_DROP;
return (1);
}
return (kn->kn_fflags != 0);
}
static struct filterops procdesc_kqops = {
.f_isfd = 1,
.f_detach = procdesc_kqops_detach,
.f_event = procdesc_kqops_event,
};
static int
procdesc_kqfilter(struct file *fp, struct knote *kn)
{
struct procdesc *pd;
return (EOPNOTSUPP);
pd = fp->f_data;
switch (kn->kn_filter) {
case EVFILT_PROCDESC:
kn->kn_fop = &procdesc_kqops;
kn->kn_flags |= EV_CLEAR;
knlist_add(&pd->pd_selinfo.si_note, kn, 0);
return (0);
default:
return (EINVAL);
}
}
static int

View File

@ -38,7 +38,7 @@
#define EVFILT_PROC (-5) /* attached to struct proc */
#define EVFILT_SIGNAL (-6) /* attached to struct proc */
#define EVFILT_TIMER (-7) /* timers */
/* EVFILT_NETDEV (-8) no longer supported */
#define EVFILT_PROCDESC (-8) /* attached to process descriptors */
#define EVFILT_FS (-9) /* filesystem events */
#define EVFILT_LIO (-10) /* attached to lio requests */
#define EVFILT_USER (-11) /* User events */
@ -120,7 +120,7 @@ struct kevent {
#define NOTE_REVOKE 0x0040 /* vnode access was revoked */
/*
* data/hint flags for EVFILT_PROC, shared with userspace
* data/hint flags for EVFILT_PROC and EVFILT_PROCDESC, shared with userspace
*/
#define NOTE_EXIT 0x80000000 /* process exited */
#define NOTE_FORK 0x40000000 /* process forked */

View File

@ -68,6 +68,7 @@ struct procdesc {
* In-flight data and notification of events.
*/
int pd_flags; /* (p) PD_ flags. */
u_short pd_xstat; /* (p) Exit status. */
struct selinfo pd_selinfo; /* (p) Event notification. */
struct mtx pd_lock; /* Protect data + events. */
};