Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 7 Mar 2011 18:33:29 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r219384 - head/sys/ufs/ufs
Message-ID:  <201103071833.p27IXTKs043706@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Mon Mar  7 18:33:29 2011
New Revision: 219384
URL: http://svn.freebsd.org/changeset/base/219384

Log:
  The UFS dirhash code was attempting to update shared state in the dirhash
  from multiple threads while holding a shared lock during a lookup operation.
  This could result in incorrect ENOENT failures which could then be
  permanently stored in the name cache.
  
  Specifically, the dirhash code optimizes the case that a single thread is
  walking a directory sequentially opening (or stat'ing) each file.  It uses
  state in the dirhash structure to determine if a given lookup is using the
  optimization.  If the optimization fails, it disables it and restarts the
  lookup.  The problem arises when two threads both attempt the optimization
  and fail.  The first thread will restart the loop, but the second thread
  will incorrectly think that it did not try the optimization and will only
  examine a subset of the directory entires in its hash chain.  As a result,
  it may fail to find its directory entry and incorrectly fail with ENOENT.
  
  To make this safe for use with shared locks, simplify the state stored in
  the dirhash and move some of the state (the part that determines if the
  current thread is trying the optimization) into a local variable.  One
  result is that we will now try the optimization more often.  We still
  update the value under the shared lock, but it is a single atomic store
  similar to i_diroff that is stored in UFS directory i-nodes for the
  non-dirhash lookup.
  
  Reviewed by:	kib
  MFC after:	1 week

Modified:
  head/sys/ufs/ufs/dirhash.h
  head/sys/ufs/ufs/ufs_dirhash.c

Modified: head/sys/ufs/ufs/dirhash.h
==============================================================================
--- head/sys/ufs/ufs/dirhash.h	Mon Mar  7 18:01:58 2011	(r219383)
+++ head/sys/ufs/ufs/dirhash.h	Mon Mar  7 18:33:29 2011	(r219384)
@@ -98,7 +98,6 @@ struct dirhash {
 	int	dh_dirblks;	/* number of DIRBLKSIZ blocks in dir */
 	int	dh_firstfree[DH_NFSTATS + 1]; /* first blk with N words free */
 
-	int	dh_seqopt;	/* sequential access optimisation enabled */
 	doff_t	dh_seqoff;	/* sequential access optimisation offset */
 
 	int	dh_score;	/* access count for this dirhash */

Modified: head/sys/ufs/ufs/ufs_dirhash.c
==============================================================================
--- head/sys/ufs/ufs/ufs_dirhash.c	Mon Mar  7 18:01:58 2011	(r219383)
+++ head/sys/ufs/ufs/ufs_dirhash.c	Mon Mar  7 18:33:29 2011	(r219384)
@@ -402,8 +402,7 @@ ufsdirhash_build(struct inode *ip)
 		dh->dh_firstfree[i] = -1;
 	dh->dh_firstfree[DH_NFSTATS] = 0;
 	dh->dh_hused = 0;
-	dh->dh_seqopt = 0;
-	dh->dh_seqoff = 0;
+	dh->dh_seqoff = -1;
 	dh->dh_score = DH_SCOREINIT;
 	dh->dh_lastused = time_second;
 
@@ -551,7 +550,7 @@ ufsdirhash_lookup(struct inode *ip, char
 	struct direct *dp;
 	struct vnode *vp;
 	struct buf *bp;
-	doff_t blkoff, bmask, offset, prevoff;
+	doff_t blkoff, bmask, offset, prevoff, seqoff;
 	int i, slot;
 	int error;
 
@@ -591,29 +590,30 @@ ufsdirhash_lookup(struct inode *ip, char
 	bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
 	blkoff = -1;
 	bp = NULL;
+	seqoff = dh->dh_seqoff;
 restart:
 	slot = ufsdirhash_hash(dh, name, namelen);
 
-	if (dh->dh_seqopt) {
+	if (seqoff != -1) {
 		/*
-		 * Sequential access optimisation. dh_seqoff contains the
+		 * Sequential access optimisation. seqoff contains the
 		 * offset of the directory entry immediately following
 		 * the last entry that was looked up. Check if this offset
 		 * appears in the hash chain for the name we are looking for.
 		 */
 		for (i = slot; (offset = DH_ENTRY(dh, i)) != DIRHASH_EMPTY;
 		    i = WRAPINCR(i, dh->dh_hlen))
-			if (offset == dh->dh_seqoff)
+			if (offset == seqoff)
 				break;
-		if (offset == dh->dh_seqoff) {
+		if (offset == seqoff) {
 			/*
 			 * We found an entry with the expected offset. This
 			 * is probably the entry we want, but if not, the
-			 * code below will turn off seqopt and retry.
+			 * code below will retry.
 			 */ 
 			slot = i;
-		} else 
-			dh->dh_seqopt = 0;
+		} else
+			seqoff = -1;
 	}
 
 	for (; (offset = DH_ENTRY(dh, slot)) != DIRHASH_EMPTY;
@@ -655,9 +655,7 @@ restart:
 				*prevoffp = prevoff;
 			}
 
-			/* Check for sequential access, and update offset. */
-			if (dh->dh_seqopt == 0 && dh->dh_seqoff == offset)
-				dh->dh_seqopt = 1;
+			/* Update offset. */
 			dh->dh_seqoff = offset + DIRSIZ(0, dp);
 			*bpp = bp;
 			*offp = offset;
@@ -666,11 +664,11 @@ restart:
 		}
 
 		/*
-		 * When the name doesn't match in the seqopt case, go back
-		 * and search normally.
+		 * When the name doesn't match in the sequential
+		 * optimization case, go back and search normally.
 		 */
-		if (dh->dh_seqopt) {
-			dh->dh_seqopt = 0;
+		if (seqoff != -1) {
+			seqoff = -1;
 			goto restart;
 		}
 	}



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