PERFORCE change 179568 for review

Ilya Putsikau ilya at FreeBSD.org
Sun Jun 13 10:58:02 UTC 2010


http://p4web.freebsd.org/@@179568?ac=10

Change 179568 by ilya at ilya_triton on 2010/06/13 10:57:15

	- Update node path while processing event, first perform direct lookup for cached path and if it fails try to get full path
	- Add rename hook
	- Perform full path lookup for fd in addwatch
	- Replace full path hash table with inode number hash table.
	- Improve path handling in both fnnode and fnevent.
	- Remove reference counting from fnwatch, there can be max 2 references

Affected files ...

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

Differences ...

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

@@ -34,12 +34,15 @@
 #include <sys/conf.h>
 #include <sys/condvar.h>
 #include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
 #include <sys/filio.h>
 #include <sys/hash.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/module.h>
+#include <sys/mount.h>
 #include <sys/mutex.h>
 #include <sys/namei.h>
 #include <sys/poll.h>
@@ -69,12 +72,14 @@
 	TAILQ_HEAD(, fnwatch)	nd_watchlist;
 	struct mtx		nd_mtx;
 	struct vnode		*nd_vnode;
+	struct mount		*nd_mount;
 	char			*nd_path;
+	char			*nd_pathfree;
+	ino_t			nd_ino;
+	volatile u_int		nd_refcnt;
 	int			nd_pathlen;
-	uint32_t		nd_pathhash;
 	int			nd_watchcount;
 	u_int			nd_supermask;
-	volatile u_int		nd_refcnt;
 };
 
 struct fneventhandle {
@@ -86,8 +91,8 @@
 struct fnevent {
 	TAILQ_ENTRY(fnevent)	ev_queueentry;
 	struct fnnode		*ev_node;
-	char			*ev_path;
-	int			ev_pathlen;
+	char			*ev_pathfree;
+	int			ev_pathpos;
 	int 			ev_mask;
 	int 			ev_cookie;
 	int			ev_handlecount;
@@ -102,7 +107,6 @@
 	struct fnsession	*wt_session;
 	int			wt_mask;
 	int			wt_wd;
-	volatile u_int		wt_refcnt;
 };
 
 
@@ -110,7 +114,6 @@
 
 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;
 
@@ -121,7 +124,7 @@
 static struct taskqueue *fsnotify_tq;
 
 static struct fnnode_hashhead **fnnode_vphashtbl;
-static struct fnnode_hashhead **fnnode_pathhashtbl;
+static struct fnnode_hashhead **fnnode_inohashtbl;
 static struct mtx fnnode_hashmtx;
 static u_long fnnode_hashmask;
 
@@ -149,16 +152,20 @@
 
 static void process_queue(void *context, int pending);
 static void enqueue_direvent(struct fnnode *dirnode, struct componentname *cnp,
-    int mask);
-static void enqueue_fileevent(struct fnnode *dirnode, int mask);
+    int cookie, int mask);
+static void enqueue_fileevent(struct fnnode *dirnode, int cookie, 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 struct fnnode* node_lookupex(struct vnode *vp, ino_t *inop,
+    int vplocked);
+static struct fnnode* node_alloc(struct vnode *vp, ino_t ino);
 static void node_destroy(struct fnnode *node);
+static void event_copypath(struct fnevent *event, char *path, int *pathlen);
+static int event_pathlen(struct fnevent *event);
+static int event_nextcookie(void);
 
 static int
 fsnotify_modevent(struct module *module, int cmd, void *arg)
@@ -185,7 +192,7 @@
 		hashsize = MAX(desiredvnodes / 32, 16);
 		fnnode_vphashtbl = hashinit(hashsize, M_FSNOTIFYHASH,
 		    &fnnode_hashmask);
-		fnnode_pathhashtbl = hashinit(hashsize, M_FSNOTIFYHASH,
+		fnnode_inohashtbl = hashinit(hashsize, M_FSNOTIFYHASH,
 		    &fnnode_hashmask);
 
 		TASK_INIT(&fsnotify_task, 0, process_queue, NULL);
@@ -218,7 +225,7 @@
 		taskqueue_drain(fsnotify_tq, &fsnotify_task);
 		taskqueue_free(fsnotify_tq);
 		free(fnnode_vphashtbl, M_FSNOTIFYHASH);
-		free(fnnode_pathhashtbl, M_FSNOTIFYHASH);
+		free(fnnode_inohashtbl, M_FSNOTIFYHASH);
 		mtx_destroy(&fsnotify_queue_mtx);
 		mtx_destroy(&fnnode_hashmtx);
 		break;
@@ -281,11 +288,13 @@
 static int
 fsnotify_read(struct cdev *dev, struct uio *uio, int flag)
 {
-	struct fsnotify_event *ue;
+	struct fnevent *event;
+	struct fneventhandle *eh;
 	struct fnsession *ss;
-	struct fneventhandle *eh;
+	struct fnwatch *watch;
+	struct fsnotify_event *fe;
 	int len, error;
-	char user_buf[sizeof(struct fsnotify_event) + PATH_MAX];
+	char user_buf[sizeof(struct fsnotify_event) + MAXPATHLEN];
 
 	if (uio->uio_resid == 0)
 		return (0);
@@ -312,13 +321,16 @@
 	}
 
 	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);
+	event = eh->eh_event;
+	watch = eh->eh_watch;
+	fe = (struct fsnotify_event *) user_buf;
+	fe->fe_wd = watch->wt_wd;
+	fe->fe_mask = watch->wt_mask & event->ev_mask;
+	fe->fe_fileno = event->ev_node->nd_ino;
+	fe->fe_cookie = event->ev_cookie;
+	event_copypath(event, fe->fe_name, &fe->fe_namelen);
+	fe->fe_namelen += 1;
+	len = fe->fe_namelen + sizeof(struct fsnotify_event);
 
 	mtx_unlock(&ss->ss_mtx);
 
@@ -341,9 +353,16 @@
 {
 	struct fnsession *ss;
 	struct fnnode *node;
+	struct fnnode *freenode;
+	struct fnevent *event;
 	struct fnwatch *watch;
 	struct fsnotify_addwatch_args *add_args;
+	struct file *fp;
+	struct filedesc *fdp;
 	struct vnode *vp;
+	ino_t ino;
+	char *path, *pathfree;
+	int vfslocked;
 	int error;
 
 	error = devfs_get_cdevpriv((void **)&ss);
@@ -353,34 +372,57 @@
 	switch (cmd) {
 	case FSNOTIFY_ADDWATCH:
 		add_args = (struct fsnotify_addwatch_args *)data;
-		/* TODO: get vnode for fd */
+		fdp = td->td_proc->p_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);
+		FILEDESC_SLOCK(fdp);
+		fp = fget_locked(fdp, add_args->fa_fd);
+		if (fp != NULL && fp->f_type == DTYPE_VNODE) {
+			vp = fp->f_vnode;
+		}
+		FILEDESC_SUNLOCK(fdp);
+		if (vp == NULL)
+			return (EBADF);
+		vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+		node = node_lookupex(vp, &ino, 0);
+		if (node != NULL) {
+			VFS_UNLOCK_GIANT(vfslocked);
+			freenode = NULL;
+		} else {
+			error = vn_fullpath(td, vp, &path, &pathfree);
+			VFS_UNLOCK_GIANT(vfslocked);
+			if (error != 0)
+				return (error);
+			node = node_alloc(vp, ino);
+			node->nd_path = path;
+			node->nd_pathlen = strlen(path);
+			node->nd_pathfree = pathfree;
+			freenode = node;
+		}
+		error = session_addwatch(ss, node, add_args->fa_mask, &watch);
+		if (error == 0) {
+			add_args->fa_wd = watch->wt_wd;
+		} else if (freenode != NULL) {
+			node_destroy(node);
 		}
-		error = session_addwatch(ss, node, add_args->mask, &watch);
-		if (error == 0)
-			add_args->wd = watch->wt_wd;
-		return (error);
+		break;
 	case FSNOTIFY_RMWATCH:
 		error = session_rmwatch(ss, *(int *)data);
-		return (error);
+		break;
 	case FIONREAD:
 		mtx_lock(&ss->ss_mtx);
-		if (!TAILQ_EMPTY(&ss->ss_queue))
+		if (!TAILQ_EMPTY(&ss->ss_queue)) {
+			event = TAILQ_FIRST(&ss->ss_queue)->eh_event;
 			*(int *)data = sizeof(struct fsnotify_event) +
-			    TAILQ_FIRST(&ss->ss_queue)->eh_event->ev_pathlen + 1;
-		else
+			    event_pathlen(event) + 1;
+		} else
 			*(int *)data = 0;
 		mtx_unlock(&ss->ss_mtx);
-		return (0);
+		break;
 	default:
-		return (ENOIOCTL);
+		error = ENOENT;
 	}
+
+	return (error);
 }
 
 static int
@@ -419,7 +461,7 @@
 
 	dirnode = node_lookup(dvp);
 	if (dirnode != NULL)
-		enqueue_direvent(NULL, cnp, FE_CREATE);
+		enqueue_direvent(dirnode, cnp, event_nextcookie(), FE_CREATE);
 	return (0);
 }
 
@@ -427,20 +469,18 @@
 hook_generic_remove(struct vnode *dvp, struct vnode *vp,
     struct componentname *cnp)
 {
-	struct fnnode *dirnode, *n;
+	struct fnnode *dirnode, *node;
+	int cookie;
+
+	cookie = event_nextcookie();
 
-	n = node_lookup(vp);
-	if (n != NULL) {
-		/* TODO */
-		/*
-		node_remove(n);
-		*/
-		enqueue_fileevent(NULL, FE_DESTROY);
-	}
+	node = node_lookup(vp);
+	if (node != NULL)
+		enqueue_fileevent(node, cookie, FE_DESTROY | FE_REMOVE);
 
 	dirnode = node_lookup(dvp);
 	if (dirnode != NULL)
-		enqueue_direvent(NULL, cnp, FE_REMOVE);
+		enqueue_direvent(dirnode, cnp, cookie, FE_REMOVE);
 }
 
 static int
@@ -488,21 +528,38 @@
 static int
 hook_rename(struct vop_rename_args *ap)
 {
+	struct fnnode *fdirnode, *fnode, *tdirnode, *tnode;
+	int cookie;
+
+	cookie = event_nextcookie();
+	if (ap->a_tvp != NULL) {
+		tnode = node_lookup(ap->a_tvp);
+		if (tnode != NULL) {
+			enqueue_fileevent(tnode, cookie,
+			    FE_DESTROY | FE_REMOVE);
+		}
+	}
+	fnode = node_lookupex(ap->a_fvp, NULL, 0);
+	if (fnode != NULL) {
+		/* TODO */
+		/* mark path stale */
+	}
+
+	fdirnode = node_lookupex(ap->a_fdvp, NULL, 0);
+	if (fdirnode != NULL)
+		enqueue_direvent(fdirnode, ap->a_fcnp, cookie, FE_RENAME_FROM);
+	tdirnode = node_lookup(ap->a_tdvp);
+	if (tdirnode != NULL)
+		enqueue_direvent(tdirnode, ap->a_tcnp, cookie, FE_RENAME_TO);
+
 	return (0);
 }
 
-static __inline void
-watch_hold(struct fnwatch *watch)
-{
-	refcount_acquire(&watch->wt_refcnt);
-}
-
 static void
-watch_drop(struct fnwatch *watch)
+watch_tryfree(struct fnwatch *watch)
 {
-	if (refcount_release(&watch->wt_refcnt) != 0) {
+	if (watch->wt_session == NULL && watch->wt_node == NULL)
 		free(watch, M_FSNOTIFY);
-	}
 }
 
 static int
@@ -522,24 +579,6 @@
 	return (nwd);
 }
 
-static struct fnnode*
-node_alloc(char *path, int pathlen)
-{
-	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_hold(struct fnnode *node)
 {
@@ -553,7 +592,7 @@
 		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->nd_path, M_TEMP);
 		free(node, M_FSNOTIFY);
 	}
 }
@@ -564,47 +603,65 @@
 	uint32_t h;
 
 	h = hash32_buf(vp, sizeof(vp), HASHINIT);
+	h += vp->v_mount->mnt_hashseed;
 	return fnnode_vphashtbl[h & fnnode_hashmask];
 }
 
 static __inline struct fnnode_hashhead *
-node_pathhashhead(char *path, int pathlen)
+node_inohashhead(struct mount *mnt, ino_t ino)
 {
 	uint32_t h;
 
-	h = hash32_buf(path, pathlen, HASHINIT);
-	return fnnode_vphashtbl[h & fnnode_hashmask];
+	h = ino + mnt->mnt_hashseed;
+	return fnnode_inohashtbl[h & fnnode_hashmask];
 }
 
-static void
-node_create(struct fnnode *node, struct vnode *vp)
+static struct fnnode *
+node_alloc(struct vnode *vp, ino_t ino)
 {
-	struct fnnode *ni;
+	struct fnnode *node;
 
 	MPASS(vp != NULL);
+
+	node = malloc(sizeof(struct fnnode), M_FSNOTIFY, M_WAITOK | M_ZERO);
+
+	refcount_init(&node->nd_refcnt, 1);
+
 	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_FOREACH(node, node_vphashhead(vp), nd_hashentry) {
+		if (node->nd_vnode == vp) {
+			panic("Node already exists in vnode hash table: %p",
+			    node->nd_vnode);
 		}
 	}
 	LIST_INSERT_HEAD(node_vphashhead(vp), node, nd_hashentry);
 	mtx_unlock(&fnnode_hashmtx);
+
+	return (node);
 }
 
 static void
-node_destroy(struct fnnode *node)
+node_detachwatches(struct fnnode *node)
 {
 	struct fnwatch *watch;
 
-	mtx_lock(&node->nd_mtx);
+	mtx_assert(&node->nd_mtx, MA_OWNED);
+	node->nd_watchcount = 0;
+	node->nd_supermask = 0;
 	while (!TAILQ_EMPTY(&node->nd_watchlist)) {
 		watch = TAILQ_FIRST(&node->nd_watchlist);
 		TAILQ_REMOVE(&node->nd_watchlist, watch, wt_nodeentry);
-		watch_drop(watch);
+		watch->wt_node = NULL;
+		watch_tryfree(watch);
 	}
+}
+
+static void
+node_destroy(struct fnnode *node)
+{
+	mtx_lock(&node->nd_mtx);
+	node_detachwatches(node);
 	mtx_unlock(&node->nd_mtx);
 	mtx_lock(&fnnode_hashmtx);
 	LIST_REMOVE(node, nd_hashentry);
@@ -612,16 +669,36 @@
 	node_drop(node);
 }
 
+static int
+node_getino(struct vnode *vp, ino_t *inop, int vplocked)
+{
+	struct vattr va;
+	int error;
+
+	if (vplocked == 0) {
+		error = vn_lock(vp, LK_SHARED);
+		if (error != 0)
+			return (error);
+	}
+
+	error = VOP_GETATTR(vp, &va, thread0.td_ucred);
+	if (error == 0)
+		*inop = va.va_fileid;
+
+	if (vplocked == 0)
+		VOP_UNLOCK(vp, 0);
+
+	return (error);
+}
+
 static struct fnnode*
-node_lookup(struct vnode *vp)
+node_lookupex(struct vnode *vp, ino_t *inop, int vplocked)
 {
 	struct fnnode *node, *rv;
-	int pathhash, pathlen;
-	char *path;
+	ino_t ino;
+	int error;
 
 	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) {
@@ -636,15 +713,19 @@
 	mtx_unlock(&fnnode_hashmtx);
 
 	if (node == NULL) {
-		/* TODO: lookup full path */
+		if (inop == NULL)
+			inop = &ino;
+		error = node_getino(vp, inop, vplocked);
+		if (error != 0)
+			goto done;
 
 		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)
+		LIST_FOREACH(node,
+		    node_inohashhead(vp->v_mount, *inop), nd_hashentry) {
+			if (node->nd_ino != *inop ||
+			    node->nd_mount != vp->v_mount)
 				continue;
-			/* add to vphash */
+			/* add to inohash */
 			mtx_lock(&node->nd_mtx);
 			rv = (node->nd_watchcount == 0 ? NULL : node);
 			if (rv == NULL)
@@ -654,22 +735,89 @@
 		mtx_unlock(&fnnode_hashmtx);
 	}
 
-	if (path != NULL)
-		free(path, M_FSNOTIFY);
+done:
+	return (rv);
+}
+
+static __inline struct fnnode*
+node_lookup(struct vnode *vp)
+{
+	ASSERT_VOP_LOCKED(vp, "fsnotify node lookup");
+
+	return (node_lookupex(vp, NULL, 1));
+}
+
+static int
+node_updatepath(struct fnnode *node)
+{
+	struct nameidata ni;
+	struct vnode *vp;
+	char *path, *pathfree;
+	char *npath, *npathfree;
+	int vfslocked, error;
+
+	/*
+	 * TODO: has races. should be executed only in *single* process_queue
+	 * thread
+	 */
+	vp = node->nd_vnode;
+	if ((vp->v_iflag & VI_DOOMED) != 0)
+		return (ENOENT);
+
+	path = node->nd_path;
+	pathfree = node->nd_pathfree;
+	npath = npathfree = NULL;
+	vhold(vp);
+	mtx_unlock(&node->nd_mtx);
+
+	NDINIT(&ni, LOOKUP, MPSAFE | FOLLOW, UIO_SYSSPACE, path, curthread);
+	error = namei(&ni);
+	vfslocked = NDHASGIANT(&ni);
+	if (error == 0) {
+		if (vp != ni.ni_vp) {
+			printf("fsnotify: vnode was replaced between lookups: %s\n",
+			    path);
+			error = ENOENT;
+		}
+		NDFREE(&ni, 0);
+	}
+	if (error != 0) {
+		error = vn_fullpath(curthread, vp, &npath, &npathfree);
+	}
 
-	return (rv);
+	VFS_UNLOCK_GIANT(vfslocked);
+	if ((vp->v_iflag & VI_DOOMED) != 0) {
+		printf("fsnotify: vnode is doomed: %s\n", path);
+		error = ENOENT;
+	}
+	vdrop(vp);
+	mtx_lock(&node->nd_mtx);
+	if (path != node->nd_path) {
+		/* Lookup race */
+		free(pathfree, M_TEMP);
+		MPASS(node->nd_path != NULL);
+		error = 0;
+	} else if (error == 0 && npath != NULL) {
+		free(node->nd_pathfree, M_TEMP);
+		node->nd_path = npath;
+		node->nd_pathlen = strlen(npath);
+		node->nd_pathfree = npathfree;
+		npathfree = NULL;
+	}
+	if (npathfree != NULL)
+		free(npathfree, M_TEMP);
+	return (error);
 }
 
 static struct fnevent *
-event_alloc(struct fnnode *node, char *path, int pathlen, int handle_maxsize,
+event_alloc(struct fnnode *node, char *name, int namelen, int handle_maxsize,
     int mask, int cookie)
 {
 	struct fnevent *event;
-	int addslash;
 
 	MPASS(handle_maxsize > 0);
 	MPASS(mask != 0);
-	MPASS(pathlen > 0);
+	MPASS(namelen > 0);
 
 	event = malloc(sizeof(struct fnevent) +
 	    (sizeof(struct fneventhandle) * handle_maxsize),
@@ -678,42 +826,78 @@
 	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';
+	event->ev_pathfree = uma_zalloc(namei_zone, M_WAITOK);
+	event->ev_pathpos = MAXPATHLEN - 1 - namelen;
+	memcpy(event->ev_pathfree + event->ev_pathpos, name, namelen);
+	event->ev_pathfree[MAXPATHLEN - 1] = '\0';
 
 	return (event);
 }
 
-static int
-event_nextcookie(void)
+static void
+event_free(struct fnevent *event)
+{
+	node_drop(event->ev_node);
+	uma_zfree(namei_zone, event->ev_pathfree);
+	free(event, M_FSNOTIFY);
+}
+
+static __inline int
+event_pathlen(struct fnevent *event)
 {
-	static volatile int cookie = 1;
+	return (MAXPATHLEN - 1 - event->ev_pathpos);
+}
 
-	return atomic_fetchadd_int(&cookie, 1);
+static __inline void
+event_copypath(struct fnevent *event, char *path, int *pathlen)
+{
+	*pathlen = event_pathlen(event);
+	memcpy(path, event->ev_pathfree + *pathlen, *pathlen);
 }
 
 static void
 eventhandle_drop(struct fneventhandle *eh)
 {
 	struct fnnode *node;
+	int handlecount;
 
 	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);
-	}
+	handlecount = --eh->eh_event->ev_handlecount;
 	mtx_unlock(&node->nd_mtx);
+	MPASS(handlecount >= 0);
+	if (handlecount == 0)
+		event_free(eh->eh_event);
+}
+
+static void
+event_prependpath(struct fnevent *event, struct fnnode *node)
+{
+	int pos, len;
+
+	pos = event->ev_pathpos;
+	len = node->nd_pathlen;
+	MPASS(len > 0 && node->nd_path[len - 1] != '/');
+	MPASS(MAXPATHLEN - pos < len + 1);
+
+	event->ev_pathfree[--pos] = '/';
+	pos -= len;
+	memcpy(event->ev_pathfree + pos, node->nd_path, len);
+
+	event->ev_pathpos = pos;
+}
+
+static int
+event_nextcookie(void)
+{
+	static volatile int cookie = 1;
+	int r;
+
+	r = atomic_fetchadd_int(&cookie, 1);
+	if (r == 0)
+		r = atomic_fetchadd_int(&cookie, 1);
+	return (r);
 }
 
 static int
