diff options
author | Konstantin Belousov <kib@FreeBSD.org> | 2017-07-21 18:42:35 +0000 |
---|---|---|
committer | Konstantin Belousov <kib@FreeBSD.org> | 2017-07-21 18:42:35 +0000 |
commit | 5cf14660ae199f05c39f210a8760ed03e6715095 (patch) | |
tree | b9221fcb0226de3c23e2315fd77e810689c08f94 /sys/ufs/ffs/ffs_snapshot.c | |
parent | c5364714080e88b1b20c93196caae700ff5a3af3 (diff) | |
download | src-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.c | 50 |
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. */ |