kqueue: Add EV_KEEPUDATA flag

When this flag is set, operations that update an existing kevent will
not change the udata field.  This can be used to NOTE_TRIGGER or
EV_{EN,DIS}ABLE events without overwriting the stashed pointer.

Reviewed by:	Domagoj Stolfa <domagoj.stolfa@gmail.com>
Obtained from:	CheriBSD
Sponsored by:	Microsoft
Differential Revision:	https://reviews.freebsd.org/D30286
This commit is contained in:
Nathaniel Wesley Filardo 2021-09-23 17:31:39 -07:00 committed by John Baldwin
parent 701a99939f
commit 0321a7990b
4 changed files with 86 additions and 2 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 7, 2021
.Dd September 23, 2021
.Dt KQUEUE 2
.Os
.Sh NAME
@ -262,6 +262,21 @@ Filters may set this flag to indicate filter-specific EOF condition.
See
.Sx RETURN VALUES
below.
.It Dv EV_KEEPUDATA
Causes
.Fn kevent
to leave unchanged any
.Fa udata
associated with an existing event. This allows other aspects of the
event to be modified without requiring the caller to know the
.Fa udata
value presently associated.
This is especially useful with
.Dv NOTE_TRIGGER
or flags like
.Dv EV_ENABLE.
This flag may not be used with
.Dv EV_ADD.
.El
.Pp
The predefined system filters are listed below.

View File

@ -1496,6 +1496,13 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td,
return EINVAL;
if (kev->flags & EV_ADD) {
/* Reject an invalid flag pair early */
if (kev->flags & EV_KEEPUDATA) {
tkn = NULL;
error = EINVAL;
goto done;
}
/*
* Prevent waiting with locks. Non-sleepable
* allocation failures are handled in the loop, only
@ -1684,7 +1691,8 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td,
kn_enter_flux(kn);
KQ_UNLOCK(kq);
knl = kn_list_lock(kn);
kn->kn_kevent.udata = kev->udata;
if ((kev->flags & EV_KEEPUDATA) == 0)
kn->kn_kevent.udata = kev->udata;
if (!fops->f_isfd && fops->f_touch != NULL) {
fops->f_touch(kn, kev, EVENT_REGISTER);
} else {

View File

@ -138,6 +138,7 @@ struct kevent32_freebsd11 {
#define EV_ENABLE 0x0004 /* enable event */
#define EV_DISABLE 0x0008 /* disable event (not reported) */
#define EV_FORCEONESHOT 0x0100 /* enable _ONESHOT and force trigger */
#define EV_KEEPUDATA 0x0200 /* do not update the udata field */
/* flags */
#define EV_ONESHOT 0x0010 /* only report one occurrence */

View File

@ -60,6 +60,32 @@ event_wait(void)
success();
}
static void
event_wait_keepudata(void)
{
const char *test_id = "kevent(EVFILT_USER, wait w/ EV_KEEPUDATA)";
struct kevent kev;
test_begin(test_id);
test_no_kevents();
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, &kev);
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_KEEPUDATA, NOTE_TRIGGER, 0,
NULL);
kev.fflags &= ~NOTE_FFCTRLMASK;
kev.fflags &= ~NOTE_TRIGGER;
kev.flags = EV_CLEAR;
kev.udata = &kev;
kevent_cmp(&kev, kevent_get(kqfd));
test_no_kevents();
success();
}
static void
disable_and_enable(void)
{
@ -88,6 +114,38 @@ disable_and_enable(void)
success();
}
static void
disable_and_enable_keepudata(void)
{
const char *test_id =
"kevent(EVFILT_USER, EV_DISABLE and EV_ENABLE w/ EV_KEEPUDATA)";
struct kevent kev;
test_begin(test_id);
test_no_kevents();
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, &kev);
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DISABLE | EV_KEEPUDATA, 0, 0,
NULL);
/* Trigger the event, but since it is disabled, nothing will happen. */
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_KEEPUDATA, NOTE_TRIGGER, 0, NULL);
test_no_kevents();
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ENABLE | EV_KEEPUDATA, 0, 0,
NULL);
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_KEEPUDATA, NOTE_TRIGGER, 0, NULL);
kev.flags = EV_CLEAR;
kev.fflags &= ~NOTE_FFCTRLMASK;
kev.fflags &= ~NOTE_TRIGGER;
kev.udata = &kev;
kevent_cmp(&kev, kevent_get(kqfd));
success();
}
static void
oneshot(void)
{
@ -120,7 +178,9 @@ test_evfilt_user(void)
add_and_delete();
event_wait();
event_wait_keepudata();
disable_and_enable();
disable_and_enable_keepudata();
oneshot();
/* TODO: try different fflags operations */