Author: avg
Date: Wed Sep 25 20:00:59 2019
New Revision: 352723
URL: https://svnweb.freebsd.org/changeset/base/352723

Log:
  MFC r352506: fix dsl_scan_ds_clone_swapped logic
  
  PR:           239566

Modified:
  stable/12/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_scan.c
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_scan.c
==============================================================================
--- stable/12/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_scan.c Wed Sep 
25 19:56:23 2019        (r352722)
+++ stable/12/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_scan.c Wed Sep 
25 20:00:59 2019        (r352723)
@@ -2014,16 +2014,17 @@ ds_clone_swapped_bookmark(dsl_dataset_t *ds1, dsl_data
 }
 
 /*
- * Called when a parent dataset and its clone are swapped. If we were
+ * Called when an origin dataset and its clone are swapped.  If we were
  * currently traversing the dataset, we need to switch to traversing the
- * newly promoted parent.
+ * newly promoted clone.
  */
 void
 dsl_scan_ds_clone_swapped(dsl_dataset_t *ds1, dsl_dataset_t *ds2, dmu_tx_t *tx)
 {
        dsl_pool_t *dp = ds1->ds_dir->dd_pool;
        dsl_scan_t *scn = dp->dp_scan;
-       uint64_t mintxg;
+       uint64_t mintxg1, mintxg2;
+       boolean_t ds1_queued, ds2_queued;
 
        if (!dsl_scan_is_running(scn))
                return;
@@ -2031,44 +2032,81 @@ dsl_scan_ds_clone_swapped(dsl_dataset_t *ds1, dsl_data
        ds_clone_swapped_bookmark(ds1, ds2, &scn->scn_phys.scn_bookmark);
        ds_clone_swapped_bookmark(ds1, ds2, &scn->scn_phys_cached.scn_bookmark);
 
-       if (scan_ds_queue_contains(scn, ds1->ds_object, &mintxg)) {
-               scan_ds_queue_remove(scn, ds1->ds_object);
-               scan_ds_queue_insert(scn, ds2->ds_object, mintxg);
+       /*
+        * Handle the in-memory scan queue.
+        */
+       ds1_queued = scan_ds_queue_contains(scn, ds1->ds_object, &mintxg1);
+       ds2_queued = scan_ds_queue_contains(scn, ds2->ds_object, &mintxg2);
+
+       /* Sanity checking. */
+       if (ds1_queued) {
+               ASSERT3U(mintxg1, ==, dsl_dataset_phys(ds1)->ds_prev_snap_txg);
+               ASSERT3U(mintxg1, ==, dsl_dataset_phys(ds2)->ds_prev_snap_txg);
        }
-       if (scan_ds_queue_contains(scn, ds2->ds_object, &mintxg)) {
+       if (ds2_queued) {
+               ASSERT3U(mintxg2, ==, dsl_dataset_phys(ds1)->ds_prev_snap_txg);
+               ASSERT3U(mintxg2, ==, dsl_dataset_phys(ds2)->ds_prev_snap_txg);
+       }
+
+       if (ds1_queued && ds2_queued) {
+               /*
+                * If both are queued, we don't need to do anything.
+                * The swapping code below would not handle this case correctly,
+                * since we can't insert ds2 if it is already there. That's
+                * because scan_ds_queue_insert() prohibits a duplicate insert
+                * and panics.
+                */
+       } else if (ds1_queued) {
+               scan_ds_queue_remove(scn, ds1->ds_object);
+               scan_ds_queue_insert(scn, ds2->ds_object, mintxg1);
+       } else if (ds2_queued) {
                scan_ds_queue_remove(scn, ds2->ds_object);
-               scan_ds_queue_insert(scn, ds1->ds_object, mintxg);
+               scan_ds_queue_insert(scn, ds1->ds_object, mintxg2);
        }
 
-       if (zap_lookup_int_key(dp->dp_meta_objset, scn->scn_phys.scn_queue_obj,
-           ds1->ds_object, &mintxg) == 0) {
-               int err;
-               ASSERT3U(mintxg, ==, dsl_dataset_phys(ds1)->ds_prev_snap_txg);
-               ASSERT3U(mintxg, ==, dsl_dataset_phys(ds2)->ds_prev_snap_txg);
-               VERIFY3U(0, ==, zap_remove_int(dp->dp_meta_objset,
+       /*
+        * Handle the on-disk scan queue.
+        * The on-disk state is an out-of-date version of the in-memory state,
+        * so the in-memory and on-disk values for ds1_queued and ds2_queued may
+        * be different. Therefore we need to apply the swap logic to the
+        * on-disk state independently of the in-memory state.
+        */
+       ds1_queued = zap_lookup_int_key(dp->dp_meta_objset,
+           scn->scn_phys.scn_queue_obj, ds1->ds_object, &mintxg1) == 0;
+       ds2_queued = zap_lookup_int_key(dp->dp_meta_objset,
+           scn->scn_phys.scn_queue_obj, ds2->ds_object, &mintxg2) == 0;
+
+       /* Sanity checking. */
+       if (ds1_queued) {
+               ASSERT3U(mintxg1, ==, dsl_dataset_phys(ds1)->ds_prev_snap_txg);
+               ASSERT3U(mintxg1, ==, dsl_dataset_phys(ds2)->ds_prev_snap_txg);
+       }
+       if (ds2_queued) {
+               ASSERT3U(mintxg2, ==, dsl_dataset_phys(ds1)->ds_prev_snap_txg);
+               ASSERT3U(mintxg2, ==, dsl_dataset_phys(ds2)->ds_prev_snap_txg);
+       }
+
+       if (ds1_queued && ds2_queued) {
+               /*
+                * If both are queued, we don't need to do anything.
+                * Alternatively, we could check for EEXIST from
+                * zap_add_int_key() and back out to the original state, but
+                * that would be more work than checking for this case upfront.
+                */
+       } else if (ds1_queued) {
+               VERIFY3S(0, ==, zap_remove_int(dp->dp_meta_objset,
                    scn->scn_phys.scn_queue_obj, ds1->ds_object, tx));
-               err = zap_add_int_key(dp->dp_meta_objset,
-                   scn->scn_phys.scn_queue_obj, ds2->ds_object, mintxg, tx);
-               VERIFY(err == 0 || err == EEXIST);
-               if (err == EEXIST) {
-                       /* Both were there to begin with */
-                       VERIFY(0 == zap_add_int_key(dp->dp_meta_objset,
-                           scn->scn_phys.scn_queue_obj,
-                           ds1->ds_object, mintxg, tx));
-               }
+               VERIFY3S(0, ==, zap_add_int_key(dp->dp_meta_objset,
+                   scn->scn_phys.scn_queue_obj, ds2->ds_object, mintxg1, tx));
                zfs_dbgmsg("clone_swap ds %llu; in queue; "
                    "replacing with %llu",
                    (u_longlong_t)ds1->ds_object,
                    (u_longlong_t)ds2->ds_object);
-       }
-       if (zap_lookup_int_key(dp->dp_meta_objset, scn->scn_phys.scn_queue_obj,
-           ds2->ds_object, &mintxg) == 0) {
-               ASSERT3U(mintxg, ==, dsl_dataset_phys(ds1)->ds_prev_snap_txg);
-               ASSERT3U(mintxg, ==, dsl_dataset_phys(ds2)->ds_prev_snap_txg);
-               VERIFY3U(0, ==, zap_remove_int(dp->dp_meta_objset,
+       } else if (ds2_queued) {
+               VERIFY3S(0, ==, zap_remove_int(dp->dp_meta_objset,
                    scn->scn_phys.scn_queue_obj, ds2->ds_object, tx));
-               VERIFY(0 == zap_add_int_key(dp->dp_meta_objset,
-                   scn->scn_phys.scn_queue_obj, ds1->ds_object, mintxg, tx));
+               VERIFY3S(0, ==, zap_add_int_key(dp->dp_meta_objset,
+                   scn->scn_phys.scn_queue_obj, ds1->ds_object, mintxg2, tx));
                zfs_dbgmsg("clone_swap ds %llu; in queue; "
                    "replacing with %llu",
                    (u_longlong_t)ds2->ds_object,
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to