Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 7 Dec 2017 09:05:35 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r326657 - head/sys/ufs/ufs
Message-ID:  <201712070905.vB795ZNl016288@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Thu Dec  7 09:05:34 2017
New Revision: 326657
URL: https://svnweb.freebsd.org/changeset/base/326657

Log:
  Fix livelock in ufsdirhash_create().
  
  When more than one thread enters ufsdirhash_create() for the same
  directory and the inode dirhash is instantiated, but the dirhash' hash
  is not, all of them lock the dirhash shared and then try to upgrade.
  Since there are several threads owning the lock shared, upgrade fails
  and the same attempt is repeated, ad infinitum.
  
  To break the lockstep, lock the dirhash in exclusive mode after the
  failed try-upgrade.
  
  Reported and tested by:	pho
  Sponsored by:	Mellanox Technologies
  MFC after:	1 week

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

Modified: head/sys/ufs/ufs/ufs_dirhash.c
==============================================================================
--- head/sys/ufs/ufs/ufs_dirhash.c	Thu Dec  7 07:55:38 2017	(r326656)
+++ head/sys/ufs/ufs/ufs_dirhash.c	Thu Dec  7 09:05:34 2017	(r326657)
@@ -192,9 +192,11 @@ ufsdirhash_create(struct inode *ip)
 	struct dirhash *ndh;
 	struct dirhash *dh;
 	struct vnode *vp;
+	bool excl;
 
 	ndh = dh = NULL;
 	vp = ip->i_vnode;
+	excl = false;
 	for (;;) {
 		/* Racy check for i_dirhash to prefetch a dirhash structure. */
 		if (ip->i_dirhash == NULL && ndh == NULL) {
@@ -231,8 +233,11 @@ ufsdirhash_create(struct inode *ip)
 		ufsdirhash_hold(dh);
 		VI_UNLOCK(vp);
 
-		/* Acquire a shared lock on existing hashes. */
-		sx_slock(&dh->dh_lock);
+		/* Acquire a lock on existing hashes. */
+		if (excl)
+			sx_xlock(&dh->dh_lock);
+		else
+			sx_slock(&dh->dh_lock);
 
 		/* The hash could've been recycled while we were waiting. */
 		VI_LOCK(vp);
@@ -253,9 +258,10 @@ ufsdirhash_create(struct inode *ip)
 		 * so we can recreate it.  If we fail the upgrade, drop our
 		 * lock and try again.
 		 */
-		if (sx_try_upgrade(&dh->dh_lock))
+		if (excl || sx_try_upgrade(&dh->dh_lock))
 			break;
 		sx_sunlock(&dh->dh_lock);
+		excl = true;
 	}
 	/* Free the preallocated structure if it was not necessary. */
 	if (ndh) {



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