Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 6 Jun 2010 17:12:30 GMT
From:      Ilya Putsikau <ilya@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 179260 for review
Message-ID:  <201006061712.o56HCUqn013200@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@179260?ac=10

Change 179260 by ilya@ilya_triton on 2010/06/06 17:11:32

	Add device read, poll and ioctl calls.
	Ioctls: add watch, remove watch, FIONREAD
	Inital support for creating and searching for nodes

Affected files ...

.. //depot/projects/soc2010/ilya_fsnotify/src/sys/kern/vfs_notify.c#2 edit
.. //depot/projects/soc2010/ilya_fsnotify/src/sys/sys/fsnotify.h#2 edit

Differences ...

==== //depot/projects/soc2010/ilya_fsnotify/src/sys/kern/vfs_notify.c#2 (text+ko) ====

@@ -35,6 +35,7 @@
 #include <sys/condvar.h>
 #include <sys/fcntl.h>
 #include <sys/filio.h>
+#include <sys/hash.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
@@ -44,70 +45,86 @@
 #include <sys/poll.h>
 #include <sys/proc.h>
 #include <sys/queue.h>
+#include <sys/refcount.h>
 #include <sys/systm.h>
 #include <sys/taskqueue.h>
 #include <sys/vnode.h>
 
 #include <sys/fsnotify.h>
 
-struct fn_eventhandle;
+struct fneventhandle;
+struct fnwatch;
 
-struct fn_session {
-	TAILQ_HEAD(, fn_eventhandle) queue;
-	struct mtx queue_mtx;
-	struct cv queue_ready_cv;
-	struct selinfo queue_poll;
-	int queue_size;
+struct fnsession {
+	struct mtx		ss_mtx;
+	TAILQ_HEAD(, fneventhandle) ss_queue;
+	TAILQ_HEAD(, fnwatch)	ss_watchlist;
+	struct cv		ss_queuecv;
+	struct selinfo		ss_queuepoll;
+	int			ss_queuesize;
 };
 
-struct fn_watch {
-	TAILQ_ENTRY(fn_watch) node_entry;
-	struct fn_node *node;
-	struct fn_session *session;
-	int wd;
-	int mask;
+struct fnnode {
+	LIST_ENTRY(fnnode)	nd_hashentry;
+	TAILQ_HEAD(, fnwatch)	nd_watchlist;
+	struct mtx		nd_mtx;
+	struct vnode		*nd_vnode;
+	char			*nd_path;
+	int			nd_pathlen;
+	uint32_t		nd_pathhash;
+	int			nd_watchcount;
+	u_int			nd_supermask;
+	volatile u_int		nd_refcnt;
 };
 
-struct fn_node {
-	TAILQ_HEAD(, fn_watch) watch_list;
-	struct mtx node_mtx;
-	struct vnode *vnode;
-	char *path;
-	char *path_buf;
-	int path_len;
-	int watch_count;
-	u_int supermask;
-	volatile u_int refcnt;
+struct fneventhandle {
+	TAILQ_ENTRY(fneventhandle) eh_queueentry;
+	struct fnevent		*eh_event;
+	struct fnwatch		*eh_watch;
 };
 
-struct fn_eventhandle {
-	TAILQ_ENTRY(fn_eventhandle) sessionqueue_entry;
-	struct fn_event *event;
-	struct fn_watch *watch;
+struct fnevent {
+	TAILQ_ENTRY(fnevent)	ev_queueentry;
+	struct fnnode		*ev_node;
+	char			*ev_path;
+	int			ev_pathlen;
+	int 			ev_mask;
+	int 			ev_cookie;
+	int			ev_handlecount;
+	int			ev_handlemaxsize;
+	struct fneventhandle	ev_handlebuf[0];
 };
 
-struct fn_event {
-	TAILQ_ENTRY(fn_event) queue_entry;
-	struct fn_node *node;
-	int flags;
-	int mask;
-	int cookie;
-	int handle_count;
-	int handle_maxsize;
-	int path_pos;
-	char path_buf[PATH_MAX];
-	struct fn_eventhandle handle_buf[0];
+struct fnwatch {
+	TAILQ_ENTRY(fnwatch)	wt_nodeentry;
+	TAILQ_ENTRY(fnwatch)	wt_sessionentry;
+	struct fnnode		*wt_node;
+	struct fnsession	*wt_session;
+	int			wt_mask;
+	int			wt_wd;
+	volatile u_int		wt_refcnt;
 };
 
 
+LIST_HEAD(fnnode_hashhead, fnnode);
+
+static MALLOC_DEFINE(M_FSNOTIFY, "fsnotify", "fsnotify");
+static MALLOC_DEFINE(M_FSNOTIFYHASH, "fsnotify_hash", "fsnotify hash tables");
+static MALLOC_DEFINE(M_FSNOTIFYPATH, "fsnotify_path", "fsnotify path buffers");
+
 static struct cdev *fsnotify_dev;
-static MALLOC_DEFINE(M_FSNOTIFY, "fsnotify", "fsnotify");
-static TAILQ_HEAD(, fn_event) fsnotify_queue =
+
+static TAILQ_HEAD(, fnevent) fsnotify_queue =
     TAILQ_HEAD_INITIALIZER(fsnotify_queue);
 static struct mtx fsnotify_queue_mtx;
 static struct task fsnotify_task;
 static struct taskqueue *fsnotify_tq;
 
+static struct fnnode_hashhead **fnnode_vphashtbl;
+static struct fnnode_hashhead **fnnode_pathhashtbl;
+static struct mtx fnnode_hashmtx;
+static u_long fnnode_hashmask;
+
 static d_open_t fsnotify_open;
 static d_read_t fsnotify_read;
 static d_ioctl_t fsnotify_ioctl;
@@ -131,14 +148,22 @@
 static vop_symlink_t hook_symlink;
 
 static void process_queue(void *context, int pending);
-static void enqueue_direvent(struct fn_node *pn, struct componentname *cnp,
+static void enqueue_direvent(struct fnnode *dirnode, struct componentname *cnp,
     int mask);
-static void enqueue_fileevent(struct fn_node *pn, int mask);
-static struct fn_node* node_lookup(struct vnode *vp);
+static void enqueue_fileevent(struct fnnode *dirnode, int mask);
+static void session_drophandle(struct fnsession *ss, struct fneventhandle *eh);
+static int session_addwatch(struct fnsession *ss, struct fnnode *node, int mask,
+    struct fnwatch **watchpp);
+static int session_rmwatch(struct fnsession *ss, int wd);
+static struct fnnode* node_lookup(struct vnode *vp);
+static struct fnnode* node_alloc(char *path, int pathlen);
+static void node_create(struct fnnode *node, struct vnode *vp);
+static void node_destroy(struct fnnode *node);
 
 static int
 fsnotify_modevent(struct module *module, int cmd, void *arg)
 {
+	int hashsize;
 	int error = 0;
 
 	switch (cmd) {
@@ -155,9 +180,16 @@
 		}
 
 		mtx_init(&fsnotify_queue_mtx, "fsnotify_queue", NULL, MTX_DEF);
+		mtx_init(&fnnode_hashmtx, "fsnotify_hash", NULL, MTX_DEF);
+
+		hashsize = MAX(desiredvnodes / 32, 16);
+		fnnode_vphashtbl = hashinit(hashsize, M_FSNOTIFYHASH,
+		    &fnnode_hashmask);
+		fnnode_pathhashtbl = hashinit(hashsize, M_FSNOTIFYHASH,
+		    &fnnode_hashmask);
 
 		TASK_INIT(&fsnotify_task, 0, process_queue, NULL);
-		fsnotify_tq = taskqueue_create_fast("fsnotify", M_WAITOK,
+		fsnotify_tq = taskqueue_create("fsnotify", M_WAITOK,
 		    taskqueue_thread_enqueue, &fsnotify_tq);
 		taskqueue_start_threads(&fsnotify_tq, 1, PWAIT,
 		    "fsnotify taskq");
@@ -185,7 +217,10 @@
 		destroy_dev(fsnotify_dev);
 		taskqueue_drain(fsnotify_tq, &fsnotify_task);
 		taskqueue_free(fsnotify_tq);
+		free(fnnode_vphashtbl, M_FSNOTIFYHASH);
+		free(fnnode_pathhashtbl, M_FSNOTIFYHASH);
 		mtx_destroy(&fsnotify_queue_mtx);
+		mtx_destroy(&fnnode_hashmtx);
 		break;
 	case MOD_SHUTDOWN:
 		break;
@@ -214,12 +249,16 @@
 static void
 fsnotify_session_dtor(void *data)
 {
-	struct fn_session *ss = data;
+	struct fnsession *ss = data;
+	struct fneventhandle *eh;
 
-	/* TODO drop all elements in queue */
+	while (!TAILQ_EMPTY(&ss->ss_queue)) {
+		eh = TAILQ_FIRST(&ss->ss_queue);
+		session_drophandle(ss, eh);
+	}
 
-	cv_destroy(&ss->queue_ready_cv);
-	mtx_destroy(&ss->queue_mtx);
+	cv_destroy(&ss->ss_queuecv);
+	mtx_destroy(&ss->ss_mtx);
 
 	free(ss, M_FSNOTIFY);
 }
@@ -227,12 +266,12 @@
 static int
 fsnotify_open(struct cdev *dev, int flag, int mode, struct thread *td)
 {
-	struct fn_session *ss;
+	struct fnsession *ss;
 
-	ss = malloc(sizeof(struct fn_session), M_FSNOTIFY, M_WAITOK | M_ZERO);
+	ss = malloc(sizeof(struct fnsession), M_FSNOTIFY, M_WAITOK | M_ZERO);
 
-	mtx_init(&ss->queue_mtx, "fn_session_queue", NULL, MTX_DEF);
-	cv_init(&ss->queue_ready_cv, "fn_session_queue_ready");
+	mtx_init(&ss->ss_mtx, "fnsession_queue", NULL, MTX_DEF);
+	cv_init(&ss->ss_queuecv, "fnsession_queuecv");
 
 	devfs_set_cdevpriv(ss, fsnotify_session_dtor);
 
@@ -242,8 +281,11 @@
 static int
 fsnotify_read(struct cdev *dev, struct uio *uio, int flag)
 {
-	struct fn_session *ss;
-	int error;
+	struct fsnotify_event *ue;
+	struct fnsession *ss;
+	struct fneventhandle *eh;
+	int len, error;
+	char user_buf[sizeof(struct fsnotify_event) + PATH_MAX];
 
 	if (uio->uio_resid == 0)
 		return (0);
@@ -251,8 +293,44 @@
 	error = devfs_get_cdevpriv((void **)&ss);
 	if (error != 0)
 		return (error);
+
+	printf("fsnotify_read: offset %jd\n", uio->uio_offset);
 	
-	/* TODO */
+	mtx_lock(&ss->ss_mtx);
+
+	if (TAILQ_EMPTY(&ss->ss_queue)) {
+		if (flag & O_NONBLOCK)
+			error = EWOULDBLOCK;
+		else
+			error = cv_wait_sig(&ss->ss_queuecv,
+			    &ss->ss_mtx);
+	}
+
+	if (error != 0) {
+		mtx_unlock(&ss->ss_mtx);
+		return (error);
+	}
+
+	eh = TAILQ_FIRST(&ss->ss_queue);
+	ue = (struct fsnotify_event *) user_buf;
+	ue->wd = eh->eh_watch->wt_wd;
+	ue->mask = eh->eh_watch->wt_mask & eh->eh_event->ev_mask;
+	ue->cookie = eh->eh_event->ev_cookie;
+	ue->len = eh->eh_event->ev_pathlen + 1;
+	memcpy(ue->name, eh->eh_event->ev_path, ue->len);
+	len = ue->len + sizeof(struct fsnotify_event);
+
+	mtx_unlock(&ss->ss_mtx);
+
+	error = uiomove(user_buf, MIN(len, uio->uio_resid), uio);
+	printf("fsnotify_read: uiomove: error %d, offset %jd, len %d\n", error,
+	    uio->uio_offset, len);
+	if (error == 0 && uio->uio_offset >= len) {
+		uio->uio_offset = 0;
+		mtx_lock(&ss->ss_mtx);
+		session_drophandle(ss, eh);
+		mtx_unlock(&ss->ss_mtx);
+	}
 
 	return (error);
 }
@@ -261,7 +339,11 @@
 fsnotify_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
     struct thread *td)
 {
-	struct fn_session *ss;
+	struct fnsession *ss;
+	struct fnnode *node;
+	struct fnwatch *watch;
+	struct fsnotify_addwatch_args *add_args;
+	struct vnode *vp;
 	int error;
 
 	error = devfs_get_cdevpriv((void **)&ss);
@@ -270,11 +352,31 @@
 
 	switch (cmd) {
 	case FSNOTIFY_ADDWATCH:
-		return (0);
+		add_args = (struct fsnotify_addwatch_args *)data;
+		/* TODO: get vnode for fd */
+		vp = NULL;
+		node = node_lookup(vp);
+		if (node == NULL) {
+			/* TODO: get full path for vp */
+			node_alloc(NULL, 0);
+			node_create(node, vp);
+			mtx_lock(&node->nd_mtx);
+		}
+		error = session_addwatch(ss, node, add_args->mask, &watch);
+		if (error == 0)
+			add_args->wd = watch->wt_wd;
+		return (error);
 	case FSNOTIFY_RMWATCH:
-		return (0);
+		error = session_rmwatch(ss, *(int *)data);
+		return (error);
 	case FIONREAD:
-		*(int *)data = 0;
+		mtx_lock(&ss->ss_mtx);
+		if (!TAILQ_EMPTY(&ss->ss_queue))
+			*(int *)data = sizeof(struct fsnotify_event) +
+			    TAILQ_FIRST(&ss->ss_queue)->eh_event->ev_pathlen + 1;
+		else
+			*(int *)data = 0;
+		mtx_unlock(&ss->ss_mtx);
 		return (0);
 	default:
 		return (ENOIOCTL);
@@ -284,21 +386,24 @@
 static int
 fsnotify_poll(struct cdev *dev, int events, struct thread *td)
 {
-	struct fn_session *ss;
+	struct fnsession *ss;
 	int revents;
 
 	if (devfs_get_cdevpriv((void **)&ss) != 0)
 		return (events &
-		    (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
+		    (POLLHUP | POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM));
 
 	revents = 0;
 
 	if (events & (POLLIN | POLLRDNORM)) {
-		/* TODO */
+		mtx_lock(&ss->ss_mtx);
+		if (!TAILQ_EMPTY(&ss->ss_queue))
+			revents = (events & (POLLIN | POLLRDNORM));
+		mtx_unlock(&ss->ss_mtx);
 	}
 
 	if (revents == 0)
-		selrecord(td, &ss->queue_poll);
+		selrecord(td, &ss->ss_queuepoll);
 
 	return (revents);
 }
@@ -310,10 +415,10 @@
 hook_generic_create(struct vnode *dvp, struct vnode *vp,
     struct componentname *cnp)
 {
-	struct fn_node *pn;
+	struct fnnode *dirnode;
 
-	pn = node_lookup(dvp);
-	if (pn != NULL)
+	dirnode = node_lookup(dvp);
+	if (dirnode != NULL)
 		enqueue_direvent(NULL, cnp, FE_CREATE);
 	return (0);
 }
@@ -322,7 +427,7 @@
 hook_generic_remove(struct vnode *dvp, struct vnode *vp,
     struct componentname *cnp)
 {
-	struct fn_node *pn, *n;
+	struct fnnode *dirnode, *n;
 
 	n = node_lookup(vp);
 	if (n != NULL) {
@@ -333,36 +438,36 @@
 		enqueue_fileevent(NULL, FE_DESTROY);
 	}
 
-	pn = node_lookup(dvp);
-	if (pn != NULL)
+	dirnode = node_lookup(dvp);
+	if (dirnode != NULL)
 		enqueue_direvent(NULL, cnp, FE_REMOVE);
 }
 
 static int
 hook_create(struct vop_create_args *ap)
 {
-	hook_generic_remove(ap->a_dvp, *ap->a_vpp, ap->a_cnp);
+	hook_generic_create(ap->a_dvp, *ap->a_vpp, ap->a_cnp);
 	return (0);
 }
 
 static int
 hook_mkdir(struct vop_mkdir_args *ap)
 {
-	hook_generic_remove(ap->a_dvp, *ap->a_vpp, ap->a_cnp);
+	hook_generic_create(ap->a_dvp, *ap->a_vpp, ap->a_cnp);
 	return (0);
 }
 
 static int
 hook_link(struct vop_link_args *ap)
 {
-	hook_generic_remove(ap->a_tdvp, ap->a_vp, ap->a_cnp);
+	hook_generic_create(ap->a_tdvp, ap->a_vp, ap->a_cnp);
 	return (0);
 }
 
 static int
 hook_symlink(struct vop_symlink_args *ap)
 {
-	hook_generic_remove(ap->a_dvp, *ap->a_vpp, ap->a_cnp);
+	hook_generic_create(ap->a_dvp, *ap->a_vpp, ap->a_cnp);
 	return (0);
 }
 
@@ -386,65 +491,208 @@
 	return (0);
 }
 
-#if 0
-static struct fn_node*
-node_alloc(char *name, int namelen)
+static __inline void
+watch_hold(struct fnwatch *watch)
+{
+	refcount_acquire(&watch->wt_refcnt);
+}
+
+static void
+watch_drop(struct fnwatch *watch)
 {
-	struct fn_node *n;
+	if (refcount_release(&watch->wt_refcnt) != 0) {
+		free(watch, M_FSNOTIFY);
+	}
+}
 
-	n = malloc(sizeof(struct fn_node) + namelen + 1, M_FSNOTIFY,
-	    M_WAITOK | M_ZERO);
+static int
+watch_nextwd(void)
+{
+	static volatile int wd = 1;
+	int nwd;
 
-	n->refcnt = 1;
+again:
+	nwd = atomic_fetchadd_int(&wd, 1);
 
-	/* FIXME 
-	memcpy(n->name, name, namelen);
-	n->namelen = namelen;
-	*/
+	if (nwd <= 0) {
+		if (atomic_cmpset_int(&wd, nwd, 1) == 0)
+			goto again;
+	}
 
-	return n;
+	return (nwd);
 }
-#endif
 
-static __inline struct fn_node *
-node_hold(struct fn_node *node)
+static struct fnnode*
+node_alloc(char *path, int pathlen)
 {
-	return node;
+	struct fnnode *node;
+
+	node = malloc(sizeof(struct fnnode), M_FSNOTIFY, M_WAITOK | M_ZERO);
+	node->nd_path = malloc(pathlen + 1, M_FSNOTIFYPATH, M_WAITOK);
+
+	refcount_init(&node->nd_refcnt, 1);
+
+	memcpy(node->nd_path, path, pathlen);
+	node->nd_path[pathlen] = '\0';
+	node->nd_pathlen = pathlen;
+	node->nd_pathhash = hash32_buf(node->nd_path, pathlen, HASHINIT);
+
+	return (node);
 }
 
 static __inline void
-node_drop(struct fn_node *node)
+node_hold(struct fnnode *node)
+{
+	refcount_acquire(&node->nd_refcnt);
+}
+
+static void
+node_drop(struct fnnode *node)
+{
+	if (refcount_release(&node->nd_refcnt) != 0) {
+		KASSERT(node->nd_watchcount == 0 && node->nd_supermask == 0 &&
+		    TAILQ_EMPTY(&node->nd_watchlist), ("Invalid reference count"));
+		mtx_destroy(&node->nd_mtx);
+		free(node->nd_path, M_FSNOTIFYPATH);
+		free(node, M_FSNOTIFY);
+	}
+}
+
+static __inline struct fnnode_hashhead *
+node_vphashhead(struct vnode *vp)
+{
+	uint32_t h;
+
+	h = hash32_buf(vp, sizeof(vp), HASHINIT);
+	return fnnode_vphashtbl[h & fnnode_hashmask];
+}
+
+static __inline struct fnnode_hashhead *
+node_pathhashhead(char *path, int pathlen)
+{
+	uint32_t h;
+
+	h = hash32_buf(path, pathlen, HASHINIT);
+	return fnnode_vphashtbl[h & fnnode_hashmask];
+}
+
+static void
+node_create(struct fnnode *node, struct vnode *vp)
+{
+	struct fnnode *ni;
+
+	MPASS(vp != NULL);
+	mtx_lock(&fnnode_hashmtx);
+	/* DEBUG */
+	LIST_FOREACH(ni, node_vphashhead(vp), nd_hashentry) {
+		if (ni->nd_vnode == vp) {
+			panic("Node already exists in vnode hash table: %s",
+			    node->nd_path);
+		}
+	}
+	LIST_INSERT_HEAD(node_vphashhead(vp), node, nd_hashentry);
+	mtx_unlock(&fnnode_hashmtx);
+}
+
+static void
+node_destroy(struct fnnode *node)
 {
+	struct fnwatch *watch;
+
+	mtx_lock(&node->nd_mtx);
+	while (!TAILQ_EMPTY(&node->nd_watchlist)) {
+		watch = TAILQ_FIRST(&node->nd_watchlist);
+		TAILQ_REMOVE(&node->nd_watchlist, watch, wt_nodeentry);
+		watch_drop(watch);
+	}
+	mtx_unlock(&node->nd_mtx);
+	mtx_lock(&fnnode_hashmtx);
+	LIST_REMOVE(node, nd_hashentry);
+	mtx_unlock(&fnnode_hashmtx);
+	node_drop(node);
 }
 
-static struct fn_node*
+static struct fnnode*
 node_lookup(struct vnode *vp)
 {
-	return (NULL);
+	struct fnnode *node, *rv;
+	int pathhash, pathlen;
+	char *path;
+
+	rv = NULL;
+	path = NULL;
+	pathhash = pathlen = 0; /* FIXME */
+	/* Node is always on one of the hash tables. */
+	mtx_lock(&fnnode_hashmtx);
+	LIST_FOREACH(node, node_vphashhead(vp), nd_hashentry) {
+		if (node->nd_vnode == vp) {
+			mtx_lock(&node->nd_mtx);
+			rv = (node->nd_watchcount == 0 ? NULL : node);
+			if (rv == NULL)
+				mtx_unlock(&node->nd_mtx);
+			break;
+		}
+	}
+	mtx_unlock(&fnnode_hashmtx);
+
+	if (node == NULL) {
+		/* TODO: lookup full path */
+
+		mtx_lock(&fnnode_hashmtx);
+		LIST_FOREACH(node, node_vphashhead(vp), nd_hashentry) {
+			if (node->nd_pathhash != pathhash ||
+			    node->nd_pathlen != pathlen ||
+			    memcmp(node->nd_path, path, pathlen) != 0)
+				continue;
+			/* add to vphash */
+			mtx_lock(&node->nd_mtx);
+			rv = (node->nd_watchcount == 0 ? NULL : node);
+			if (rv == NULL)
+				mtx_unlock(&node->nd_mtx);
+			break;
+		}
+		mtx_unlock(&fnnode_hashmtx);
+	}
+
+	if (path != NULL)
+		free(path, M_FSNOTIFY);
+
+	return (rv);
 }
 
-
-static struct fn_event *
-event_alloc(struct fn_node *node, int handle_maxsize, int mask, int cookie)
+static struct fnevent *
+event_alloc(struct fnnode *node, char *path, int pathlen, int handle_maxsize,
+    int mask, int cookie)
 {
-	struct fn_event *event;
+	struct fnevent *event;
+	int addslash;
 
 	MPASS(handle_maxsize > 0);
 	MPASS(mask != 0);
+	MPASS(pathlen > 0);
 
-	event = malloc(sizeof(struct fn_event) +
-	    (sizeof(struct fn_eventhandle) * handle_maxsize),
+	event = malloc(sizeof(struct fnevent) +
+	    (sizeof(struct fneventhandle) * handle_maxsize),
 	    M_FSNOTIFY, M_WAITOK | M_ZERO);
-	event->path_pos = sizeof(event->path_buf) - 1;
-	event->handle_maxsize = handle_maxsize;
-	event->node = node;
-	event->mask = mask;
-	event->cookie = cookie;
+	event->ev_handlemaxsize = handle_maxsize;
+	event->ev_node = node;
+	event->ev_mask = mask;
+	event->ev_cookie = cookie;
+	addslash = (path[0] != '/' && node->nd_path[0] != '/' ? 1 : 0);
+	event->ev_pathlen = node->nd_pathlen + addslash + pathlen;
+	event->ev_path = malloc(event->ev_pathlen + 1, M_FSNOTIFYPATH,
+	    M_WAITOK);
+	memcpy(event->ev_path, node->nd_path, node->nd_pathlen);
+	if (addslash != 0)
+		event->ev_path[node->nd_pathlen] = '/';
+	memcpy(event->ev_path + node->nd_pathlen + addslash, path,
+	    pathlen);
+	event->ev_path[event->ev_pathlen] = '\0';
 
 	return (event);
 }
 
-static __inline int
+static int
 event_nextcookie(void)
 {
 	static volatile int cookie = 1;
@@ -452,122 +700,183 @@
 	return atomic_fetchadd_int(&cookie, 1);
 }
 
+static void
+eventhandle_drop(struct fneventhandle *eh)
+{
+	struct fnnode *node;
+
+	node = eh->eh_event->ev_node;
+
+	mtx_lock(&node->nd_mtx);
+	eh->eh_event->ev_handlecount--;
+	if (eh->eh_event->ev_handlecount == 0) {
+		node_drop(node);
+		free(eh->eh_event, M_FSNOTIFY);
+	}
+	mtx_unlock(&node->nd_mtx);
+}
+
 static int
-event_prependpath(struct fn_event *event, char *path, int pathlen, int addslash)
+session_addwatch(struct fnsession *ss, struct fnnode *node, int mask,
+    struct fnwatch **watchpp)
 {
-	int pos;
+	struct fnwatch *watch;
 
-	MPASS(path[pathlen] != '/');
-	MPASS(pathlen > 0);
+	watch = malloc(sizeof(struct fnwatch), M_FSNOTIFY, M_WAITOK | M_ZERO);
 
-	pos = event->path_pos;
+	refcount_init(&watch->wt_refcnt, 2);
 
-	if (addslash != 0)
-		addslash = 1;
+	watch->wt_wd = watch_nextwd();
+	watch->wt_mask = mask;
+	watch->wt_session = ss;
+	node_hold(node);
+	watch->wt_node = node;
 
-	if (pathlen + addslash > pos) {
-		printf("fsnotify: path is too long: %.*s/%s\n", pathlen, path,
-		    event->path_buf);
-		return (ENAMETOOLONG);
-	}
+	mtx_lock(&ss->ss_mtx);
+	TAILQ_INSERT_TAIL(&ss->ss_watchlist, watch, wt_sessionentry);
+	mtx_unlock(&ss->ss_mtx);
 
+	mtx_lock(&node->nd_mtx);
+	TAILQ_INSERT_TAIL(&node->nd_watchlist, watch, wt_nodeentry);
+	mtx_unlock(&node->nd_mtx);
 
-	if (addslash)
-		event->path_buf[pos--] = '/';
+	if (watchpp != NULL)
+		*watchpp = watch;
 
-	pos -= pathlen;
-	memcpy(path, event->path_buf + pos, pathlen);
+	return (0);
+}
 
-	event->path_pos = pos;
+static int
+session_rmwatch(struct fnsession *ss, int wd)
+{
+	struct fnwatch *watch, *tmp;
 
+	mtx_lock(&ss->ss_mtx);
+	TAILQ_FOREACH_SAFE(watch, &ss->ss_watchlist, wt_sessionentry, tmp) {
+		if (watch->wt_wd != wd)
+			continue;
+		TAILQ_REMOVE(&ss->ss_watchlist, watch, wt_sessionentry);
+		watch_drop(watch);
+		watch->wt_session = NULL;
+		break;
+	}
+	mtx_unlock(&ss->ss_mtx);
+	if (watch == NULL)
+		return (ENOENT);
 	return (0);
 }
 
 static void
-session_enqueue(struct fn_eventhandle *eh)
+session_drophandle(struct fnsession *ss, struct fneventhandle *eh)
+{
+	TAILQ_REMOVE(&ss->ss_queue, eh, eh_queueentry);
+	ss->ss_queuesize--;
+
+	eventhandle_drop(eh);
+}
+
+static void
+session_enqueue(struct fneventhandle *eh)
 {
-	struct fn_session *ss;
+	struct fnsession *ss;
+
+	ss = eh->eh_watch->wt_session;
 
-	ss = eh->watch->session;
-	mtx_lock(&ss->queue_mtx);
-	TAILQ_INSERT_TAIL(&ss->queue, eh, sessionqueue_entry);
-	ss->queue_size++;
-	mtx_unlock(&ss->queue_mtx);
-	cv_broadcast(&ss->queue_ready_cv);
-	selwakeup(&ss->queue_poll);
+	if (ss == NULL)
+		return;
 
+	mtx_lock(&ss->ss_mtx);
+	if (eh->eh_watch->wt_session != NULL) {
+		mtx_unlock(&ss->ss_mtx);
+		eventhandle_drop(eh);
+		return;
+	}
+	TAILQ_INSERT_TAIL(&ss->ss_queue, eh, eh_queueentry);
+	ss->ss_queuesize++;
+	mtx_unlock(&ss->ss_mtx);
+	cv_broadcast(&ss->ss_queuecv);
+	selwakeup(&ss->ss_queuepoll);
 }
 
 static void
 process_queue(void *context, int pending)
 {
-	struct fn_node *node;
-	struct fn_watch *watch;
-	struct fn_event *event;
-	struct fn_eventhandle *eh;
+	struct fnnode *node;
+	struct fnwatch *watch;
+	struct fnevent *event;
+	struct fneventhandle *eh;
 	int i, handle_count;
 
 	while (1) {
 		mtx_lock(&fsnotify_queue_mtx);
-		if (!TAILQ_EMPTY(&fsnotify_queue))
+		if (TAILQ_EMPTY(&fsnotify_queue)) {
+			mtx_unlock(&fsnotify_queue_mtx);
 			break;
+		}
 		event = TAILQ_FIRST(&fsnotify_queue);
-		TAILQ_REMOVE(&fsnotify_queue, event, queue_entry);
+		TAILQ_REMOVE(&fsnotify_queue, event, ev_queueentry);
 		mtx_unlock(&fsnotify_queue_mtx);
 
-		node = event->node;
+		node = event->ev_node;
 
 		/* FIXME: lookup node full path */
 
-		mtx_lock(&node->node_mtx);
-		TAILQ_FOREACH(watch, &node->watch_list, node_entry) {
-			if ((watch->mask & event->mask) == 0)
+		mtx_lock(&node->nd_mtx);
+		TAILQ_FOREACH(watch, &node->nd_watchlist, wt_nodeentry) {
+			if ((watch->wt_mask & event->ev_mask) == 0)
 				continue;
-			if (event->handle_count >= event->handle_maxsize) {
+			if (event->ev_handlecount >= event->ev_handlemaxsize) {
 				printf("fsnotify: event handle buffer is too small: %d\n",
-				    event->handle_maxsize);
+				    event->ev_handlemaxsize);
 				break;
 			}
-			eh = &event->handle_buf[event->handle_count++];
-			eh->event = event;
-			eh->watch = watch;
+			eh = &event->ev_handlebuf[event->ev_handlecount++];
+			eh->eh_event = event;
+			eh->eh_watch = watch;
 		}
-		handle_count = event->handle_count;
-		mtx_unlock(&node->node_mtx);
+		handle_count = event->ev_handlecount;
+		mtx_unlock(&node->nd_mtx);
 
 		MPASS(handle_count > 0);
 		for (i = 0; i < handle_count; i++) {
-			session_enqueue(&event->handle_buf[i]);
+			session_enqueue(&event->ev_handlebuf[i]);
 		}
 	}
 }
 
 static void
-enqueue_direvent(struct fn_node *pn, struct componentname *cnp, int mask)
+enqueue_direvent(struct fnnode *dirnode, struct componentname *cnp, int mask)
 {
-	struct fn_event *event;
-	int error;
+	struct fnevent *event;
+	int supermask, watch_count;
 
 	printf("enqueue_direvent: %s %x\n", cnp->cn_nameptr, mask);
 
-	if ((pn->supermask & mask) == 0) {
-		node_drop(pn);
+	mtx_assert(&dirnode->nd_mtx, MA_OWNED);
+	watch_count = dirnode->nd_watchcount;
+	supermask = dirnode->nd_supermask & mask;
+	node_hold(dirnode);
+	mtx_unlock(&dirnode->nd_mtx);
+
+	if (supermask == 0) {
+		node_drop(dirnode);
 		return;
 	}
 
-	event = event_alloc(pn, pn->watch_count + 1, mask, event_nextcookie());
-	error = event_prependpath(event, cnp->cn_nameptr, cnp->cn_namelen, 0);
-	if (error != 0) {
-		node_drop(pn);
-		free(event, M_FSNOTIFY);
-		return;
-	}
+	KASSERT(watch_count > 0, ("No watchers found"));
+
+	event = event_alloc(dirnode, cnp->cn_nameptr, cnp->cn_namelen,
+	    watch_count + 1, mask, event_nextcookie());
+
+	mtx_lock(&fsnotify_queue_mtx);
+	TAILQ_INSERT_TAIL(&fsnotify_queue, event, ev_queueentry);
+	mtx_unlock(&fsnotify_queue_mtx);
 
 	taskqueue_enqueue(fsnotify_tq, &fsnotify_task);
 }
 
 static void
-enqueue_fileevent(struct fn_node *pn, int mask)
+enqueue_fileevent(struct fnnode *node, int mask)
 {
 	printf("enqueue_fileevent: %x\n", mask);
 }

==== //depot/projects/soc2010/ilya_fsnotify/src/sys/sys/fsnotify.h#2 (text+ko) ====

@@ -30,14 +30,6 @@
 #ifndef _SYS_FSNOTIFY_H_
 #define	_SYS_FSNOTIFY_H_
 
-struct fsnotify_event {
-	int32_t		wd;
-	uint32_t	mask;
-	uint32_t	cookie;
-	uint32_t	len;
-	char		name[0];
-};
-
 #define FE_CREATE		0x0001
 #define FE_REMOVE		0x0002
 #define FE_RENAME_FROM		0x0004
@@ -49,9 +41,23 @@
 
 #define FE_DESTROY		0x0080
 
-#define FSNOTIFY_ADDWATCH	_IOW('F', 1, int)
+#define FSNOTIFY_ADDWATCH	_IOWR('F', 1, struct fsnotify_addwatch_args)
 #define FSNOTIFY_RMWATCH	_IOW('F', 2, int)
 
+struct fsnotify_event {
+	int32_t		wd;
+	uint32_t	mask;
+	uint32_t	cookie;
+	uint32_t	len;
+	char		name[0];
+};
+
+struct fsnotify_addwatch_args {
+	int		fd;
+	int32_t		wd;
+	uint32_t	mask;
+};
+
 #ifdef _KERNEL
 
 extern vop_create_t *fsnotify_hook_create;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201006061712.o56HCUqn013200>