Skip to content

Instantly share code, notes, and snippets.

@thesjg
Created May 31, 2011 06:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thesjg/1000064 to your computer and use it in GitHub Desktop.
Save thesjg/1000064 to your computer and use it in GitHub Desktop.
KEVENT SUBSYSTEM REFACTOR DESIGN DOCUMENT
/*
* Drivers and etc. will only expose an event filter now, instead of
* attach/detach/event filters. One filter function each is exposed for read,
* write, except, etc (instead of 3 each).
*
* A new kq api will be exposed for drivers to hook themselves into kq at
* initialization time, they will provide flags (such as whether they are mpsafe
* or not), some opaque private data (a hook) and a function pointer. The driver
* will provide this for each of the event types it wishes to services, READ,
* WRITE, etc.
*
* This new kq init api will return a REF of some sort that KNOTE() will operate
* on in place of the current exposed list.
*
* When an instance of a driver goes away it will call a new destroy function
* in the kq api, the kq subsystem will then assume responsibility for servicing
* any requests against those knotes in a sane manner and deconstruction as
* necessary.
*/
struct kfilter_ops {
u_int fo_flags;
caddr_t fo_hook;
int (*fo_event) (struct kfilter_note *kn, caddr_t hook, long hint);
}
/*
* The kq subsystem will maintain a list of registered filters, along with the
* filter ops for each (flags, hooks and callbacks)
*/
struct kfilter {
TAILQ_HEAD(, kfilter_entry) f_entries;
struct filterops[EVFILT_SYSCOUNT] f_fops;
};
/*
* the kevent structure is no longer embedded, instead we just take
* what we need, part is stored in kfilter_note and the shared parts are
* in kfilter_entry
*
* 1 of these structures will exist for each fd of each filter type, so
* if you are poll(2)'ing read/write on a socket there will be 2 of these,
* one for read and another for write hanging off of a single kfilter_entry
* structure
*/
struct kfilter_note {
struct kfilter_event *fn_event; /* Parent filter event */
short fn_filter;
u_int fn_ufflags; /* Flags passed from userland */
intptr_t fn_udata; /* Data passed from userland */
/*
* These are set-able by the filter and will be returned to userland
* (if kq) or acted upon in the select/poll/etc. layers
*/
u_short fn_flags;
u_int fn_fflags;
intptr_t fn_data;
int fn_status; /* ...? */
};
/*
* This is a new container structure and takes the place of the knote structure
* in all of the lists that are traversed inside the kq subsystem. This is
* essentially what is used for accounting where struct kfilter_note is the
* "business end" and gets handed to filters.
*
* Each of select, poll, epoll, etc. maintains its own kqueue internally
* so there is no cross-pollination.
*/
struct kfilter_entry {
TAILQ_ENTRY(kfilter_entry) fe_link; /* Entries embedded in proc (for fd) */
TAILQ_ENTRY(kfilter_entry) fe_kqlink; /* List of entries in our parent kqueue */
TAILQ_ENTRY(kfilter_entry) fe_pending; /* Entries w/ pending "queued" notes (also in struct kqueue) */
TAILQ_ENTRY(kfilter_entry) fe_entry; /* Per-device filter notes (embedded in struct kfilter) */
/* event entries are indexed on the fe_link list by ident (fd) */
uintptr_t fe_ident; /* ident (typically fd) of all attached knotes */
int fe_status; /* KFE_REPROCESS, KFE_PROCESSING, etc... */
struct kqueue *fe_kq; /* kqueue we are attached to */
union {
struct file *p_fp; /* */
struct proc *p_proc; /* */
}
struct kfilter_note[EVFILT_SYSCOUNT] *fe_notes; /* 1 each for READ, WRITE, etc. or NULL */
intptr_t fn_apidata; /* Opaque API Data (from select or poll, etc.?) */
};
struct kqueue {
TAILQ_HEAD(, kfilter_entry) kq_fepend; /* Pending filter entries */
TAILQ_HEAD(, kfilter_entry) kq_felist; /* All filter entries */
int kq_count; /* number of pending events */
struct sigio *kq_sigio;
struct filedesc *kq_fdp;
int kq_state;
u_long kq_knhashmask; /* size of knhash */
struct klist *kq_knhash; /* hash table for knotes */
/* pulled kqinfo, needs ... */
struct kfilter kq_filter;
};
KNOTE(list, hint) is the current KNOTE proto., this might become simply..
KNOTE(REF, hint) or KNOTE_ALT(REF, hint, filt) ..
where filt would be one of EVFILT_READ, EVFILT_WRITE, etc.., the driver could
optimize the re-scan if it knew what type of event would succeed as a result of
its recent action.
----------
Implementing poll, select, epoll, etc.
Currently these are primarily implemented by copyin and copyout functions
which are passed an array of kevent structures. I forsee changing this so
that instead of staging up kevent structures and passing them, the kq internals
will simply build an array of pointers to queued kfilter_entry structures and
pass that array. This will make for slightly more work for the kqueue proper
copyin and copyout routines, it will have to build kevent structures
and copy them out, instead of just doing a copyout(), but all other copyin
and copyout routines will be simplified.
Possibly develop an API to help facilitate the copyin/copyout routines for
the various api's, if warranted, they will be sticking their fingers directly
into the kfilter_entry and kfilter_note structures.
Maybe let the copyin function just push as many items as it wants into
the subsystem in one shot via an API instead of an 8 item limit or etc? Same for
copyout (but with a brake at 64 or such?)?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment