Module Name:    src
Committed By:   haad
Date:           Wed Oct 28 23:44:51 UTC 2009

Modified Files:
        src/external/cddl/osnet/dist/uts/common/fs/zfs: zfs_vnops.c zfs_znode.c

Log Message:
Add workaround about zfs vnode reclaiming deadlock by checking if we don't
ehld ZFS_MUTEX_OBJ already. If we can lock OBJ_MUTEX deffer execution of
zfs_zinactive to taskq. Code was inspired by FreeBSD zfs_freebsd_reclaim.

XXX. This needs to be fixed after finding final solution for vnode lifecycle.


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 \
    src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c \
    src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_znode.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c
diff -u src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.2 src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.3
--- src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.2	Fri Aug  7 20:16:45 2009
+++ src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c	Wed Oct 28 23:44:51 2009
@@ -1950,12 +1950,6 @@
 	vnevent_rmdir(vp, dvp, name, ct);
 
 	/*
-	 * Grab a lock on the directory to make sure that noone is
-	 * trying to add (or lookup) entries while we are removing it.
-	 */
-	rw_enter(&zp->z_name_lock, RW_WRITER);
-
-	/*
 	 * Grab a lock on the parent pointer to make sure we play well
 	 * with the treewalk and directory rename code.
 	 */
@@ -4236,13 +4230,36 @@
 	return (0);
 }
 
+/*
+ * Destroy znode from taskq thread without ZFS_OBJ_MUTEX held.
+ */
+static void
+zfs_reclaim_deferred(void *arg, int pending)
+{
+	znode_t *zp = arg;
+	zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+	uint64_t z_id = zp->z_id;
+
+	/*
+	 * Don't allow a zfs_zget() while were trying to release this znode
+	 */
+	ZFS_OBJ_HOLD_ENTER(zfsvfs, z_id);
+
+	/* Don't need to call ZFS_OBJ_HOLD_EXIT zfs_inactive did thatfor us. */
+	zfs_zinactive(zp);
+	
+}
+
 static int
 zfs_netbsd_reclaim(struct vop_reclaim_args *ap)
 {
 	vnode_t	*vp = ap->a_vp;
 	znode_t	*zp = VTOZ(vp);
 	zfsvfs_t *zfsvfs;
+	int locked;
 
+	locked = 0;
+	
 	ASSERT(zp != NULL);
 	KASSERT(!vn_has_cached_data(vp));
 
@@ -4251,13 +4268,11 @@
 	mutex_enter(&zp->z_lock);
 	ASSERT(zp->z_phys);
 
-	dprintf("destroying znode %p -- vnode %p -- zp->z_buf = %p\n", zp, ZTOV(zp), zp->z_dbuf);
-	//cpu_Debugger();
-
-	rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER);
+//	dprintf("destroying znode %p -- vnode %p -- zp->z_buf = %p\n", zp, ZTOV(zp), zp->z_dbuf);
+//	rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER);
 	genfs_node_destroy(vp);
 	cache_purge(vp);
-//	ZTOV(zp) = NULL;
+
 	if (zp->z_dbuf == NULL) {
 		/*
 		 * The fs has been unmounted, or we did a
@@ -4270,8 +4285,34 @@
 	}
 	mutex_exit(&zp->z_lock);
 
-	zfs_zinactive(zp);
-	rw_exit(&zfsvfs->z_teardown_inactive_lock);
+	mutex_enter(&zp->z_lock);
+	if (!zp->z_unlinked) {
+		/*
+		 * XXX Hack because ZFS_OBJ_MUTEX is held we can't call zfs_zinactive
+		 * now. I need to defer zfs_zinactive to another thread which doesn't hold this mutex.
+		 */
+		locked = MUTEX_HELD(ZFS_OBJ_MUTEX(zfsvfs, zp->z_id)) ? 2 :
+		    ZFS_OBJ_HOLD_TRYENTER(zfsvfs, zp->z_id);
+		if (locked == 0) {
+			/*
+			 * Lock can't be obtained due to deadlock possibility,
+			 * so defer znode destruction.
+			 */
+			taskq_dispatch(system_taskq, zfs_reclaim_deferred, zp, 0);
+		} else {
+			zfs_znode_dmu_fini(zp);
+			/* Our LWP is holding ZFS_OBJ_HELD mutex but it was locked before
+			   zfs_zinactive was called therefore we can't release it. */
+			if (locked == 1)
+				ZFS_OBJ_HOLD_EXIT(zfsvfs, zp->z_id);
+			zfs_znode_free(zp);
+		}
+	} else
+		mutex_exit(&zp->z_lock);
+
+	ZTOV(zp) = NULL;
+	vp->v_data = NULL; /* v_data must be NULL for a cleaned vnode. */
+	
 	return (0);
 }
 
Index: src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_znode.c
diff -u src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_znode.c:1.2 src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_znode.c:1.3
--- src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_znode.c:1.2	Fri Aug  7 20:16:45 2009
+++ src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_znode.c	Wed Oct 28 23:44:51 2009
@@ -675,7 +675,7 @@
 	}
 
 	dprintf("zfs_znode_alloc znode %p -- vnode %p\n", zp, vp);
-	dprintf("zfs_znode_alloc Initializing genfs_node at %p\n", vp);
+	dprintf("zfs_znode_alloc z_id %ld\n", zp->z_id);
 	//cpu_Debugger();
 	
 	uvm_vnp_setsize(vp, zp->z_phys->zp_size);
@@ -829,6 +829,9 @@
 
 	pzp->zp_mode = MAKEIMODE(vap->va_type, vap->va_mode);
 	if (!(flag & IS_ROOT_NODE)) {
+		dprintf("zfs_mknode parent vp %p - zp %p\n", ZTOV(dzp), dzp);
+		dprintf("Going to lock %p with %ld\n", ZFS_OBJ_MUTEX(zfsvfs, obj), obj);
+		
 		ZFS_OBJ_HOLD_ENTER(zfsvfs, obj);
 		*zpp = zfs_znode_alloc(zfsvfs, db, 0);
 
@@ -952,7 +955,6 @@
 			err = ENOENT;
 		} else {
 			if ((vp = ZTOV(zp)) != NULL) {
-				
 				mutex_enter(&vp->v_interlock);
 				mutex_exit(&zp->z_lock);
 				if (vget(vp, LK_INTERLOCK) != 0) {
@@ -1057,19 +1059,19 @@
 	zfs_znode_free(zp);
 }
 
+/*
+ * zfs_zinactive must be called with ZFS_OBJ_HOLD_ENTER held. And this lock
+ * will be released in zfs_zinactive.
+ */
 void
 zfs_zinactive(znode_t *zp)
 {
 	vnode_t	*vp = ZTOV(zp);
-	zfsvfs_t *zfsvfs = zp->z_zfsvfs;
-	uint64_t z_id = zp->z_id;
 
 	ASSERT(zp->z_dbuf && zp->z_phys);
 
-	/*
-	 * Don't allow a zfs_zget() while were trying to release this znode
-	 */
-	ZFS_OBJ_HOLD_ENTER(zfsvfs, z_id);
+	//printf("zfs_zinactive vp %p - zp %p\n", vp, zp);
+	//printf("Going to lock %p with %ld\n", ZFS_OBJ_MUTEX(zfsvfs, z_id), z_id);
 
 	mutex_enter(&zp->z_lock);
 	/*
@@ -1079,9 +1081,6 @@
 	if (zp->z_unlinked) {
 		mutex_exit(&zp->z_lock);
 		ZFS_OBJ_HOLD_EXIT(zfsvfs, z_id);
-#ifndef __NetBSD__
-		ASSERT(vp->v_count == 0);
-#endif
 		zfs_rmnode(zp);
 		return;
 	}

Reply via email to