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$ .\" $FreeBSD$
.\" .\"
.Dd November 4, 2013 .Dd April 7, 2014
.Dt KQUEUE 2 .Dt KQUEUE 2
.Os .Os
.Sh NAME .Sh NAME
@ -412,6 +412,24 @@ and the child process will not signal a NOTE_CHILD event.
On return, On return,
.Va fflags .Va fflags
contains the events which triggered the filter. 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 .It EVFILT_SIGNAL
Takes the signal number to monitor as the identifier and returns Takes the signal number to monitor as the identifier and returns
when the given signal is delivered to the process. when the given signal is delivered to the process.

View File

@ -32,7 +32,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd April 4, 2014 .Dd April 7, 2014
.Dt PDFORK 2 .Dt PDFORK 2
.Os .Os
.Sh NAME .Sh NAME
@ -117,6 +117,13 @@ and
allow waiting for process state transitions; currently only allow waiting for process state transitions; currently only
.Dv POLLHUP .Dv POLLHUP
is defined, and will be raised when the process dies. 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 .Pp
.Xr close 2 .Xr close 2
will close the process descriptor unless will close the process descriptor unless

View File

@ -290,7 +290,7 @@ static struct {
{ &proc_filtops }, /* EVFILT_PROC */ { &proc_filtops }, /* EVFILT_PROC */
{ &sig_filtops }, /* EVFILT_SIGNAL */ { &sig_filtops }, /* EVFILT_SIGNAL */
{ &timer_filtops }, /* EVFILT_TIMER */ { &timer_filtops }, /* EVFILT_TIMER */
{ &null_filtops }, /* former EVFILT_NETDEV */ { &file_filtops }, /* EVFILT_PROCDESC */
{ &fs_filtops }, /* EVFILT_FS */ { &fs_filtops }, /* EVFILT_FS */
{ &null_filtops }, /* EVFILT_LIO */ { &null_filtops }, /* EVFILT_LIO */
{ &user_filtops }, /* EVFILT_USER */ { &user_filtops }, /* EVFILT_USER */
@ -417,27 +417,22 @@ filt_procdetach(struct knote *kn)
static int static int
filt_proc(struct knote *kn, long hint) filt_proc(struct knote *kn, long hint)
{ {
struct proc *p = kn->kn_ptr.p_proc; struct proc *p;
u_int event; u_int event;
/* p = kn->kn_ptr.p_proc;
* mask off extra data /* Mask off extra data. */
*/
event = (u_int)hint & NOTE_PCTRLMASK; 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) if (kn->kn_sfflags & event)
kn->kn_fflags |= 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 (event == NOTE_EXIT) {
if (!(kn->kn_status & KN_DETACHED)) if (!(kn->kn_status & KN_DETACHED))
knlist_remove_inevent(&p->p_klist, kn); 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; kn->kn_ptr.p_proc = NULL;
if (kn->kn_fflags & NOTE_EXIT) if (kn->kn_fflags & NOTE_EXIT)
kn->kn_data = p->p_xstat; kn->kn_data = p->p_xstat;

View File

@ -236,6 +236,7 @@ procdesc_new(struct proc *p, int flags)
if (flags & PD_DAEMON) if (flags & PD_DAEMON)
pd->pd_flags |= PDF_DAEMON; pd->pd_flags |= PDF_DAEMON;
PROCDESC_LOCK_INIT(pd); 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 * 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), KASSERT((pd->pd_flags & PDF_CLOSED),
("procdesc_free: !PDF_CLOSED")); ("procdesc_free: !PDF_CLOSED"));
knlist_destroy(&pd->pd_selinfo.si_note);
PROCDESC_LOCK_DESTROY(pd); PROCDESC_LOCK_DESTROY(pd);
uma_zfree(procdesc_zone, pd); uma_zfree(procdesc_zone, pd);
} }
@ -296,6 +298,7 @@ procdesc_exit(struct proc *p)
("procdesc_exit: closed && parent not init")); ("procdesc_exit: closed && parent not init"));
pd->pd_flags |= PDF_EXITED; pd->pd_flags |= PDF_EXITED;
pd->pd_xstat = p->p_xstat;
/* /*
* If the process descriptor has been closed, then we have nothing * 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; pd->pd_flags &= ~PDF_SELECTED;
selwakeup(&pd->pd_selinfo); selwakeup(&pd->pd_selinfo);
} }
KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_EXIT);
PROCDESC_UNLOCK(pd); PROCDESC_UNLOCK(pd);
return (0); return (0);
} }
@ -460,11 +464,71 @@ procdesc_poll(struct file *fp, int events, struct ucred *active_cred,
return (revents); 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 static int
procdesc_kqfilter(struct file *fp, struct knote *kn) 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 static int

View File

@ -38,7 +38,7 @@
#define EVFILT_PROC (-5) /* attached to struct proc */ #define EVFILT_PROC (-5) /* attached to struct proc */
#define EVFILT_SIGNAL (-6) /* attached to struct proc */ #define EVFILT_SIGNAL (-6) /* attached to struct proc */
#define EVFILT_TIMER (-7) /* timers */ #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_FS (-9) /* filesystem events */
#define EVFILT_LIO (-10) /* attached to lio requests */ #define EVFILT_LIO (-10) /* attached to lio requests */
#define EVFILT_USER (-11) /* User events */ #define EVFILT_USER (-11) /* User events */
@ -120,7 +120,7 @@ struct kevent {
#define NOTE_REVOKE 0x0040 /* vnode access was revoked */ #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_EXIT 0x80000000 /* process exited */
#define NOTE_FORK 0x40000000 /* process forked */ #define NOTE_FORK 0x40000000 /* process forked */

View File

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