Coda File System

[Greg Troxel] vnode locking in coda_lookup

From: Greg Troxel <gdt_at_ir.bbn.com>
Date: Wed, 04 Apr 2007 19:42:25 -0400
If any of you are running NetBSD-current and feeling brave, the patch
below may be of interest.  If you're one of the 5 people in the world
who understands vnode locking protocols, then I'd appreciate review.



From: Greg Troxel <gdt_at_ir.bbn.com>
To: tech-kern_at_netbsd.org
Subject: vnode locking in coda_lookup
Date: Wed, 04 Apr 2007 19:38:14 -0400
Message-ID: <rmiabxnemmx.fsf_at_fnord.ir.bbn.com>
MIME-Version: 1.0

I have attempted to fix vnode locking in coda's lookup routine.
Basically, coda_lookup (in sys/coda/coda_vnops.c) never followed the
special rules for IS_DOTDOT.  I've read the rules, and looked at
ufs_lookup, but I'm not sure this is right.  It does survive 

  for i in `cd /usr/bin && ls`; do cp -pf /usr/bin/$i ../$i & done
and 
  for i in `cd .. && ls`; do cat ../$i  > /dev/null & done

I have also cleaned up and rewritten comments, some of which probably
date from Mach days.

I know that wrstuden@ in the past said that LK_RETRY was not correct.
ufs_lookup does it, and I'm still unclear on this.  I do have a trusty
Thinkpad 600E crashbox for testing.

So two questions:

  is this correct?

  is this clearly better than what was there?



--- coda_vnops.c.~1.52.~	2007-03-12 14:44:43.000000000 -0400
+++ coda_vnops.c	2007-04-04 19:26:49.000000000 -0400
@@ -885,22 +885,20 @@ coda_lookup(void *v)
 {
 /* true args */
     struct vop_lookup_args *ap = v;
+    /* (locked) vnode of dir in which to do lookup */
     struct vnode *dvp = ap->a_dvp;
     struct cnode *dcp = VTOC(dvp);
+    /* output variable for result */
     struct vnode **vpp = ap->a_vpp;
-    /*
-     * It looks as though ap->a_cnp->ni_cnd->cn_nameptr holds the rest
-     * of the string to xlate, and that we must try to get at least
-     * ap->a_cnp->ni_cnd->cn_namelen of those characters to macth.  I
-     * could be wrong.
-     */
-    struct componentname  *cnp = ap->a_cnp;
+    /* name to lookup */
+    struct componentname *cnp = ap->a_cnp;
     kauth_cred_t cred = cnp->cn_cred;
     struct lwp *l = cnp->cn_lwp;
 /* locals */
     struct cnode *cp;
     const char *nm = cnp->cn_nameptr;
     int len = cnp->cn_namelen;
+    int flags = cnp->cn_flags;
     CodaFid VFid;
     int	vtype;
     int error = 0;
@@ -910,6 +908,17 @@ coda_lookup(void *v)
     CODADEBUG(CODA_LOOKUP, myprintf(("lookup: %s in %s\n",
 				   nm, coda_f2s(&dcp->c_fid))););
 
+    /*
+     * XXX componentname flags in MODMASK are not handled at all */
+     */
+
+    /*
+     * The overall strategy is to switch on the lookup type and get a
+     * result vnode that is vref'd but not locked.  Then, the code at
+     * exit: switches on ., .., and regular lookups and does the right
+     * locking.
+     */
+
     /* Check for lookup of control object. */
     if (IS_CTL_NAME(dvp, nm, len)) {
 	*vpp = coda_ctlvp;
@@ -918,6 +927,7 @@ coda_lookup(void *v)
 	goto exit;
     }
 
+    /* Avoid trying to hand venus an unreasonably long name. */
     if (len+1 > CODA_MAXNAMLEN) {
 	MARK_INT_FAIL(CODA_LOOKUP_STATS);
 	CODADEBUG(CODA_LOOKUP, myprintf(("name too long: lookup, %s (%s)\n",
@@ -926,8 +936,13 @@ coda_lookup(void *v)
 	error = EINVAL;
 	goto exit;
     }
-    /* First try to look the file up in the cfs name cache */
-    /* lock the parent vnode? */
+
+    /*
+     * Try to resolve the lookup in the minicache.  If that fails, ask
+     * venus to do the lookup.  XXX The interaction between vnode
+     * locking and coda locking to avoid deadlocks on .. lookups is
+     * not clear.
+     */
     cp = coda_nc_lookup(dcp, nm, len, cred);
     if (cp) {
 	*vpp = CTOV(cp);
@@ -935,8 +950,7 @@ coda_lookup(void *v)
 	CODADEBUG(CODA_LOOKUP,
 		 myprintf(("lookup result %d vpp %p\n",error,*vpp));)
     } else {
-
-	/* The name wasn't cached, so we need to contact Venus */
+	/* The name wasn't cached, so ask Venus. */
 	error = venus_lookup(vtomi(dvp), &dcp->c_fid, nm, len, cred, l, &VFid, &vtype);
 
 	if (error) {
@@ -952,9 +966,13 @@ coda_lookup(void *v)
 
 	    cp = make_coda_node(&VFid, dvp->v_mount, vtype);
 	    *vpp = CTOV(cp);
+	    /* vpp is now vrefed. */
 
-	    /* enter the new vnode in the Name Cache only if the top bit isn't set */
-	    /* And don't enter a new vnode for an invalid one! */
+	    /*
+	     * Unless this vnode is marked CODA_NOCACHE, enter it into
+	     * the coda name cache to avoid a future venus round-trip.
+	     * XXX Interaction with componentname NOCACHE is unclear.
+	     */
 	    if (!(vtype & CODA_NOCACHE))
 		coda_nc_enter(VTOC(dvp), nm, len, cred, VTOC(*vpp));
 	}
@@ -978,6 +996,11 @@ coda_lookup(void *v)
 	cnp->cn_flags |= SAVENAME;
 	*ap->a_vpp = NULL;
     }
+    /*
+     * XXX If creating and we found something, does that need to be an
+     * error?
+     */
+
 
     /*
      * If we are removing, and we are at the last element, and we
@@ -997,17 +1020,25 @@ coda_lookup(void *v)
     }
 
     /*
-     * If the lookup went well, we need to lock the child.
+     * If the lookup succeeded, we must generally lock the returned
+     * vnode.  This could be a ., .., or normal lookup.  See
+     * vnodeops(9) for the details.
      */
     if (!error || (error == EJUSTRETURN)) {
-	/* The parent is locked, and may be the same as the child */
+	/* Lookup has a value and it isn't "."? */
 	if (*ap->a_vpp && (*ap->a_vpp != dvp)) {
-	    /* Different, go ahead and lock it. */
-	    vn_lock(*ap->a_vpp, LK_EXCLUSIVE|LK_RETRY);
+	    if (flags & ISDOTDOT)
+		/* ..: unlock parent */
+		VOP_UNLOCK(dvp, 0);
+	    /* all but .: lock child */
+	    vn_lock(*ap->a_vpp, LK_EXCLUSIVE | LK_RETRY);
+	    if (flags & ISDOTDOT)
+		/* ..: relock parent */
+	        vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
 	}
+	/* else .: leave dvp locked */
     } else {
-	/* If the lookup failed, we need to ensure that the leaf is NULL */
-	/* Don't change any locking? */
+	/* The lookup failed, so return NULL.  Leave dvp locked. */
 	*ap->a_vpp = NULL;
     }
     return(error);
Received on 2007-04-04 19:45:30