@@ -724,8 +908,6 @@
 
 	watch = malloc(sizeof(struct fnwatch), M_FSNOTIFY, M_WAITOK | M_ZERO);
 
-	refcount_init(&watch->wt_refcnt, 2);
-
 	watch->wt_wd = watch_nextwd();
 	watch->wt_mask = mask;
 	watch->wt_session = ss;
@@ -738,6 +920,8 @@
 
 	mtx_lock(&node->nd_mtx);
 	TAILQ_INSERT_TAIL(&node->nd_watchlist, watch, wt_nodeentry);
+	node->nd_watchcount++;
+	node->nd_supermask |= watch->wt_mask;
 	mtx_unlock(&node->nd_mtx);
 
 	if (watchpp != NULL)
@@ -756,8 +940,8 @@
 		if (watch->wt_wd != wd)
 			continue;
 		TAILQ_REMOVE(&ss->ss_watchlist, watch, wt_sessionentry);
-		watch_drop(watch);
 		watch->wt_session = NULL;
+		watch_tryfree(watch);
 		break;
 	}
 	mtx_unlock(&ss->ss_mtx);
@@ -805,6 +989,7 @@
 	struct fnwatch *watch;
 	struct fnevent *event;
 	struct fneventhandle *eh;
+	struct vnode *vp;
 	int i, handle_count;
 
 	while (1) {
@@ -819,8 +1004,6 @@
 
 		node = event->ev_node;
 
-		/* FIXME: lookup node full path */
-
 		mtx_lock(&node->nd_mtx);
 		TAILQ_FOREACH(watch, &node->nd_watchlist, wt_nodeentry) {
 			if ((watch->wt_mask & event->ev_mask) == 0)
@@ -835,17 +1018,31 @@
 			eh->eh_watch = watch;
 		}
 		handle_count = event->ev_handlecount;
+		if (handle_count == 0) {
+			node->nd_supermask &= ~event->ev_mask;
+			mtx_unlock(&node->nd_mtx);
+			event_free(event);
+			continue;
+		}
+		vp = node->nd_vnode;
+		if (vp != NULL)
+			node_updatepath(node);
+		else
+			printf("fsnotify: vnode not found, reusing cached path: %s\n",
+			    node->nd_path);
+		if (event->ev_mask & FE_DESTROY)
+			node_detachwatches(node);
+
+		event_prependpath(event, node);
 		mtx_unlock(&node->nd_mtx);
 
-		MPASS(handle_count > 0);
-		for (i = 0; i < handle_count; i++) {
+		for (i = 0; i < handle_count; i++)
 			session_enqueue(&event->ev_handlebuf[i]);
-		}
 	}
 }
 
 static void
-enqueue_direvent(struct fnnode *dirnode, struct componentname *cnp, int mask)
+enqueue_direvent(struct fnnode *dirnode, struct componentname *cnp, int cookie, int mask)
 {
 	struct fnevent *event;
 	int supermask, watch_count;
@@ -866,7 +1063,7 @@
 	KASSERT(watch_count > 0, ("No watchers found"));
 
 	event = event_alloc(dirnode, cnp->cn_nameptr, cnp->cn_namelen,
-	    watch_count + 1, mask, event_nextcookie());
+	    watch_count + 1, mask, cookie);
 
 	mtx_lock(&fsnotify_queue_mtx);
 	TAILQ_INSERT_TAIL(&fsnotify_queue, event, ev_queueentry);
@@ -876,7 +1073,7 @@
 }
 
 static void
-enqueue_fileevent(struct fnnode *node, int mask)
+enqueue_fileevent(struct fnnode *node, int cookie, int mask)
 {
 	printf("enqueue_fileevent: %x\n", mask);
 }

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

@@ -45,17 +45,18 @@
 #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];
+	int32_t		fe_wd;
+	uint32_t	fe_mask;
+	uint32_t	fe_cookie;
+	uint32_t	fe_namelen;
+	ino_t		fe_fileno;
+	char		fe_name[0];
 };
 
 struct fsnotify_addwatch_args {
-	int		fd;
-	int32_t		wd;
-	uint32_t	mask;
+	int		fa_fd;
+	uint32_t	fa_mask;
+	int32_t		fa_wd;
 };
 
 #ifdef _KERNEL


More information about the p4-projects mailing list