MFC 197240,197241,197242,197243,197293,197294,197407:

Add EVFILT_USER filter and EV_DISPATCH/EV_RECEIPT flags to kevent(2).

Approved by: rwatson (mentor)
This commit is contained in:
Stacey Son 2009-10-31 21:22:18 +00:00
parent 13529df13b
commit 83613795b2
3 changed files with 248 additions and 41 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 6, 2007
.Dd September 15, 2009
.Dt KQUEUE 2
.Os
.Sh NAME
@ -201,11 +201,25 @@ Disable the event so
.Fn kevent
will not return it.
The filter itself is not disabled.
.It EV_DISPATCH
Disable the event source immediately after delivery of an event.
See
.Dv EV_DISABLE
above.
.It EV_DELETE
Removes the event from the kqueue.
Events which are attached to
file descriptors are automatically deleted on the last close of
the descriptor.
.It EV_RECEIPT
This flag is useful for making bulk changes to a kqueue without draining
any pending events.
When passed as input, it forces
.Dv EV_ERROR
to always be returned.
When a filter is successfully added the
.Va data
field will be zero.
.It EV_ONESHOT
Causes the event to return only the first occurrence of the filter
being triggered.
@ -441,6 +455,44 @@ The link state is invalid.
On return,
.Va fflags
contains the events which triggered the filter.
.It Dv EVFILT_USER
Establishes a user event identified by
.Va ident
which is not assosicated with any kernel mechanism but is triggered by
user level code.
The lower 24 bits of the
.Va fflags
may be used for user defined flags and manipulated using the following:
.Bl -tag -width XXNOTE_FFLAGSMASK
.It Dv NOTE_FFNOP
Ignore the input
.Va fflags .
.It Dv NOTE_FFAND
Bitwise AND
.Va fflags .
.It Dv NOTE_FFOR
Bitwise OR
.Va fflags .
.It Dv NOTE_COPY
Copy
.Va fflags .
.It Dv NOTE_FFCTRLMASK
Control mask for
.Va fflags .
.It Dv NOTE_FFLAGSMASK
User defined flag mask for
.Va fflags .
.El
.Pp
A user event is triggered for output with the following:
.Bl -tag -width XXNOTE_FFLAGSMASK
.It Dv NOTE_TRIGGER
Cause the event to be triggered.
.El
.Pp
On return,
.Va fflags
contains the users defined flags in the lower 24 bits.
.El
.Sh RETURN VALUES
The

View File

