aboutsummaryrefslogtreecommitdiff
path: root/sys/ufs/ffs/ffs_snapshot.c
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2017-07-21 18:42:35 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2017-07-21 18:42:35 +0000
commit5cf14660ae199f05c39f210a8760ed03e6715095 (patch)
treeb9221fcb0226de3c23e2315fd77e810689c08f94 /sys/ufs/ffs/ffs_snapshot.c
parentc5364714080e88b1b20c93196caae700ff5a3af3 (diff)
downloadsrc-5cf14660ae199f05c39f210a8760ed03e6715095.tar.gz
src-5cf14660ae199f05c39f210a8760ed03e6715095.zip
Improve publication of the newly allocated snapdata.
For freshly allocated snapdata, Lock sn_lock in advance, so si_snapdata readers see the locked snapdata and not race. For existing snapdata, if the thread was put to sleep waiting for sn_lock, re-read si_snapdata. This either closes the race or makes the reliance on LK_DRAIN less important. Reported and tested by: pho Sponsored by: The FreeBSD Foundation MFC after: 2 weeks
Notes
Notes: svn path=/head/; revision=321349
Diffstat (limited to 'sys/ufs/ffs/ffs_snapshot.c')
-rw-r--r--sys/ufs/ffs/ffs_snapshot.c50
1 files changed, 32 insertions, 18 deletions
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c
index 66a677330ce2..b4e31258f60d 100644
--- a/sys/ufs/ffs/ffs_snapshot.c
+++ b/sys/ufs/ffs/ffs_snapshot.c
@@ -2638,8 +2638,8 @@ try_free_snapdata(struct vnode *devvp)
static struct snapdata *
ffs_snapdata_acquire(struct vnode *devvp)
{
- struct snapdata *nsn;
- struct snapdata *sn;
+ struct snapdata *nsn, *sn;
+ int error;
/*
* Allocate a free snapdata. This is done before acquiring the
@@ -2647,23 +2647,37 @@ ffs_snapdata_acquire(struct vnode *devvp)
* held.
*/
nsn = ffs_snapdata_alloc();
- /*
- * If there snapshots already exist on this filesystem grab a
- * reference to the shared lock. Otherwise this is the first
- * snapshot on this filesystem and we need to use our
- * pre-allocated snapdata.
- */
- VI_LOCK(devvp);
- if (devvp->v_rdev->si_snapdata == NULL) {
- devvp->v_rdev->si_snapdata = nsn;
- nsn = NULL;
+
+ for (;;) {
+ VI_LOCK(devvp);
+ sn = devvp->v_rdev->si_snapdata;
+ if (sn == NULL) {
+ /*
+ * This is the first snapshot on this
+ * filesystem and we use our pre-allocated
+ * snapdata. Publish sn with the sn_lock
+ * owned by us, to avoid the race.
+ */
+ error = lockmgr(&nsn->sn_lock, LK_EXCLUSIVE |
+ LK_NOWAIT, NULL);
+ if (error != 0)
+ panic("leaked sn, lockmgr error %d", error);
+ sn = devvp->v_rdev->si_snapdata = nsn;
+ VI_UNLOCK(devvp);
+ nsn = NULL;
+ break;
+ }
+
+ /*
+ * There is a snapshots which already exists on this
+ * filesystem, grab a reference to the common lock.
+ */
+ error = lockmgr(&sn->sn_lock, LK_INTERLOCK |
+ LK_EXCLUSIVE | LK_SLEEPFAIL, VI_MTX(devvp));
+ if (error == 0)
+ break;
}
- sn = devvp->v_rdev->si_snapdata;
- /*
- * Acquire the snapshot lock.
- */
- lockmgr(&sn->sn_lock,
- LK_INTERLOCK | LK_EXCLUSIVE | LK_RETRY, VI_MTX(devvp));
+
/*
* Free any unused snapdata.
*/