hammer2 - error handling 1/N (chain_scan)
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 31 Aug 2017 01:46:14 +0000 (18:46 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 31 Aug 2017 05:19:01 +0000 (22:19 -0700)
* Make HAMMER2_ERROR_* bit flags so they can be OR'd together.

* Change hammer2_chain_scan() to return an error code instead of the
  passed-in bref pointer.

* Adjust the vfsops recovery scan and bulkfree to the new API.

sys/vfs/hammer2/hammer2.h
sys/vfs/hammer2/hammer2_bulkfree.c
sys/vfs/hammer2/hammer2_chain.c
sys/vfs/hammer2/hammer2_ioctl.c
sys/vfs/hammer2/hammer2_subr.c
sys/vfs/hammer2/hammer2_vfsops.c

index d07e0e2..7674a0b 100644 (file)
@@ -380,6 +380,9 @@ RB_PROTOTYPE(hammer2_chain_tree, hammer2_chain, rbnode, hammer2_chain_cmp);
  * I/O otherwise.  If set for a cluster it generally means that the cluster
  * code could not find a valid copy to present.
  *
+ * All H2 error codes are flags and can be accumulated by ORing them
+ * together.
+ *
  * IO          - An I/O error occurred
  * CHECK       - I/O succeeded but did not match the check code
  * INCOMPLETE  - A cluster is not complete enough to use, or
@@ -391,11 +394,15 @@ RB_PROTOTYPE(hammer2_chain_tree, hammer2_chain, rbnode, hammer2_chain_cmp);
  * NOTE: Chain's data field is usually NULL on an IO error but not necessarily
  *      NULL on other errors.  Check chain->error, not chain->data.
  */
-#define HAMMER2_ERROR_NONE             0
-#define HAMMER2_ERROR_IO               1       /* device I/O error */
-#define HAMMER2_ERROR_CHECK            2       /* check code mismatch */
-#define HAMMER2_ERROR_INCOMPLETE       3       /* incomplete cluster */
-#define HAMMER2_ERROR_DEPTH            4       /* temporary depth limit */
+#define HAMMER2_ERROR_NONE             0       /* no error (must be 0) */
+#define HAMMER2_ERROR_IO               0x0001  /* device I/O error */
+#define HAMMER2_ERROR_CHECK            0x0002  /* check code mismatch */
+#define HAMMER2_ERROR_INCOMPLETE       0x0004  /* incomplete cluster */
+#define HAMMER2_ERROR_DEPTH            0x0008  /* temporary depth limit */
+#define HAMMER2_ERROR_BADBREF          0x0010  /* temporary depth limit */
+
+#define HAMMER2_ERROR_ABORTED          0x1000  /* aborted operation */
+#define HAMMER2_ERROR_EOF              0x2000  /* non-error end of scan */
 
 /*
  * Flags passed to hammer2_chain_lookup() and hammer2_chain_next()
@@ -1181,11 +1188,6 @@ TAILQ_HEAD(hammer2_pfslist, hammer2_pfs);
  */
 #define HAMMER2_CHECK_NULL     0x00000001
 
-/*
- * Bulkscan
- */
-#define HAMMER2_BULK_ABORT     0x00000001
-
 /*
  * Misc
  */
@@ -1270,6 +1272,23 @@ hammer2_dedup_mask(hammer2_io_t *dio, hammer2_off_t data_off, u_int bytes)
        return mask;
 }
 
+static __inline
+int
+hammer2_error_to_errno(int error)
+{
+       if (error) {
+               if (error & HAMMER2_ERROR_IO)
+                       error = EIO;
+               else if (error & HAMMER2_ERROR_CHECK)
+                       error = EDOM;
+               else if (error & HAMMER2_ERROR_ABORTED)
+                       error = EINTR;
+               else
+                       error = EDOM;
+       }
+       return error;
+}
+
 extern struct vop_ops hammer2_vnode_vops;
 extern struct vop_ops hammer2_spec_vops;
 extern struct vop_ops hammer2_fifo_vops;