@ -1,6 +1,7 @@
/*-
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
* Copyright 2004 John-Mark Gurney <jmg@FreeBSD.org>
* Copyright (c) 2009 Apple, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -141,6 +142,11 @@ static void filt_timerexpire(void *knx);
static int filt_timerattach(struct knote *kn);
static void filt_timerdetach(struct knote *kn);
static int filt_timer(struct knote *kn, long hint);
static int filt_userattach(struct knote *kn);
static void filt_userdetach(struct knote *kn);
static int filt_user(struct knote *kn, long hint);
static void filt_usertouch(struct knote *kn, struct kevent *kev,
u_long type);
static struct filterops file_filtops =
{ 1, filt_fileattach, NULL, NULL };
@ -151,6 +157,12 @@ static struct filterops proc_filtops =
{ 0, filt_procattach, filt_procdetach, filt_proc };
static struct filterops timer_filtops =
{ 0, filt_timerattach, filt_timerdetach, filt_timer };
static struct filterops user_filtops = {
.f_attach = filt_userattach,
.f_detach = filt_userdetach,
.f_event = filt_user,
.f_touch = filt_usertouch,
};
static uma_zone_t knote_zone;
static int kq_ncallouts = 0;
@ -255,6 +267,7 @@ static struct {
{ &file_filtops }, /* EVFILT_NETDEV */
{ &fs_filtops }, /* EVFILT_FS */
{ &null_filtops }, /* EVFILT_LIO */
{ &user_filtops }, /* EVFILT_USER */
};
/*
@ -557,6 +570,94 @@ filt_timer(struct knote *kn, long hint)
return (kn->kn_data != 0);
}
static int
filt_userattach(struct knote *kn)
{
/*
* EVFILT_USER knotes are not attached to anything in the kernel.
*/
kn->kn_hook = NULL;
if (kn->kn_fflags & NOTE_TRIGGER)
kn->kn_hookid = 1;
else
kn->kn_hookid = 0;
return (0);
}
static void
filt_userdetach(__unused struct knote *kn)
{
/*
* EVFILT_USER knotes are not attached to anything in the kernel.
*/
}
static int
filt_user(struct knote *kn, __unused long hint)
{
return (kn->kn_hookid);
}
static void
filt_usertouch(struct knote *kn, struct kevent *kev, u_long type)
{
u_int ffctrl;
switch (type) {
case EVENT_REGISTER:
if (kev->fflags & NOTE_TRIGGER)
kn->kn_hookid = 1;
ffctrl = kev->fflags & NOTE_FFCTRLMASK;
kev->fflags &= NOTE_FFLAGSMASK;
switch (ffctrl) {
case NOTE_FFNOP:
break;
case NOTE_FFAND:
kn->kn_sfflags &= kev->fflags;
break;
case NOTE_FFOR:
kn->kn_sfflags |= kev->fflags;
break;
case NOTE_FFCOPY:
kn->kn_sfflags = kev->fflags;
break;
default:
/* XXX Return error? */
break;
}
kn->kn_sdata = kev->data;
if (kev->flags & EV_CLEAR) {
kn->kn_hookid = 0;
kn->kn_data = 0;
kn->kn_fflags = 0;
}
break;
case EVENT_PROCESS:
*kev = kn->kn_kevent;
kev->fflags = kn->kn_sfflags;
kev->data = kn->kn_sdata;
if (kn->kn_flags & EV_CLEAR) {
kn->kn_hookid = 0;
kn->kn_data = 0;
kn->kn_fflags = 0;
}
break;
default:
panic("filt_usertouch() - invalid type (%ld)", type);
break;
}
}
int
kqueue(struct thread *td, struct kqueue_args *uap)
{
@ -717,7 +818,7 @@ kern_kevent(struct thread *td, int fd, int nchanges, int nevents,
continue;
kevp->flags &= ~EV_SYSFLAGS;
error = kqueue_register(kq, kevp, td, 1);
if (error) {
if (error || (kevp->flags & EV_RECEIPT)) {
if (nevents != 0) {
kevp->flags = EV_ERROR;
kevp->data = error;
@ -919,17 +1020,11 @@ findkn:
goto findkn;
}
if (kn == NULL && ((kev->flags & EV_ADD) == 0)) {
KQ_UNLOCK(kq);
error = ENOENT;
goto done;
}
/*
* kn now contains the matching knote, or NULL if no match
*/
if (kev->flags & EV_ADD) {
if (kn == NULL) {
if (kn == NULL) {
if (kev->flags & EV_ADD) {
kn = tkn;
tkn = NULL;
if (kn == NULL) {
@ -968,34 +1063,16 @@ findkn:
goto done;
}
KN_LIST_LOCK(kn);
goto done_ev_add;
} else {
/*
* The user may change some filter values after the
* initial EV_ADD, but doing so will not reset any
* filter which has already been triggered.
*/
kn->kn_status |= KN_INFLUX;
/* No matching knote and the EV_ADD flag is not set. */
KQ_UNLOCK(kq);
KN_LIST_LOCK(kn);
kn->kn_sfflags = kev->fflags;
kn->kn_sdata = kev->data;
kn->kn_kevent.udata = kev->udata;
error = ENOENT;
goto done;
}
/*
* We can get here with kn->kn_knlist == NULL.
* This can happen when the initial attach event decides that
* the event is "completed" already. i.e. filt_procattach
* is called on a zombie process. It will call filt_proc
* which will remove it from the list, and NULL kn_knlist.
*/
event = kn->kn_fop->f_event(kn, 0);
KQ_LOCK(kq);
if (event)
KNOTE_ACTIVATE(kn, 1);
kn->kn_status &= ~KN_INFLUX;
KN_LIST_UNLOCK(kn);
} else if (kev->flags & EV_DELETE) {
}
if (kev->flags & EV_DELETE) {
kn->kn_status |= KN_INFLUX;
KQ_UNLOCK(kq);
if (!(kn->kn_status & KN_DETACHED))
@ -1004,6 +1081,37 @@ findkn:
goto done;
}
/*
* The user may change some filter values after the initial EV_ADD,
* but doing so will not reset any filter which has already been
* triggered.
*/
kn->kn_status |= KN_INFLUX;
KQ_UNLOCK(kq);
KN_LIST_LOCK(kn);
kn->kn_kevent.udata = kev->udata;
if (!fops->f_isfd && fops->f_touch != NULL) {
fops->f_touch(kn, kev, EVENT_REGISTER);
} else {
kn->kn_sfflags = kev->fflags;
kn->kn_sdata = kev->data;
}
/*
* We can get here with kn->kn_knlist == NULL. This can happen when
* the initial attach event decides that the event is "completed"
* already. i.e. filt_procattach is called on a zombie process. It
* will call filt_proc which will remove it from the list, and NULL
* kn_knlist.
*/
done_ev_add:
event = kn->kn_fop->f_event(kn, 0);
KQ_LOCK(kq);
if (event)
KNOTE_ACTIVATE(kn, 1);
kn->kn_status &= ~KN_INFLUX;
KN_LIST_UNLOCK(kn);
if ((kev->flags & EV_DISABLE) &&
((kn->kn_status & KN_DISABLED) == 0)) {
kn->kn_status |= KN_DISABLED;
@ -1183,7 +1291,7 @@ kqueue_scan(struct kqueue *kq, int maxevents, struct kevent_copyops *k_ops,
struct timeval atv, rtv, ttv;
struct knote *kn, *marker;
int count, timeout, nkev, error, influx;
int haskqglobal;
int haskqglobal, touch;
count = maxevents;
nkev = 0;
@ -1315,12 +1423,25 @@ start:
influx = 1;
continue;
}
*kevp = kn->kn_kevent;
touch = (!kn->kn_fop->f_isfd &&
kn->kn_fop->f_touch != NULL);
if (touch)
kn->kn_fop->f_touch(kn, kevp, EVENT_PROCESS);
else
*kevp = kn->kn_kevent;
KQ_LOCK(kq);
KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal);
if (kn->kn_flags & EV_CLEAR) {
kn->kn_data = 0;
kn->kn_fflags = 0;
if (kn->kn_flags & (EV_CLEAR | EV_DISPATCH)) {
/*
* Manually clear knotes who weren't
* 'touch'ed.
*/
if (touch == 0 && kn->kn_flags & EV_CLEAR) {
kn->kn_data = 0;
kn->kn_fflags = 0;
}
if (kn->kn_flags & EV_DISPATCH)
kn->kn_status |= KN_DISABLED;
kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE);
kq->kq_count--;
} else

View File

@ -41,7 +41,8 @@
#define EVFILT_NETDEV (-8) /* network devices */
#define EVFILT_FS (-9) /* filesystem events */
#define EVFILT_LIO (-10) /* attached to lio requests */
#define EVFILT_SYSCOUNT 10
#define EVFILT_USER (-11) /* User events */
#define EVFILT_SYSCOUNT 11
#define EV_SET(kevp_, a, b, c, d, e, f) do { \
struct kevent *kevp = (kevp_); \
@ -71,6 +72,8 @@ struct kevent {
/* flags */
#define EV_ONESHOT 0x0010 /* only report one occurrence */
#define EV_CLEAR 0x0020 /* clear event state after reporting */
#define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
#define EV_DISPATCH 0x0080 /* disable event after reporting */
#define EV_SYSFLAGS 0xF000 /* reserved by system */
#define EV_FLAG1 0x2000 /* filter-specific flag */
@ -79,6 +82,25 @@ struct kevent {
#define EV_EOF 0x8000 /* EOF detected */
#define EV_ERROR 0x4000 /* error, data contains errno */
/*
* data/hint flags/masks for EVFILT_USER, shared with userspace
*
* On input, the top two bits of fflags specifies how the lower twenty four
* bits should be applied to the stored value of fflags.
*
* On output, the top two bits will always be set to NOTE_FFNOP and the
* remaining twenty four bits will contain the stored fflags value.
*/
#define NOTE_FFNOP 0x00000000 /* ignore input fflags */
#define NOTE_FFAND 0x40000000 /* AND fflags */
#define NOTE_FFOR 0x80000000 /* OR fflags */
#define NOTE_FFCOPY 0xc0000000 /* copy fflags */
#define NOTE_FFCTRLMASK 0xc0000000 /* masks for operations */
#define NOTE_FFLAGSMASK 0x00ffffff
#define NOTE_TRIGGER 0x01000000 /* Cause the event to be
triggered for output. */
/*
* data/hint flags for EVFILT_{READ|WRITE}, shared with userspace
*/
@ -154,11 +176,22 @@ MALLOC_DECLARE(M_KQUEUE);
*/
#define NOTE_SIGNAL 0x08000000
/*
* Hint values for the optional f_touch event filter. If f_touch is not set
* to NULL and f_isfd is zero the f_touch filter will be called with the type
* argument set to EVENT_REGISTER during a kevent() system call. It is also
* called under the same conditions with the type argument set to EVENT_PROCESS
* when the event has been triggered.
*/
#define EVENT_REGISTER 1
#define EVENT_PROCESS 2
struct filterops {
int f_isfd; /* true if ident == filedescriptor */
int (*f_attach)(struct knote *kn);
void (*f_detach)(struct knote *kn);
int (*f_event)(struct knote *kn, long hint);
void (*f_touch)(struct knote *kn, struct kevent *kev, u_long type);
};
/*
@ -193,6 +226,7 @@ struct knote {
} kn_ptr;
struct filterops *kn_fop;
void *kn_hook;
int kn_hookid;
#define kn_id kn_kevent.ident
#define kn_filter kn_kevent.filter