svn commit: r198732 - in stable/8: lib/libc lib/libc/gen lib/libc/stdio lib/libc/stdtime lib/libc/string lib/libc/sys sys sys/amd64/include/xen sys/cddl/contrib/opensolaris sys/contrib/dev/acpica s...

Stacey Son sson at FreeBSD.org
Sat Oct 31 21:22:18 UTC 2009


Author: sson
Date: Sat Oct 31 21:22:18 2009
New Revision: 198732
URL: http://svn.freebsd.org/changeset/base/198732

Log:
  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)

Modified:
  stable/8/lib/libc/   (props changed)
  stable/8/lib/libc/gen/   (props changed)
  stable/8/lib/libc/stdio/asprintf.c   (props changed)
  stable/8/lib/libc/stdtime/   (props changed)
  stable/8/lib/libc/string/ffsll.c   (props changed)
  stable/8/lib/libc/string/flsll.c   (props changed)
  stable/8/lib/libc/string/wcpcpy.c   (props changed)
  stable/8/lib/libc/string/wcpncpy.c   (props changed)
  stable/8/lib/libc/sys/kqueue.2
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)
  stable/8/sys/dev/xen/xenpci/   (props changed)
  stable/8/sys/kern/kern_event.c
  stable/8/sys/sys/event.h

Modified: stable/8/lib/libc/sys/kqueue.2
==============================================================================
--- stable/8/lib/libc/sys/kqueue.2	Sat Oct 31 20:59:13 2009	(r198731)
+++ stable/8/lib/libc/sys/kqueue.2	Sat Oct 31 21:22:18 2009	(r198732)
@@ -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

Modified: stable/8/sys/kern/kern_event.c
==============================================================================
--- stable/8/sys/kern/kern_event.c	Sat Oct 31 20:59:13 2009	(r198731)
+++ stable/8/sys/kern/kern_event.c	Sat Oct 31 21:22:18 2009	(r198732)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon at FreeBSD.org>
  * Copyright 2004 John-Mark Gurney <jmg at 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, i
 				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 maxev
 	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

Modified: stable/8/sys/sys/event.h
==============================================================================
--- stable/8/sys/sys/event.h	Sat Oct 31 20:59:13 2009	(r198731)
+++ stable/8/sys/sys/event.h	Sat Oct 31 21:22:18 2009	(r198732)
@@ -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


More information about the svn-src-stable-8 mailing list