Author: kib
Date: Fri Jul 21 18:42:35 2017
New Revision: 321349
URL: https://svnweb.freebsd.org/changeset/base/321349

Log:
  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

Modified:
  head/sys/ufs/ffs/ffs_snapshot.c

Modified: head/sys/ufs/ffs/ffs_snapshot.c
==============================================================================
--- head/sys/ufs/ffs/ffs_snapshot.c     Fri Jul 21 18:36:17 2017        
(r321348)
+++ head/sys/ufs/ffs/ffs_snapshot.c     Fri Jul 21 18:42:35 2017        
(r321349)
@@ -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.
         */
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to