diff --git a/sys/ufs/ufs/ufs_dirhash.c b/sys/ufs/ufs/ufs_dirhash.c index 3969f8ed1b41..77bef146d6fd 100644 --- a/sys/ufs/ufs/ufs_dirhash.c +++ b/sys/ufs/ufs/ufs_dirhash.c @@ -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) {