Add optional touch event filter hooks to kevents.

The touch event filter is called when a kernel event data is possibly
updated.  There are two hook points.  First, during a kevent() system
call.  Second, when an event has been triggered.

Approved by:	rwatson (co-mentor)
This commit is contained in:
Stacey Son 2009-09-16 03:15:57 +00:00
parent 11c99a6d7b
commit 95128e983f
2 changed files with 68 additions and 37 deletions

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
@ -934,17 +935,11 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa
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) {
@ -983,34 +978,16 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa
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))
@ -1019,6 +996,37 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa
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;
@ -1198,7 +1206,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;
@ -1330,12 +1338,23 @@ kqueue_scan(struct kqueue *kq, int maxevents, struct kevent_copyops *k_ops,
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;
/*
* Manually clear knotes who weren't
* 'touch'ed.
*/
if (touch == 0) {
kn->kn_data = 0;
kn->kn_fflags = 0;
}
kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE);
kq->kq_count--;
} else

View File

@ -154,11 +154,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, long type);
};
/*
@ -193,6 +204,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