@@ -1439,7 +1458,7 @@ hammer2_chain_t *hammer2_chain_next(hammer2_chain_t **parentp,
                                hammer2_key_t *key_nextp,
                                hammer2_key_t key_beg, hammer2_key_t key_end,
                                int *cache_indexp, int flags);
-hammer2_blockref_t *hammer2_chain_scan(hammer2_chain_t *parent,
+int hammer2_chain_scan(hammer2_chain_t *parent,
                                hammer2_chain_t **chainp,
                                hammer2_blockref_t *bref,
                                int *firstp, int *cache_indexp, int flags);
index 489b686..138df8c 100644 (file)
@@ -104,14 +104,17 @@ hammer2_bulk_scan(hammer2_chain_t *parent,
        hammer2_blockref_t bref;
        hammer2_chain_t *chain;
        int cache_index = -1;
-       int doabort = 0;
        int first = 1;
+       int rup_error;
+       int error;
 
        ++info->pri;
 
        hammer2_chain_lock(parent, HAMMER2_RESOLVE_ALWAYS |
                                   HAMMER2_RESOLVE_SHARED);
        chain = NULL;
+       rup_error = 0;
+       error = 0;
 
        /*
         * Generally loop on the contents if we have not been flagged
@@ -121,31 +124,30 @@ hammer2_bulk_scan(hammer2_chain_t *parent,
         * the frontend, so we can release locks temporarily without
         * imploding.
         */
-       while ((doabort & HAMMER2_BULK_ABORT) == 0 &&
-               hammer2_chain_scan(parent, &chain, &bref, &first,
-                                  &cache_index,
-                                  HAMMER2_LOOKUP_NODATA |
-                                  HAMMER2_LOOKUP_SHARED) != NULL) {
+       for (;;) {
+               error |= hammer2_chain_scan(parent, &chain, &bref, &first,
+                                           &cache_index,
+                                           HAMMER2_LOOKUP_NODATA |
+                                           HAMMER2_LOOKUP_SHARED);
+
+               /*
+                * Handle EOF or other error at current level.  This stops
+                * the bulkfree scan.
+                */
+               if (error)
+                       break;
+
                /*
                 * Process bref, chain is only non-NULL if the bref
                 * might be recursable (its possible that we sometimes get
                 * a non-NULL chain where the bref cannot be recursed).
                 */
-#if 0
-               kprintf("SCAN %p/%p %016jx.%02x\n",
-                       parent, chain, bref.data_off, bref.type);
-               int xerr = tsleep(&info->pri, PCATCH, "slp", hz / 10);
-               if (xerr == EINTR || xerr == ERESTART) {
-                       doabort |= HAMMER2_BULK_ABORT;
-               }
-#endif
                ++info->pri;
                if (h2_bulkfree_test(info, &bref, 1))
                        continue;
 
-               doabort |= func(info, &bref);
-
-               if (doabort & HAMMER2_BULK_ABORT)
+               error |= func(info, &bref);
+               if (error)
                        break;
 
                /*
@@ -185,7 +187,8 @@ hammer2_bulk_scan(hammer2_chain_t *parent,
 
                                hammer2_chain_unlock(chain);
                                info->pri = 0;
-                               doabort |= hammer2_bulk_scan(chain, func, info);
+                               rup_error |=
+                                       hammer2_bulk_scan(chain, func, info);
                                info->pri += savepri;
                                hammer2_chain_lock(chain,
                                                   HAMMER2_RESOLVE_ALWAYS |
@@ -197,6 +200,8 @@ hammer2_bulk_scan(hammer2_chain_t *parent,
                        /* does not recurse */
                        break;
                }
+               if (rup_error & HAMMER2_ERROR_ABORTED)
+                       break;
        }
        if (chain) {
                hammer2_chain_unlock(chain);
@@ -210,7 +215,7 @@ hammer2_bulk_scan(hammer2_chain_t *parent,
 
        hammer2_chain_unlock(parent);
 
-       return doabort;
+       return ((error | rup_error) & ~HAMMER2_ERROR_EOF);
 }
 
 /*
@@ -320,7 +325,7 @@ hammer2_bulkfree_pass(hammer2_dev_t *hmp, hammer2_chain_t *vchain,
        hammer2_chain_save_t *save;
        hammer2_off_t incr;
        size_t size;
-       int doabort = 0;
+       int error;
 
        /*
         * We have to clear the live dedup cache as it might have entries
@@ -358,6 +363,7 @@ hammer2_bulkfree_pass(hammer2_dev_t *hmp, hammer2_chain_t *vchain,
         * Loop on a full meta-data scan as many times as required to
         * get through all available storage.
         */
+       error = 0;
        while (cbinfo.sbase < hmp->voldata.volu_size) {
                /*
                 * We have enough ram to represent (incr) bytes of storage.
@@ -391,14 +397,14 @@ hammer2_bulkfree_pass(hammer2_dev_t *hmp, hammer2_chain_t *vchain,
                hammer2_trans_init(hmp->spmp, 0);
                cbinfo.mtid = hammer2_trans_sub(hmp->spmp);
                cbinfo.pri = 0;
-               doabort |= hammer2_bulk_scan(vchain, h2_bulkfree_callback,
-                                            &cbinfo);
+               error |= hammer2_bulk_scan(vchain, h2_bulkfree_callback,
+                                          &cbinfo);
 
                while ((save = TAILQ_FIRST(&cbinfo.list)) != NULL &&
-                      doabort == 0) {
+                      error == 0) {
                        TAILQ_REMOVE(&cbinfo.list, save, entry);
                        cbinfo.pri = 0;
-                       doabort |= hammer2_bulk_scan(save->chain,
+                       error |= hammer2_bulk_scan(save->chain,
                                                     h2_bulkfree_callback,
                                                     &cbinfo);
                        hammer2_chain_drop(save->chain);
@@ -411,8 +417,8 @@ hammer2_bulkfree_pass(hammer2_dev_t *hmp, hammer2_chain_t *vchain,
                        save = TAILQ_FIRST(&cbinfo.list);
                }
 
-               kprintf("bulkfree lastdrop %d %d doabort=%d\n",
-                       vchain->refs, vchain->core.chain_count, doabort);
+               kprintf("bulkfree lastdrop %d %d error=0x%04x\n",
+                       vchain->refs, vchain->core.chain_count, error);
 
                /*
                 * If complete scan succeeded we can synchronize our
@@ -420,7 +426,7 @@ hammer2_bulkfree_pass(hammer2_dev_t *hmp, hammer2_chain_t *vchain,
                 * did occur we cannot safely synchronize our partially
                 * filled-out in-memory freemap.
                 */
-               if (doabort == 0) {
+               if (error == 0) {
                        h2_bulkfree_sync(&cbinfo);
 
                        hammer2_voldata_lock(hmp);
@@ -433,7 +439,7 @@ hammer2_bulkfree_pass(hammer2_dev_t *hmp, hammer2_chain_t *vchain,
                 * Cleanup for next loop.
                 */
                hammer2_trans_done(hmp->spmp);
-               if (doabort)
+               if (error)
                        break;
                cbinfo.sbase = cbinfo.sstop;
                cbinfo.adj_free = 0;
@@ -452,16 +458,22 @@ hammer2_bulkfree_pass(hammer2_dev_t *hmp, hammer2_chain_t *vchain,
                (int)incr / 100,
                (int)incr % 100);
 
-       kprintf("    transition->free   %ld\n", cbinfo.count_10_00);
-       kprintf("    transition->staged %ld\n", cbinfo.count_11_10);
-       kprintf("    ERR(00)->allocated %ld\n", cbinfo.count_00_11);
-       kprintf("    ERR(01)->allocated %ld\n", cbinfo.count_01_11);
-       kprintf("    staged->allocated  %ld\n", cbinfo.count_10_11);
-       kprintf("    ~2MB segs cleaned  %ld\n", cbinfo.count_l0cleans);
-       kprintf("    linear adjusts     %ld\n", cbinfo.count_linadjusts);
-       kprintf("    dedup factor       %ld\n", cbinfo.count_dedup_factor);
+       if (error) {
+               kprintf("    bulkfree was aborted\n");
+       } else {
+               kprintf("    transition->free   %ld\n", cbinfo.count_10_00);
+               kprintf("    transition->staged %ld\n", cbinfo.count_11_10);
+               kprintf("    ERR(00)->allocated %ld\n", cbinfo.count_00_11);
+               kprintf("    ERR(01)->allocated %ld\n", cbinfo.count_01_11);
+               kprintf("    staged->allocated  %ld\n", cbinfo.count_10_11);
+               kprintf("    ~2MB segs cleaned  %ld\n", cbinfo.count_l0cleans);
+               kprintf("    linear adjusts     %ld\n",
+                       cbinfo.count_linadjusts);
+               kprintf("    dedup factor       %ld\n",
+                       cbinfo.count_dedup_factor);
+       }
 
-       return doabort;
+       return error;
 }
 
 static void
@@ -505,13 +517,13 @@ h2_bulkfree_callback(hammer2_bulkfree_info_t *cbinfo, hammer2_blockref_t *bref)
        uint16_t class;
        size_t bytes;
        int radix;
-       int error;
 
        /*
         * Check for signal and allow yield to userland during scan
         */
        if (hammer2_signal_check(&cbinfo->save_time))
-               return HAMMER2_BULK_ABORT;
+               return HAMMER2_ERROR_ABORTED;
+
        if (bref->type == HAMMER2_BREF_TYPE_INODE) {
                ++cbinfo->count_inodes_scanned;
                if ((cbinfo->count_inodes_scanned & 65535) == 0)
@@ -524,7 +536,6 @@ h2_bulkfree_callback(hammer2_bulkfree_info_t *cbinfo, hammer2_blockref_t *bref)
         * Calculate the data offset and determine if it is within
         * the current freemap range being gathered.
         */
-       error = 0;
        data_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
        if (data_off < cbinfo->sbase || data_off >= cbinfo->sstop)
                return 0;
@@ -644,7 +655,7 @@ h2_bulkfree_callback(hammer2_bulkfree_info_t *cbinfo, hammer2_blockref_t *bref)
                else
                        bytes -= HAMMER2_FREEMAP_BLOCK_SIZE;
        }
-       return error;
+       return 0;
 }
 
 /*
index 3604b13..aef1c24 100644 (file)
@@ -2652,6 +2652,22 @@ hammer2_chain_next(hammer2_chain_t **parentp, hammer2_chain_t *chain,
 }
 
 /*
+ * Caller wishes to iterate chains under parent, loading new chains into
+ * chainp.  Caller must initialize *chainp to NULL and *firstp to 1, and
+ * then call hammer2_chain_scan() repeatedly until a non-zero return.
+ * During the scan, *firstp will be set to 0 and (*chainp) will be replaced
+ * with the returned chain for the scan.  The returned *chainp will be
+ * locked and referenced.  Any prior contents will be unlocked and dropped.
+ *
+ * Caller should check the return value.  A normal scan EOF will return
+ * exactly HAMMER2_ERRORF_EOF.  Any other non-zero value indicates an
+ * error trying to access parent data.  Any error in the returned chain
+ * must be tested separately by the caller.
+ *
+ * (*chainp) is dropped on each scan, but will only be set if the returned
+ * element itself can recurse.  Leaf elements are NOT resolved, loaded, or
+ * returned via *chainp.  The caller will get their bref only.
+ *
  * The raw scan function is similar to lookup/next but does not seek to a key.
  * Blockrefs are iterated via first_bref = (parent, NULL) and
  * next_chain = (parent, bref).
@@ -2660,15 +2676,8 @@ hammer2_chain_next(hammer2_chain_t **parentp, hammer2_chain_t *chain,
  * nominally returns a locked and referenced *chainp != NULL for chains
  * the caller might need to recurse on (and will dipose of any *chainp passed
  * in).  The caller must check the chain->bref.type either way.
- *
- * *chainp is not set for leaf elements.
- *
- * This function takes a pointer to a stack-based bref structure whos
- * contents is updated for each iteration.  The same pointer is returned,
- * or NULL when the iteration is complete.  *firstp must be set to 1 for
- * the first ieration.  This function will set it to 0.
  */
-hammer2_blockref_t *
+int
 hammer2_chain_scan(hammer2_chain_t *parent, hammer2_chain_t **chainp,
                   hammer2_blockref_t *bref, int *firstp,
                   int *cache_indexp, int flags)
@@ -2685,8 +2694,10 @@ hammer2_chain_scan(hammer2_chain_t *parent, hammer2_chain_t **chainp,
        int how;
        int generation;
        int maxloops = 300000;
+       int error;
 
        hmp = parent->hmp;
+       error = 0;
 
        /*
         * Scan flags borrowed from lookup.
@@ -2723,15 +2734,19 @@ hammer2_chain_scan(hammer2_chain_t *parent, hammer2_chain_t **chainp,
                        chain = NULL;
                }
                if (key == 0) {
-                       bref = NULL;
+                       error |= HAMMER2_ERROR_EOF;
                        goto done;
                }
        }
 
 again:
-       KKASSERT(parent->error == 0);   /* XXX case not handled yet */
+       if (parent->error) {
+               error = parent->error;
+               goto done;
+       }
        if (--maxloops == 0)
                panic("hammer2_chain_scan: maxloops");
+
        /*
         * Locate the blockref array.  Currently we do a fully associative
         * search through the array.
@@ -2747,7 +2762,7 @@ again:
                 */
                if (parent->data->ipdata.meta.op_flags &
                    HAMMER2_OPFLAG_DIRECTDATA) {
-                       bref = NULL;
+                       error |= HAMMER2_ERROR_EOF;
                        goto done;
                }
                base = &parent->data->ipdata.u.blockset.blockref[0];
@@ -2810,7 +2825,7 @@ again:
        if (bref_ptr == NULL) {
                hammer2_spin_unex(&parent->core.spin);
                KKASSERT(chain == NULL);
-               bref = NULL;
+               error |= HAMMER2_ERROR_EOF;
                goto done;
        }
 
@@ -2889,7 +2904,7 @@ again:
 
                key = next_key;
                if (key == 0) {
-                       bref = NULL;
+                       error |= HAMMER2_ERROR_EOF;
                        goto done;
                }
                goto again;
@@ -2901,7 +2916,7 @@ done:
         */
        if (chain)
                *chainp = chain;
-       return (bref);
+       return (error);
 }
 
 /*
index 6dacdd2..4225e66 100644 (file)
@@ -981,5 +981,5 @@ hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
 
        lockmgr(&hmp->bflock, LK_RELEASE);
 
-       return (error);
+       return (hammer2_error_to_errno(error));
 }
index f09e445..b85b712 100644 (file)
@@ -414,7 +414,7 @@ hammer2_signal_check(time_t *timep)
                if (*timep != time_second) {
                        *timep = time_second;
                        if (CURSIG_NOBLOCK(curthread->td_lwp) != 0)
-                               error = EINTR;
+                               error = HAMMER2_ERROR_ABORTED;
                }
        }
        return error;
index c89ac72..74b70a2 100644 (file)
@@ -2022,7 +2022,6 @@ hammer2_recovery(hammer2_dev_t *hmp)
        hammer2_tid_t sync_tid;
        hammer2_tid_t mirror_tid;
        int error;
-       int cumulative_error = 0;
 
        hammer2_trans_init(hmp->spmp, 0);
 
@@ -2040,7 +2039,7 @@ hammer2_recovery(hammer2_dev_t *hmp)
        TAILQ_INIT(&info.list);
        info.depth = 0;
        parent = hammer2_chain_lookup_init(&hmp->vchain, 0);
-       cumulative_error = hammer2_recovery_scan(hmp, parent, &info, sync_tid);
+       error = hammer2_recovery_scan(hmp, parent, &info, sync_tid);
        hammer2_chain_lookup_done(parent);
 
        while ((elm = TAILQ_FIRST(&info.list)) != NULL) {
@@ -2050,16 +2049,14 @@ hammer2_recovery(hammer2_dev_t *hmp)
                kfree(elm, M_HAMMER2);
 
                hammer2_chain_lock(parent, HAMMER2_RESOLVE_ALWAYS);
-               error = hammer2_recovery_scan(hmp, parent, &info,
+               error |= hammer2_recovery_scan(hmp, parent, &info,
                                              hmp->voldata.freemap_tid);
                hammer2_chain_unlock(parent);
                hammer2_chain_drop(parent);     /* drop elm->chain ref */
-               if (error)
-                       cumulative_error = error;
        }
        hammer2_trans_done(hmp->spmp);
 
-       return cumulative_error;
+       return error;
 }
 
 static
@@ -2072,7 +2069,8 @@ hammer2_recovery_scan(hammer2_dev_t *hmp, hammer2_chain_t *parent,
        hammer2_chain_t *chain;
        hammer2_blockref_t bref;
        int cache_index;
-       int cumulative_error = 0;
+       int tmp_error;
+       int rup_error;
        int error;
        int first;
 
@@ -2121,7 +2119,7 @@ hammer2_recovery_scan(hammer2_dev_t *hmp, hammer2_chain_t *parent,
                return 0;
                break;
        default:
-               return EDOM;
+               return HAMMER2_ERROR_BADBREF;
        }
 
        /*
@@ -2146,14 +2144,28 @@ hammer2_recovery_scan(hammer2_dev_t *hmp, hammer2_chain_t *parent,
         * Recursive scan of the last flushed transaction only.  We are
         * doing this without pmp assignments so don't leave the chains
         * hanging around after we are done with them.
+        *
+        * error        Cumulative error this level only
+        * rup_error    Cumulative error for recursion
+        * tmp_error    Specific non-cumulative recursion error
         */
        cache_index = 0;
        chain = NULL;
        first = 1;
+       rup_error = 0;
+       error = 0;
+
+       for (;;) {
+               error |= hammer2_chain_scan(parent, &chain, &bref,
+                                           &first, &cache_index,
+                                           HAMMER2_LOOKUP_NODATA);
+
+               /*
+                * Problem during scan or EOF
+                */
+               if (error)
+                       break;
 
-       while (hammer2_chain_scan(parent, &chain, &bref,
-                                 &first, &cache_index,
-                                 HAMMER2_LOOKUP_NODATA) != NULL) {
                /*
                 * If this is a leaf
                 */
@@ -2171,24 +2183,25 @@ hammer2_recovery_scan(hammer2_dev_t *hmp, hammer2_chain_t *parent,
                atomic_set_int(&chain->flags, HAMMER2_CHAIN_RELEASE);
                if (bref.mirror_tid > sync_tid) {
                        ++info->depth;
-                       error = hammer2_recovery_scan(hmp, chain,
-                                                     info, sync_tid);
+                       tmp_error = hammer2_recovery_scan(hmp, chain,
+                                                          info, sync_tid);
                        --info->depth;
-                       if (error)
-                               cumulative_error = error;
+               } else {
+                       tmp_error = 0;
                }
 
                /*
                 * Flush the recovery at the PFS boundary to stage it for
                 * the final flush of the super-root topology.
                 */
-               if ((bref.flags & HAMMER2_BREF_FLAG_PFSROOT) &&
+               if (tmp_error == 0 &&
+                   (bref.flags & HAMMER2_BREF_FLAG_PFSROOT) &&
                    (chain->flags & HAMMER2_CHAIN_ONFLUSH)) {
                        hammer2_flush(chain, HAMMER2_FLUSH_TOP);
                }
+               rup_error |= tmp_error;
        }
-
-       return cumulative_error;
+       return ((error | rup_error) & ~HAMMER2_ERROR_EOF);
 }
 
 /*