hammer2 - Start work on quorum validation. master
authorMatthew Dillon <dillon@apollo.backplane.com>
Mon, 30 Mar 2015 05:45:59 +0000 (22:45 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Mon, 30 Mar 2015 05:45:59 +0000 (22:45 -0700)
* Remove HAMMER2_PFSTYPE_SNAPSHOT, it adds unnecessary complication.
  Just make snapshots HAMMER2_PFSTYPE_MASTER.

  Add a subtype field to the inode and PFS ioctl to identify snapshots.

* Start fleshing out the quorum code.  hammer2_chain_lock() is the core.
  It will eventually also have to be async.

  hammer2_chain_lock() now does hard quorum checks, soft master checks,
  and identifies slaves and whether synchronization is needed, based on
  mirror_tid.

* More cleanup of cluster->focus.

* When removing nodes from a PFS cluster, since the cluster is being
  ripped apart and hammer2_inode_lock_ex() does quorum validation,
  we cannot use this function.  Just acquire and release the low-level
  inode mutex instead.

sys/vfs/hammer2/hammer2.h
sys/vfs/hammer2/hammer2_cluster.c
sys/vfs/hammer2/hammer2_disk.h
sys/vfs/hammer2/hammer2_ioctl.c
sys/vfs/hammer2/hammer2_ioctl.h
sys/vfs/hammer2/hammer2_vfsops.c

index 3684d1c..945398f 100644 (file)
@@ -376,7 +376,7 @@ RB_PROTOTYPE(hammer2_chain_tree, hammer2_chain, rbnode, hammer2_chain_cmp);
 #define HAMMER2_CHAIN_UNUSED00001000   0x00001000
 #define HAMMER2_CHAIN_UNUSED00002000   0x00002000
 #define HAMMER2_CHAIN_ONRBTREE         0x00004000      /* on parent RB tree */
-#define HAMMER2_CHAIN_SNAPSHOT         0x00008000      /* snapshot special */
+#define HAMMER2_CHAIN_UNUSED00008000   0x00008000
 #define HAMMER2_CHAIN_EMBEDDED         0x00010000      /* embedded data */
 #define HAMMER2_CHAIN_RELEASE          0x00020000      /* don't keep around */
 #define HAMMER2_CHAIN_BMAPPED          0x00040000      /* present in blkmap */
@@ -801,7 +801,9 @@ typedef struct hammer2_dev hammer2_dev_t;
  * how elements of the cluster act on the cluster.  pfs_mode is only applicable
  * when a PFS is mounted by the system.  pfs_nmasters is our best guess as to
  * how many masters have been configured for a cluster and is always
- * applicable.
+ * applicable.  pfs_types[] is an array with 1:1 correspondance to the
+ * iroot cluster and describes the PFS types of the nodes making up the
+ * cluster.
  *
  * WARNING! Portions of this structure have deferred initialization.  In
  *         particular, if not mounted there will be no ihidden or wthread.
@@ -826,6 +828,7 @@ struct hammer2_pfs {
        hammer2_dev_t           *spmp_hmp;      /* only if super-root pmp */
        hammer2_inode_t         *iroot;         /* PFS root inode */
        hammer2_inode_t         *ihidden;       /* PFS hidden directory */
+       uint8_t                 pfs_types[HAMMER2_MAXCLUSTER];
        struct lock             lock;           /* PFS lock for certain ops */
        hammer2_off_t           inode_count;    /* copy of inode_count */
        struct netexport        export;         /* nfs export */
index ffc1838..07aaf67 100644 (file)
@@ -245,6 +245,8 @@ hammer2_cluster_setmethod_check(hammer2_trans_t *trans,
  * The returned cluster will be focused on the chain (strictly speaking,
  * the focus should be NULL if the chain is not locked but we do not check
  * for this condition).
+ *
+ * We fake the flags.
  */
 hammer2_cluster_t *
 hammer2_cluster_from_chain(hammer2_chain_t *chain)
@@ -257,7 +259,11 @@ hammer2_cluster_from_chain(hammer2_chain_t *chain)
        cluster->focus = chain;
        cluster->pmp = chain->pmp;
        cluster->refs = 1;
-       cluster->flags = HAMMER2_CLUSTER_LOCKED;
+       cluster->flags = HAMMER2_CLUSTER_LOCKED |
+                        HAMMER2_CLUSTER_WRHARD |
+                        HAMMER2_CLUSTER_RDHARD |
+                        HAMMER2_CLUSTER_MSYNCED |
+                        HAMMER2_CLUSTER_SSYNCED;
 
        return cluster;
 }
@@ -385,7 +391,7 @@ hammer2_cluster_drop(hammer2_cluster_t *cluster)
                }
        }
        if (atomic_fetchadd_int(&cluster->refs, -1) == 1) {
-               cluster->focus = NULL;          /* safety */
+               cluster->focus = NULL;          /* safety XXX chg to assert */
                kfree(cluster, M_HAMMER2);
                /* cluster is invalid */
        }
