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:
parent
13529df13b
commit
83613795b2
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user