@@ -413,44 +419,218 @@ hammer2_cluster_wait(hammer2_cluster_t *cluster)
  * RESOLVE_RDONLY operations are effectively as-of so the quorum does not need
  * to be maintained once the topology is validated as-of the top level of
  * the operation.
+ *
+ * If a failure occurs the operation must be aborted by higher-level code and
+ * retried. XXX
  */
 int
 hammer2_cluster_lock(hammer2_cluster_t *cluster, int how)
 {
        hammer2_chain_t *chain;
-       hammer2_chain_t *tmp;
-       int i;
+       hammer2_pfs_t *pmp;
+       uint32_t nflags;
+       hammer2_tid_t quorum_tid;
+       int focus_pfs_type;
+       int nmasters;
+       int ttlslaves;
+       int ttlmasters;
+       int nslaves;
+       int nquorum;
        int error;
+       int i;
+
+       pmp = cluster->pmp;
+       KKASSERT(pmp != NULL || cluster->nchains == 0);
 
        /* cannot be on inode-embedded cluster template, must be on copy */
        KKASSERT((cluster->flags & HAMMER2_CLUSTER_INODE) == 0);
        if (cluster->flags & HAMMER2_CLUSTER_LOCKED) {
                kprintf("hammer2_cluster_lock: cluster %p already locked!\n",
                        cluster);
+       } else {
+               KKASSERT(cluster->focus == NULL);
        }
        atomic_set_int(&cluster->flags, HAMMER2_CLUSTER_LOCKED);
 
+       focus_pfs_type = 0;
+       quorum_tid = 0;
+       nflags = 0;
+       ttlslaves = 0;
+       ttlmasters = 0;
+       nslaves = 0;
+       nmasters = 0;
+
+       /*
+        * Calculate the quorum count.
+        */
+       nquorum = pmp ? pmp->pfs_nmasters / 2 + 1 : 0;
+
        if ((how & HAMMER2_RESOLVE_NOREF) == 0)
                atomic_add_int(&cluster->refs, 1);
 
-       error = 0;
+       /*
+        * Lock chains and accumulate statistics.
+        */
+       for (i = 0; i < cluster->nchains; ++i) {
+               chain = cluster->array[i].chain;
+               if (chain == NULL)
+                       continue;
+               error = hammer2_chain_lock(chain, how);
+               if (error) {
+                       kprintf("hammer2_cluster_lock: cluster %p index %d "
+                               "lock failure\n", cluster, i);
+                       cluster->array[i].chain = NULL;
+                       hammer2_chain_drop(chain);
+                       continue;
+               }
 
+               /*
+                * We can resolve some things on this array pass but other
+                * things will require a full-accounting of available
+                * masters.
+                */
+               switch (cluster->pmp->pfs_types[i]) {
+               case HAMMER2_PFSTYPE_MASTER:
+                       ++ttlmasters;
+                       if (quorum_tid < chain->bref.mirror_tid ||
+                           nmasters == 0) {
+                               nmasters = 1;
+                               quorum_tid = chain->bref.mirror_tid;
+                       } else if (quorum_tid == chain->bref.mirror_tid) {
+                               ++nmasters;
+                       }
+                       break;
+               case HAMMER2_PFSTYPE_SLAVE:
+                       ++ttlslaves;
+                       break;
+               case HAMMER2_PFSTYPE_SOFT_MASTER:
+                       nflags |= HAMMER2_CLUSTER_WRSOFT;
+                       nflags |= HAMMER2_CLUSTER_RDSOFT;
+                       break;
+               case HAMMER2_PFSTYPE_SOFT_SLAVE:
+                       nflags |= HAMMER2_CLUSTER_RDSOFT;
+                       break;
+               case HAMMER2_PFSTYPE_SUPROOT:
+                       /*
+                        * Degenerate cluster representing the super-root
+                        * topology on a single device.
+                        */
+                       nflags |= HAMMER2_CLUSTER_WRHARD;
+                       nflags |= HAMMER2_CLUSTER_RDHARD;
+                       cluster->focus = chain;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /*
+        * Pass 2 - accumulate statistics.
+        */
        for (i = 0; i < cluster->nchains; ++i) {
                chain = cluster->array[i].chain;
-               if (chain) {
-                       error = hammer2_chain_lock(chain, how);
-                       if (error) {
-                               while (--i >= 0) {
-                                       tmp = cluster->array[i].chain;
-                                       hammer2_chain_unlock(tmp);
+               if (chain == NULL)
+                       continue;
+               switch (cluster->pmp->pfs_types[i]) {
+               case HAMMER2_PFSTYPE_MASTER:
+                       /*
+                        * We must have enough up-to-date masters to reach
+                        * a quorum and the master mirror_tid must match
+                        * the quorum's mirror_tid.
+                        */
+                       if (nmasters >= nquorum &&
+                           quorum_tid == chain->bref.mirror_tid) {
+                               nflags |= HAMMER2_CLUSTER_WRHARD;
+                               nflags |= HAMMER2_CLUSTER_RDHARD;
+                               if (cluster->focus == NULL ||
+                                   focus_pfs_type == HAMMER2_PFSTYPE_SLAVE) {
+                                       focus_pfs_type = HAMMER2_PFSTYPE_MASTER;
+                                       cluster->focus = chain;
                                }
-                               atomic_add_int(&cluster->refs, -1);
-                               break;
                        }
-                       if (cluster->focus == NULL)
+                       break;
+               case HAMMER2_PFSTYPE_SLAVE:
+                       /*
+                        * We must have enough up-to-date masters to reach
+                        * a quorum and the slave mirror_tid must match the
+                        * quorum's mirror_tid.
+                        */
+                       if (nmasters >= nquorum &&
+                           quorum_tid == chain->bref.mirror_tid) {
+                               ++nslaves;
+                               nflags |= HAMMER2_CLUSTER_RDHARD;
+                               if (cluster->focus == NULL) {
+                                       focus_pfs_type = HAMMER2_PFSTYPE_SLAVE;
+                                       cluster->focus = chain;
+                               }
+                       }
+                       break;
+               case HAMMER2_PFSTYPE_SOFT_MASTER:
+                       /*
+                        * Directly mounted soft master always wins.  There
+                        * should be only one.
+                        */
+                       KKASSERT(focus_pfs_type != HAMMER2_PFSTYPE_SOFT_MASTER);
+                       cluster->focus = chain;
+                       focus_pfs_type = HAMMER2_PFSTYPE_SOFT_MASTER;
+                       break;
+               case HAMMER2_PFSTYPE_SOFT_SLAVE:
+                       /*
+                        * Directly mounted soft slave always wins.  There
+                        * should be only one.
+                        */
+                       KKASSERT(focus_pfs_type != HAMMER2_PFSTYPE_SOFT_SLAVE);
+                       if (focus_pfs_type != HAMMER2_PFSTYPE_SOFT_MASTER) {
                                cluster->focus = chain;
+                               focus_pfs_type = HAMMER2_PFSTYPE_SOFT_SLAVE;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /*
+        * Set SSYNCED or MSYNCED for slaves and masters respectively if
+        * all available nodes (even if 0 are available) are fully
+        * synchronized.  This is used by the synchronization thread to
+        * determine if there is work it could potentially accomplish.
+        */
+       if (nslaves == ttlslaves)
+               nflags |= HAMMER2_CLUSTER_SSYNCED;
+       if (nmasters == ttlmasters)
+               nflags |= HAMMER2_CLUSTER_MSYNCED;
+
+       /*
+        * Determine if the cluster was successfully locked for the
+        * requested operation and generate an error code.  The cluster
+        * will not be locked (or ref'd) if an error is returned.
+        */
+       atomic_set_int(&cluster->flags, nflags);
+       atomic_clear_int(&cluster->flags, HAMMER2_CLUSTER_ZFLAGS & ~nflags);
+       error = 0;
+
+       if (how & HAMMER2_RESOLVE_RDONLY) {
+               if ((nflags & HAMMER2_CLUSTER_RDOK) == 0)
+                       error = EIO;
+       } else {
+               if ((nflags & HAMMER2_CLUSTER_WROK) == 0)
+                       error = EIO;
+       }
+
+       if (error) {
+               kprintf("hammer2_cluster_lock: cluster %p lock failed %d\n",
+                       cluster, error);
+               for (i = 0; i < cluster->nchains; ++i) {
+                       chain = cluster->array[i].chain;
+                       if (chain == NULL)
+                               continue;
+                       hammer2_chain_unlock(chain);
                }
+               if ((how & HAMMER2_RESOLVE_NOREF) == 0)
+                       atomic_add_int(&cluster->refs, -1);
        }
+
        return error;
 }
 
@@ -549,9 +729,10 @@ hammer2_cluster_replace_locked(hammer2_cluster_t *dst, hammer2_cluster_t *src)
 
 /*
  * Copy a cluster, returned a ref'd cluster.  All underlying chains
- * are also ref'd, but not locked.  The cluster focus is not set because
- * the cluster is not yet locked (and the originating cluster does not
- * have to be locked either).
+ * are also ref'd, but not locked.
+ *
+ * The cluster focus is not set because the cluster is not yet locked
+ * (and the originating cluster does not have to be locked either).
  */
 hammer2_cluster_t *
 hammer2_cluster_copy(hammer2_cluster_t *ocluster)
@@ -602,8 +783,9 @@ hammer2_cluster_unlock(hammer2_cluster_t *cluster)
                                cluster->array[i].chain = NULL; /* safety */
                }
        }
+       cluster->focus = NULL;
+
        if (atomic_fetchadd_int(&cluster->refs, -1) == 1) {
-               cluster->focus = NULL;
                kfree(cluster, M_HAMMER2);
                /* cluster = NULL; safety */
        }
@@ -624,7 +806,6 @@ hammer2_cluster_resize(hammer2_trans_t *trans, hammer2_inode_t *ip,
        KKASSERT(cparent->pmp == cluster->pmp);         /* can be NULL */
        KKASSERT(cparent->nchains == cluster->nchains);
 
-       cluster->focus = NULL;
        for (i = 0; i < cluster->nchains; ++i) {
                chain = cluster->array[i].chain;
                if (chain) {
@@ -632,8 +813,6 @@ hammer2_cluster_resize(hammer2_trans_t *trans, hammer2_inode_t *ip,
                        hammer2_chain_resize(trans, ip,
                                             cparent->array[i].chain, chain,
                                             nradix, flags);
-                       if (cluster->focus == NULL)
-                               cluster->focus = chain;
                }
        }
 }
@@ -671,14 +850,10 @@ hammer2_cluster_modify(hammer2_trans_t *trans, hammer2_cluster_t *cluster,
        hammer2_chain_t *chain;
        int i;
 
-       cluster->focus = NULL;
        for (i = 0; i < cluster->nchains; ++i) {
                chain = cluster->array[i].chain;
-               if (chain) {
+               if (chain)
                        hammer2_chain_modify(trans, chain, flags);
-                       if (cluster->focus == NULL)
-                               cluster->focus = chain;
-               }
        }
 }
 
@@ -754,11 +929,8 @@ hammer2_cluster_lookup_init(hammer2_cluster_t *cparent, int flags)
        cluster->flags = 0;     /* cluster not locked (yet) */
        /* cluster->focus = NULL; already null */
 
-       for (i = 0; i < cparent->nchains; ++i) {
+       for (i = 0; i < cparent->nchains; ++i)
                cluster->array[i].chain = cparent->array[i].chain;
-               if (cluster->focus == NULL)
-                       cluster->focus = cluster->array[i].chain;
-       }
        cluster->nchains = cparent->nchains;
 
        /*
@@ -810,7 +982,6 @@ hammer2_cluster_lookup(hammer2_cluster_t *cparent, hammer2_key_t *key_nextp,
        cluster = kmalloc(sizeof(*cluster), M_HAMMER2, M_WAITOK | M_ZERO);
        cluster->pmp = pmp;                             /* can be NULL */
        cluster->refs = 1;
-       /* cluster->focus = NULL; already null */
        if ((flags & HAMMER2_LOOKUP_NOLOCK) == 0)
                cluster->flags |= HAMMER2_CLUSTER_LOCKED;
 
@@ -1097,14 +1268,7 @@ hammer2_cluster_rename(hammer2_trans_t *trans, hammer2_blockref_t *bref,
                                                     &cparent->array[i].chain,
                                                     chain, flags);
                        }
-                       cluster->array[i].chain = chain;
-                       if (cluster->focus == NULL)
-                               cluster->focus = chain;
-                       if (cparent->focus == NULL)
-                               cparent->focus = cparent->array[i].chain;
-               } else {
-                       if (cparent->focus == NULL)
-                               cparent->focus = cparent->array[i].chain;
+                       KKASSERT(cluster->array[i].chain == chain); /*remove*/
                }
        }
 }
@@ -1204,7 +1368,8 @@ hammer2_cluster_snapshot(hammer2_trans_t *trans, hammer2_cluster_t *ocluster,
 
        if (nip) {
                wipdata = hammer2_cluster_modify_ip(trans, nip, ncluster, 0);
-               wipdata->pfs_type = HAMMER2_PFSTYPE_SNAPSHOT;
+               wipdata->pfs_type = HAMMER2_PFSTYPE_MASTER;
+               wipdata->pfs_subtype = HAMMER2_PFSSUBTYPE_SNAPSHOT;
                wipdata->op_flags |= HAMMER2_OPFLAG_PFSROOT;
                kern_uuidgen(&wipdata->pfs_fsid, 1);
 
index 15a2919..ec31fe2 100644 (file)
@@ -808,7 +808,8 @@ typedef struct hammer2_bmap_data hammer2_bmap_data_t;
 
 struct hammer2_inode_data {
        uint16_t        version;        /* 0000 inode data version */
-       uint16_t        reserved02;     /* 0002 */
+       uint8_t         reserved02;     /* 0002 */
+       uint8_t         pfs_subtype;    /* 0003 pfs sub-type */
 
        /*
         * core inode attributes, inode type, misc flags
@@ -937,7 +938,7 @@ typedef struct hammer2_inode_data hammer2_inode_data_t;
 #define HAMMER2_PFSTYPE_SOFT_SLAVE     0x04
 #define HAMMER2_PFSTYPE_SOFT_MASTER    0x05
 #define HAMMER2_PFSTYPE_MASTER         0x06
-#define HAMMER2_PFSTYPE_SNAPSHOT       0x07
+#define HAMMER2_PFSTYPE_UNUSED07       0x07
 #define HAMMER2_PFSTYPE_SUPROOT                0x08
 #define HAMMER2_PFSTYPE_DUMMY          0x09
 #define HAMMER2_PFSTYPE_MAX            16
@@ -949,7 +950,7 @@ typedef struct hammer2_inode_data hammer2_inode_data_t;
 #define HAMMER2_PFSTRAN_SOFT_SLAVE     0x40
 #define HAMMER2_PFSTRAN_SOFT_MASTER    0x50
 #define HAMMER2_PFSTRAN_MASTER         0x60
-#define HAMMER2_PFSTRAN_SNAPSHOT       0x70
+#define HAMMER2_PFSTRAN_UNUSED70       0x70
 #define HAMMER2_PFSTRAN_SUPROOT                0x80
 #define HAMMER2_PFSTRAN_DUMMY          0x90
 
@@ -957,6 +958,9 @@ typedef struct hammer2_inode_data hammer2_inode_data_t;
 #define HAMMER2_PFS_DEC_TRANSITION(n)  (((n) >> 4) & 0x0F)
 #define HAMMER2_PFS_ENC_TRANSITION(n)  (((n) & 0x0F) << 4)
 
+#define HAMMER2_PFSSUBTYPE_NONE                0
+#define HAMMER2_PFSSUBTYPE_SNAPSHOT    1
+
 /*
  * PFS mode of operation is a bitmask.  This is typically not stored
  * on-media, but defined here because the field may be used in dmsgs.
index 1a8217a..fcc4103 100644 (file)
@@ -418,6 +418,7 @@ hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data)
                ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
                pfs->name_key = ripdata->name_key;
                pfs->pfs_type = ripdata->pfs_type;
+               pfs->pfs_subtype = ripdata->pfs_subtype;
                pfs->pfs_clid = ripdata->pfs_clid;
                pfs->pfs_fsid = ripdata->pfs_fsid;
                KKASSERT(ripdata->name_len < sizeof(pfs->name));
@@ -502,6 +503,7 @@ hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
                ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
                pfs->name_key = ripdata->name_key;
                pfs->pfs_type = ripdata->pfs_type;
+               pfs->pfs_subtype = ripdata->pfs_subtype;
                pfs->pfs_clid = ripdata->pfs_clid;
                pfs->pfs_fsid = ripdata->pfs_fsid;
                ripdata = NULL;
@@ -549,6 +551,7 @@ hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
        if (error == 0) {
                nipdata = hammer2_cluster_modify_ip(&trans, nip, ncluster, 0);
                nipdata->pfs_type = pfs->pfs_type;
+               nipdata->pfs_subtype = pfs->pfs_subtype;
                nipdata->pfs_clid = pfs->pfs_clid;
                nipdata->pfs_fsid = pfs->pfs_fsid;
                nipdata->op_flags |= HAMMER2_OPFLAG_PFSROOT;
index 0d0cd13..95e628a 100644 (file)
@@ -90,7 +90,7 @@ struct hammer2_ioc_pfs {
        hammer2_key_t           name_key;       /* super-root directory scan */
        hammer2_key_t           name_next;      /* (GET only) */
        uint8_t                 pfs_type;       /* e.g. MASTER, SLAVE, ... */
-       uint8_t                 reserved0011;
+       uint8_t                 pfs_subtype;    /* e.g. SNAPSHOT */
        uint8_t                 reserved0012;
        uint8_t                 reserved0013;
        uint32_t                reserved0014;
index 8f13f1d..62c53bb 100644 (file)
@@ -387,8 +387,7 @@ hammer2_pfsalloc(hammer2_cluster_t *cluster,
         * or other PFS types need the thread.
         */
        if (cluster && ripdata &&
-           ((ripdata->pfs_type != HAMMER2_PFSTYPE_MASTER &&
-             ripdata->pfs_type != HAMMER2_PFSTYPE_SNAPSHOT) ||
+           (ripdata->pfs_type != HAMMER2_PFSTYPE_MASTER ||
             ripdata->pfs_nmasters > 1) &&
            pmp->primary_thr.td == NULL) {
                hammer2_syncthr_create(&pmp->primary_thr, pmp,
@@ -407,9 +406,10 @@ hammer2_pfsalloc(hammer2_cluster_t *cluster,
 
        /*
         * When a cluster is passed in we must add the cluster's chains
-        * to the PFS's root inode.
+        * to the PFS's root inode and update pmp->pfs_types[].
         *
-        * XXX should fill empty array spots ?
+        * At the moment empty spots can develop due to removals or failures.
+        * Ultimately we want to re-fill these spots. XXX
         */
        if (cluster) {
                hammer2_inode_ref(pmp->iroot);
@@ -426,6 +426,7 @@ hammer2_pfsalloc(hammer2_cluster_t *cluster,
                        rchain->pmp = pmp;
                        hammer2_chain_ref(rchain);
                        pmp->iroot->cluster.array[j].chain = rchain;
+                       pmp->pfs_types[j] = ripdata->pfs_type;
 
                        /*
                         * May have to fixup dirty chain tracking.  Previous
@@ -525,7 +526,17 @@ again:
                if (i != cluster->nchains) {
                        hammer2_syncthr_freeze(&pmp->primary_thr);
 
-                       cluster = hammer2_inode_lock_ex(pmp->iroot);
+                       /*
+                        * Lock the inode and clean out matching chains.
+                        * Note that we cannot use hammer2_inode_lock_*()
+                        * here because that would attempt to validate the
+                        * cluster that we are in the middle of ripping
+                        * apart.
+                        *
+                        * WARNING! We are working directly on the inodes
+                        *          embedded cluster.
+                        */
+                       hammer2_mtx_ex(&pmp->iroot->lock);
 
                        /*
                         * Remove the chain from matching elements of the PFS.
@@ -536,12 +547,14 @@ again:
                                        continue;
 
                                cluster->array[i].chain = NULL;
-                               hammer2_chain_unlock(rchain);
+                               pmp->pfs_types[i] = 0;
+                               hammer2_chain_drop(rchain);
+
+                               /* focus hint */
                                if (cluster->focus == rchain)
                                        cluster->focus = NULL;
                        }
-                       hammer2_inode_repoint(pmp->iroot, NULL, cluster);
-                       hammer2_inode_unlock_ex(pmp->iroot, cluster);
+                       hammer2_mtx_unlock(&pmp->iroot->lock);
                        didfreeze = 1;  /* remaster, unfreeze down below */
                } else {
                        didfreeze = 0;
@@ -879,6 +892,7 @@ hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data,
                spmp->iroot = NULL;
                spmp->iroot = hammer2_inode_get(spmp, NULL, cluster);
                spmp->spmp_hmp = hmp;
+               spmp->pfs_types[0] = ripdata->pfs_type;
                hammer2_inode_ref(spmp->iroot);
                hammer2_inode_unlock_ex(spmp->iroot, cluster);
                schain = NULL;