hammer2 - Cluster API stabilization
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 19 Mar 2014 05:17:08 +0000 (22:17 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 19 Mar 2014 05:17:08 +0000 (22:17 -0700)
* Fix issues that come up with mount/umount/cpdup tests related to the
  new cluster API.  Ref-counting, locks, and many other (expected) items.

* Fleshing out cluster->focus was a bit more involved than I had hoped.

* Generally speaking, cluster refs and locks work the same way that chain
  refs and locks do.

sys/vfs/hammer2/hammer2.h
sys/vfs/hammer2/hammer2_chain.c
sys/vfs/hammer2/hammer2_cluster.c
sys/vfs/hammer2/hammer2_inode.c
sys/vfs/hammer2/hammer2_vfsops.c
sys/vfs/hammer2/hammer2_vnops.c

index d6920e3..1c5f274 100644 (file)
@@ -566,6 +566,9 @@ typedef struct hammer2_trans hammer2_trans_t;
 #define HAMMER2_FREEMAP_HEUR           (HAMMER2_FREEMAP_HEUR_NRADIX * \
                                         HAMMER2_FREEMAP_HEUR_TYPES)
 
+#define HAMMER2_CLUSTER_COPY_CHAINS    0x0001  /* copy chains */
+#define HAMMER2_CLUSTER_COPY_NOREF     0x0002  /* do not ref chains */
+
 /*
  * Global (per device) mount structure for device (aka vp->v_mount->hmp)
  */
@@ -775,8 +778,8 @@ void hammer2_adjreadcounter(hammer2_blockref_t *bref, size_t bytes);
 /*
  * hammer2_inode.c
  */
-struct vnode *hammer2_igetv(hammer2_inode_t *ip, int *errorp);
-
+struct vnode *hammer2_igetv(hammer2_inode_t *ip, hammer2_cluster_t *cparent,
+                       int *errorp);
 void hammer2_inode_lock_nlinks(hammer2_inode_t *ip);
 void hammer2_inode_unlock_nlinks(hammer2_inode_t *ip);
 hammer2_inode_t *hammer2_inode_lookup(hammer2_pfsmount_t *pmp,
index b031dd6..56e1415 100644 (file)
@@ -2770,6 +2770,10 @@ done:
  * FULL.  This typically means that the caller is creating the chain after
  * doing a hammer2_chain_lookup().
  *
+ * A non-NULL bref is typically passed when key and keybits must be overridden.
+ * Note that hammer2_cluster_duplicate() *ONLY* uses the key and keybits fields
+ * from a passed-in bref and uses the old chain's bref for everything else.
+ *
  * The old chain must be in a DELETED state unless snapshot is non-zero.
  *
  * The new chain will be live (i.e. not deleted), and modified.
index ad5a8fc..15caffe 100644 (file)
@@ -84,29 +84,64 @@ hammer2_cluster_unlinked(hammer2_cluster_t *cluster)
        return((cluster->focus->flags & HAMMER2_CHAIN_UNLINKED) != 0);
 }
 
+/*
+ * Return a bref representative of the cluster.  Any data offset is removed
+ * (since it would only be applicable to a particular chain in the cluster).
+ *
+ * However, the radix portion of data_off is used for many purposes and will
+ * be retained.
+ */
 void
 hammer2_cluster_bref(hammer2_cluster_t *cluster, hammer2_blockref_t *bref)
 {
        *bref = cluster->focus->bref;
-       bref->data_off = 0;     /* should be opaque to caller */
+       bref->data_off &= HAMMER2_OFF_MASK_RADIX;
 }
 
 void
 hammer2_cluster_set_chainflags(hammer2_cluster_t *cluster, uint32_t flags)
 {
+       hammer2_chain_t *chain;
        int i;
 
-       for (i = 0; i < cluster->nchains; ++i)
-               atomic_set_int(&cluster->array[i]->flags, flags);
+       for (i = 0; i < cluster->nchains; ++i) {
+               chain = cluster->array[i];
+               if (chain)
+                       atomic_set_int(&chain->flags, flags);
+       }
 }
 
 void
 hammer2_cluster_setsubmod(hammer2_trans_t *trans, hammer2_cluster_t *cluster)
 {
+       hammer2_chain_t *chain;
        int i;
 
-       for (i = 0; i < cluster->nchains; ++i)
-               hammer2_chain_setsubmod(trans, cluster->array[i]);
+       for (i = 0; i < cluster->nchains; ++i) {
+               chain = cluster->array[i];
+               if (chain)
+                       hammer2_chain_setsubmod(trans, chain);
+       }
+}
+
+/*
+ * Create a cluster with one ref from the specified chain.  The chain
+ * is not further referenced.  The caller typically supplies a locked
+ * chain and transfers ownership to the cluster.
+ */
+hammer2_cluster_t *
+hammer2_cluster_from_chain(hammer2_chain_t *chain)
+{
+       hammer2_cluster_t *cluster;
+
+       cluster = kmalloc(sizeof(*cluster), M_HAMMER2, M_WAITOK | M_ZERO);
+       cluster->array[0] = chain;
+       cluster->nchains = 1;
+       cluster->focus = chain;
+       cluster->pmp = chain->pmp;              /* can be NULL */
+       cluster->refs = 1;
+
+       return cluster;
 }
 
 /*
@@ -204,9 +239,11 @@ hammer2_cluster_core_alloc(hammer2_trans_t *trans,
        int i;
 
        for (i = 0; i < ocluster->nchains; ++i) {
-               hammer2_chain_core_alloc(trans,
-                                        ncluster->array[i],
-                                        ocluster->array[i]);
+               if (ncluster->array[i]) {
+                       hammer2_chain_core_alloc(trans,
+                                                ncluster->array[i],
+                                                ocluster->array[i]);
+               }
        }
 }
 
@@ -219,11 +256,14 @@ hammer2_cluster_core_alloc(hammer2_trans_t *trans,
 void
 hammer2_cluster_ref(hammer2_cluster_t *cluster)
 {
+       hammer2_chain_t *chain;
        int i;
 
        atomic_add_int(&cluster->refs, 1);
        for (i = 0; i < cluster->nchains; ++i) {
-               hammer2_chain_ref(cluster->array[i]);
+               chain = cluster->array[i];
+               if (chain)
+                       hammer2_chain_ref(chain);
        }
 }
 
@@ -234,18 +274,23 @@ hammer2_cluster_ref(hammer2_cluster_t *cluster)
 void
 hammer2_cluster_drop(hammer2_cluster_t *cluster)
 {
+       hammer2_chain_t *chain;
        int i;
 
+       KKASSERT(cluster->refs > 0);
        for (i = 0; i < cluster->nchains; ++i) {
-               hammer2_chain_drop(cluster->array[i]);
-               if (cluster->refs == 1)
-                       cluster->array[i] = NULL;
+               chain = cluster->array[i];
+               if (chain) {
+                       hammer2_chain_drop(chain);
+                       if (cluster->refs == 1)
+                               cluster->array[i] = NULL;
+               }
        }
-       if (atomic_fetchadd_int(&cluster->refs, -1) != 1) {
-               KKASSERT(cluster->refs > 0);
-               return;
+       if (atomic_fetchadd_int(&cluster->refs, -1) == 1) {
+               cluster->focus = NULL;
+               kfree(cluster, M_HAMMER2);
+               /* cluster = NULL; safety */
        }
-       kfree(cluster, M_HAMMER2);
 }
 
 void
@@ -261,18 +306,22 @@ hammer2_cluster_wait(hammer2_cluster_t *cluster)
 int
 hammer2_cluster_lock(hammer2_cluster_t *cluster, int how)
 {
+       hammer2_chain_t *chain;
        int i;
        int error;
 
        error = 0;
        atomic_add_int(&cluster->refs, 1);
        for (i = 0; i < cluster->nchains; ++i) {
-               error = hammer2_chain_lock(cluster->array[i], how);
-               if (error) {
-                       while (--i >= 0)
-                               hammer2_chain_unlock(cluster->array[i]);
-                       atomic_add_int(&cluster->refs, -1);
-                       break;
+               chain = cluster->array[i];
+               if (chain) {
+                       error = hammer2_chain_lock(chain, how);
+                       if (error) {
+                               while (--i >= 0)
+                                       hammer2_chain_unlock(cluster->array[i]);
+                               atomic_add_int(&cluster->refs, -1);
+                               break;
+                       }
                }
        }
        return error;
@@ -289,23 +338,32 @@ hammer2_cluster_lock(hammer2_cluster_t *cluster, int how)
 void
 hammer2_cluster_replace(hammer2_cluster_t *dst, hammer2_cluster_t *src)
 {
+       hammer2_chain_t *chain;
        int i;
 
        KKASSERT(dst->refs == 1);
+       dst->focus = NULL;
 
        for (i = 0; i < src->nchains; ++i) {
-               hammer2_chain_ref(src->array[i]);
-               if (i < dst->nchains)
-                       hammer2_chain_unlock(dst->array[i]);
-               dst->array[i] = src->array[i];
+               chain = src->array[i];
+               if (chain) {
+                       hammer2_chain_ref(chain);
+                       if (i < dst->nchains && dst->array[i])
+                               hammer2_chain_unlock(dst->array[i]);
+                       dst->array[i] = chain;
+                       if (dst->focus == NULL)
+                               dst->focus = chain;
+               }
        }
        while (i < dst->nchains) {
-               hammer2_chain_unlock(dst->array[i]);
-               dst->array[i] = NULL;
+               chain = dst->array[i];
+               if (chain) {
+                       hammer2_chain_unlock(chain);
+                       dst->array[i] = NULL;
+               }
                ++i;
        }
        dst->nchains = src->nchains;
-       dst->focus = src->focus;
 }
 
 /*
@@ -319,34 +377,53 @@ hammer2_cluster_replace(hammer2_cluster_t *dst, hammer2_cluster_t *src)
 void
 hammer2_cluster_replace_locked(hammer2_cluster_t *dst, hammer2_cluster_t *src)
 {
+       hammer2_chain_t *chain;
        int i;
 
        KKASSERT(dst->refs == 1);
 
+       dst->focus = NULL;
        for (i = 0; i < src->nchains; ++i) {
-               hammer2_chain_lock(src->array[i], 0);
-               if (i < dst->nchains)
-                       hammer2_chain_unlock(dst->array[i]);
-               dst->array[i] = src->array[i];
+               chain = src->array[i];
+               if (chain) {
+                       hammer2_chain_lock(chain, 0);
+                       if (i < dst->nchains && dst->array[i])
+                               hammer2_chain_unlock(dst->array[i]);
+                       dst->array[i] = src->array[i];
+                       if (dst->focus == NULL)
+                               dst->focus = chain;
+               }
        }
        while (i < dst->nchains) {
-               hammer2_chain_unlock(dst->array[i]);
-               dst->array[i] = NULL;
+               chain = dst->array[i];
+               if (chain) {
+                       hammer2_chain_unlock(chain);
+                       dst->array[i] = NULL;
+               }
                ++i;
        }
        dst->nchains = src->nchains;
-       dst->focus = src->focus;
 }
 
 /*
  * Copy a cluster, returned a ref'd cluster.  All underlying chains
  * are also ref'd, but not locked.
  *
- * If with_chains is 0 the returned cluster has a ref count of 1 but
- * no chains will be assigned.
+ * If HAMMER2_CLUSTER_COPY_CHAINS is specified, the chains are copied
+ * to the new cluster and a reference is nominally added to them and to
+ * the cluster.  The cluster will have 1 ref.
+ *
+ * If HAMMER2_CLUSTER_COPY_NOREF is specified along with CHAINS, the chains
+ * are copied but no additional references are made and the cluster will have
+ * 0 refs.  Callers must ref the cluster and the chains before dropping it
+ * (typically by locking it).
+ *
+ * If flags are passed as 0 the copy is setup as if it contained the chains
+ * but the chains will not be copied over, and the cluster will have 0 refs.
+ * Callers must ref the cluster before dropping it (typically by locking it).
  */
 hammer2_cluster_t *
-hammer2_cluster_copy(hammer2_cluster_t *ocluster, int with_chains)
+hammer2_cluster_copy(hammer2_cluster_t *ocluster, int copy_flags)
 {
        hammer2_pfsmount_t *pmp = ocluster->pmp;
        hammer2_cluster_t *ncluster;
@@ -356,11 +433,16 @@ hammer2_cluster_copy(hammer2_cluster_t *ocluster, int with_chains)
        ncluster->pmp = pmp;
        ncluster->nchains = ocluster->nchains;
        ncluster->focus = ocluster->focus;
-       if (with_chains) {
-               ncluster->refs = 1;
+       ncluster->refs = 1;
+       if (copy_flags & HAMMER2_CLUSTER_COPY_CHAINS) {
+               if ((copy_flags & HAMMER2_CLUSTER_COPY_NOREF) == 0)
+                       ncluster->refs = 1;
                for (i = 0; i < ocluster->nchains; ++i) {
                        ncluster->array[i] = ocluster->array[i];
-                       hammer2_chain_ref(ncluster->array[i]);
+                       if ((copy_flags & HAMMER2_CLUSTER_COPY_NOREF) == 0 &&
+                           ncluster->array[i]) {
+                               hammer2_chain_ref(ncluster->array[i]);
+                       }
                }
        }
        return (ncluster);
@@ -373,31 +455,41 @@ hammer2_cluster_copy(hammer2_cluster_t *ocluster, int with_chains)
 void
 hammer2_cluster_unlock(hammer2_cluster_t *cluster)
 {
+       hammer2_chain_t *chain;
        int i;
 
-       for (i = 0; i < cluster->nchains; ++i)
-               hammer2_chain_unlock(cluster->array[i]);
+       KKASSERT(cluster->refs > 0);
+       for (i = 0; i < cluster->nchains; ++i) {
+               chain = cluster->array[i];
+               if (chain) {
+                       hammer2_chain_unlock(chain);
+                       if (cluster->refs == 1)
+                               cluster->array[i] = NULL;       /* safety */
+               }
+       }
        if (atomic_fetchadd_int(&cluster->refs, -1) == 1) {
-               for (i = 0; i < cluster->nchains; ++i)  /* safety */
-                       cluster->array[i] = NULL;
+               cluster->focus = NULL;
                kfree(cluster, M_HAMMER2);
-               return;
+               /* cluster = NULL; safety */
        }
-       KKASSERT(cluster->refs > 0);
 }
 
 /*
- * Refactor the chains of a locked cluster
+ * Refactor the locked chains of a cluster.
  */
 void
 hammer2_cluster_refactor(hammer2_cluster_t *cluster)
 {
        int i;
 
+       cluster->focus = NULL;
        for (i = 0; i < cluster->nchains; ++i) {
-               hammer2_chain_refactor(&cluster->array[i]);
+               if (cluster->array[i]) {
+                       hammer2_chain_refactor(&cluster->array[i]);
+                       if (cluster->focus == NULL)
+                               cluster->focus = cluster->array[i];
+               }
        }
-       cluster->focus = cluster->array[0];
 }
 
 /*
@@ -414,12 +506,18 @@ 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) {
-               hammer2_chain_resize(trans, ip,
-                                    cparent->array[i], &cluster->array[i],
-                                    nradix, flags);
+               if (cluster->array[i]) {
+                       KKASSERT(cparent->array[i]);
+                       hammer2_chain_resize(trans, ip,
+                                            cparent->array[i],
+                                            &cluster->array[i],
+                                            nradix, flags);
+                       if (cluster->focus == NULL)
+                               cluster->focus = cluster->array[i];
+               }
        }
-       cluster->focus = cluster->array[0];
 }
 
 /*
@@ -453,12 +551,26 @@ hammer2_cluster_modify(hammer2_trans_t *trans, hammer2_cluster_t *cluster,
 {
        int i;
 
-       for (i = 0; i < cluster->nchains; ++i)
-               hammer2_chain_modify(trans, &cluster->array[i], flags);
-       cluster->focus = cluster->array[0];
+       cluster->focus = NULL;
+       for (i = 0; i < cluster->nchains; ++i) {
+               if (cluster->array[i]) {
+                       hammer2_chain_modify(trans, &cluster->array[i], flags);
+                       if (cluster->focus == NULL)
+                               cluster->focus = cluster->array[i];
+               }
+       }
 }
 
 /*
+ * Synchronize modifications with other chains in a cluster.
+ *
+ * Nominal front-end operations only edit non-block-table data in a single
+ * chain.  This code copies such modifications to the other chains in the
+ * cluster.
+ */
+/* hammer2_cluster_modsync() */
+
+/*
  * Lookup initialization/completion API
  */
 hammer2_cluster_t *
@@ -469,10 +581,14 @@ hammer2_cluster_lookup_init(hammer2_cluster_t *cparent, int flags)
 
        cluster = kmalloc(sizeof(*cluster), M_HAMMER2, M_WAITOK | M_ZERO);
        cluster->pmp = cparent->pmp;                    /* can be NULL */
-       for (i = 0; i < cparent->nchains; ++i)
+       /* cluster->focus = NULL; already null */
+
+       for (i = 0; i < cparent->nchains; ++i) {
                cluster->array[i] = cparent->array[i];
+               if (cluster->focus == NULL)
+                       cluster->focus = cluster->array[i];
+       }
        cluster->nchains = cparent->nchains;
-       cluster->focus = cluster->array[0];
 
        /*
         * Independently lock (this will also give cluster 1 ref)
@@ -521,33 +637,41 @@ 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 */
+       cparent->focus = NULL;
        *ddflagp = 0;
 
        for (i = 0; i < cparent->nchains; ++i) {
                key_next = *key_nextp;
+               if (cparent->array[i] == NULL) {
+                       ++null_count;
+                       continue;
+               }
                chain = hammer2_chain_lookup(&cparent->array[i], &key_next,
                                             key_beg, key_end,
                                             &cparent->cache_index[i],
                                             flags, &ddflag);
+               if (cparent->focus == NULL)
+                       cparent->focus = cparent->array[i];
                cluster->array[i] = chain;
                if (chain == NULL) {
                        ++null_count;
                } else {
-                       if (bref_type == 0)
+                       if (cluster->focus == NULL) {
                                bref_type = chain->bref.type;
-                       KKASSERT(bref_type == chain->bref.type);
-                       if (bytes == 0)
                                bytes = chain->bytes;
+                               *ddflagp = ddflag;
+                               cluster->focus = chain;
+                       }
+                       KKASSERT(bref_type == chain->bref.type);
                        KKASSERT(bytes == chain->bytes);
+                       KKASSERT(*ddflagp == ddflag);
                }
                if (key_accum > key_next)
                        key_accum = key_next;
-               KKASSERT(i == 0 || *ddflagp == ddflag);
-               *ddflagp = ddflag;
        }
        *key_nextp = key_accum;
        cluster->nchains = i;
-       cluster->focus = cluster->array[0];
 
        if (null_count == i) {
                hammer2_cluster_drop(cluster);
@@ -573,17 +697,37 @@ hammer2_cluster_next(hammer2_cluster_t *cparent, hammer2_cluster_t *cluster,
 
        key_accum = *key_nextp;
        null_count = 0;
+       cluster->focus = NULL;
+       cparent->focus = NULL;
 
        for (i = 0; i < cparent->nchains; ++i) {
                key_next = *key_nextp;
-               chain = hammer2_chain_next(&cparent->array[i],
-                                          cluster->array[i],
-                                          &key_next,
-                                          key_beg, key_end,
+               chain = cluster->array[i];
+               if (chain == NULL) {
+                       if (cparent->focus == NULL)
+                               cparent->focus = cparent->array[i];
+                       ++null_count;
+                       continue;
+               }
+               if (cparent->array[i] == NULL) {
+                       if (flags & HAMMER2_LOOKUP_NOLOCK)
+                               hammer2_chain_drop(chain);
+                       else
+                               hammer2_chain_unlock(chain);
+                       ++null_count;
+                       continue;
+               }
+               chain = hammer2_chain_next(&cparent->array[i], chain,
+                                          &key_next, key_beg, key_end,
                                           &cparent->cache_index[i], flags);
+               if (cparent->focus == NULL)
+                       cparent->focus = cparent->array[i];
                cluster->array[i] = chain;
-               if (chain == NULL)
+               if (chain == NULL) {
                        ++null_count;
+               } else if (cluster->focus == NULL) {
+                       cluster->focus = chain;
+               }
                if (key_accum > key_next)
                        key_accum = key_next;
        }
@@ -591,13 +735,14 @@ hammer2_cluster_next(hammer2_cluster_t *cparent, hammer2_cluster_t *cluster,
        if (null_count == i) {
                hammer2_cluster_drop(cluster);
                cluster = NULL;
-       } else {
-               cluster->focus = cluster->array[0];
        }
        return(cluster);
 }
 
+#if 0
 /*
+ * XXX initial NULL cluster needs reworking (pass **clusterp ?)
+ *
  * The raw scan function is similar to lookup/next but does not seek to a key.
  * Blockrefs are iterated via first_chain = (parent, NULL) and
  * next_chain = (parent, chain).
@@ -618,8 +763,21 @@ hammer2_cluster_scan(hammer2_cluster_t *cparent, hammer2_cluster_t *cluster,
        null_count = 0;
 
        for (i = 0; i < cparent->nchains; ++i) {
-               chain = hammer2_chain_scan(cparent->array[i],
-                                          cluster->array[i],
+               chain = cluster->array[i];
+               if (chain == NULL) {
+                       ++null_count;
+                       continue;
+               }
+               if (cparent->array[i] == NULL) {
+                       if (flags & HAMMER2_LOOKUP_NOLOCK)
+                               hammer2_chain_drop(chain);
+                       else
+                               hammer2_chain_unlock(chain);
+                       ++null_count;
+                       continue;
+               }
+
+               chain = hammer2_chain_scan(cparent->array[i], chain,
                                           &cparent->cache_index[i], flags);
                cluster->array[i] = chain;
                if (chain == NULL)
@@ -633,6 +791,8 @@ hammer2_cluster_scan(hammer2_cluster_t *cparent, hammer2_cluster_t *cluster,
        return(cluster);
 }
 
+#endif
+
 /*
  * Create a new cluster using the specified key
  */
@@ -642,7 +802,6 @@ hammer2_cluster_create(hammer2_trans_t *trans, hammer2_cluster_t *cparent,
                     hammer2_key_t key, int keybits, int type, size_t bytes)
 {
        hammer2_cluster_t *cluster;
-       hammer2_chain_t *chain;
        hammer2_pfsmount_t *pmp;
        int error;
        int i;
@@ -655,35 +814,85 @@ hammer2_cluster_create(hammer2_trans_t *trans, hammer2_cluster_t *cparent,
                cluster->pmp = pmp;                     /* can be NULL */
                cluster->refs = 1;
        }
+       cluster->focus = NULL;
+       cparent->focus = NULL;
+
+       /*
+        * NOTE: cluster->array[] entries can initially be NULL.  If
+        *       *clusterp is supplied, skip NULL entries, otherwise
+        *       create new chains.
+        */
        for (i = 0; i < cparent->nchains; ++i) {
-               chain = cluster->array[i];
-               error = hammer2_chain_create(trans, &cparent->array[i], &chain,
+               if (*clusterp && cluster->array[i] == NULL) {
+                       if (cparent->focus == NULL)
+                               cparent->focus = cparent->array[i];
+                       continue;
+               }
+               error = hammer2_chain_create(trans,
+                                            &cparent->array[i],
+                                            &cluster->array[i],
                                             key, keybits, type, bytes);
                KKASSERT(error == 0);
-               cluster->array[i] = chain;
+               if (cparent->focus == NULL)
+                       cparent->focus = cparent->array[i];
+               if (cluster->focus == NULL)
+                       cluster->focus = cluster->array[i];
        }
-       cluster->focus = cluster->array[0];
+       cluster->nchains = i;
        *clusterp = cluster;
 
        return error;
 }
 
 /*
- * Duplicate a cluster under a new parent
+ * Duplicate a cluster under a new parent.
+ *
+ * WARNING! Unlike hammer2_chain_duplicate(), only the key and keybits fields
+ *         are used from a passed-in non-NULL bref pointer.  All other fields
+ *         are extracted from the original chain for each chain in the
+ *         iteration.
  */
 void
 hammer2_cluster_duplicate(hammer2_trans_t *trans, hammer2_cluster_t *cparent,
                          hammer2_cluster_t *cluster, hammer2_blockref_t *bref,
                          int snapshot, int duplicate_reason)
 {
+       hammer2_chain_t *chain;
+       hammer2_blockref_t xbref;
        int i;
 
+       cluster->focus = NULL;
+       cparent->focus = NULL;
+
        for (i = 0; i < cluster->nchains; ++i) {
-               hammer2_chain_duplicate(trans, &cparent->array[i],
-                                       &cluster->array[i], bref,
-                                       snapshot, duplicate_reason);
+               chain = cluster->array[i];
+               if (chain) {
+                       if (bref) {
+                               xbref = chain->bref;
+                               xbref.key = bref->key;
+                               xbref.keybits = bref->keybits;
+                               hammer2_chain_duplicate(trans,
+                                                       &cparent->array[i],
+                                                       &chain, &xbref,
+                                                       snapshot,
+                                                       duplicate_reason);
+                       } else {
+                               hammer2_chain_duplicate(trans,
+                                                       &cparent->array[i],
+                                                       &chain, NULL,
+                                                       snapshot,
+                                                       duplicate_reason);
+                       }
+                       cluster->array[i] = chain;
+                       if (cluster->focus == NULL)
+                               cluster->focus = chain;
+                       if (cparent->focus == NULL)
+                               cparent->focus = cparent->array[i];
+               } else {
+                       if (cparent->focus == NULL)
+                               cparent->focus = cparent->array[i];
+               }
        }
-       cluster->focus = cluster->array[0];
 }
 
 /*
@@ -693,13 +902,19 @@ void
 hammer2_cluster_delete_duplicate(hammer2_trans_t *trans,
                                 hammer2_cluster_t *cluster, int flags)
 {
+       hammer2_chain_t *chain;
        int i;
 
+       cluster->focus = NULL;
        for (i = 0; i < cluster->nchains; ++i) {
-               hammer2_chain_delete_duplicate(trans, &cluster->array[i],
-                                              flags);
+               chain = cluster->array[i];
+               if (chain) {
+                       hammer2_chain_delete_duplicate(trans, &chain, flags);
+                       cluster->array[i] = chain;
+                       if (cluster->focus == NULL)
+                               cluster->focus = chain;
+               }
        }
-       cluster->focus = cluster->array[0];
 }
 
 /*
@@ -709,10 +924,13 @@ void
 hammer2_cluster_delete(hammer2_trans_t *trans, hammer2_cluster_t *cluster,
                       int flags)
 {
+       hammer2_chain_t *chain;
        int i;
 
        for (i = 0; i < cluster->nchains; ++i) {
-               hammer2_chain_delete(trans, cluster->array[i], flags);
+               chain = cluster->array[i];
+               if (chain)
+                       hammer2_chain_delete(trans, chain, flags);
        }
 }
 
index c0fc670..feb2fe2 100644 (file)
@@ -93,6 +93,9 @@ hammer2_inode_lock_ex(hammer2_inode_t *ip)
        ccms_thread_lock(&ip->topo_cst, CCMS_STATE_EXCLUSIVE);
        cluster = hammer2_cluster_copy(&ip->cluster, 0);
 
+       ip->cluster.focus = NULL;
+       cluster->focus = NULL;
+
        for (i = 0; i < cluster->nchains; ++i) {
                chain = ip->cluster.array[i];
                core = chain->core;
@@ -105,6 +108,8 @@ hammer2_inode_lock_ex(hammer2_inode_t *ip)
                                spin_unlock(&core->cst.spin);
                                ochain = ip->cluster.array[i];
                                ip->cluster.array[i] = chain;
+                               if (ip->cluster.focus == NULL)
+                                       ip->cluster.focus = chain;
                                hammer2_chain_drop(ochain);
                        }
                        hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS);
@@ -113,8 +118,9 @@ hammer2_inode_lock_ex(hammer2_inode_t *ip)
                        hammer2_chain_unlock(chain);
                }
                cluster->array[i] = chain;
+               if (cluster->focus == NULL)
+                       cluster->focus = chain;
        }
-       cluster->focus = cluster->array[0];
 
        /*
         * Returned cluster must resolve hardlink pointers
@@ -127,7 +133,6 @@ hammer2_inode_lock_ex(hammer2_inode_t *ip)
                          HAMMER2_CHAIN_DUPLICATED) == 0);
                KKASSERT(error == 0);
        }
-       cluster->focus = cluster->array[0];
 
        return (cluster);
 }
@@ -164,6 +169,8 @@ hammer2_inode_lock_sh(hammer2_inode_t *ip)
        cluster = hammer2_cluster_copy(&ip->cluster, 0);
        ccms_thread_lock(&ip->topo_cst, CCMS_STATE_SHARED);
 
+       cluster->focus = NULL;
+
        for (i = 0; i < cluster->nchains; ++i) {
                chain = ip->cluster.array[i];
                core = chain->core;
@@ -191,8 +198,9 @@ cycle_excl:
                        continue;       /* restart at i=-1 -> i=0 on loop */
                }
                cluster->array[i] = chain;
+               if (cluster->focus == NULL)
+                       cluster->focus = chain;
        }
-       cluster->focus = cluster->array[0];
 
        /*
         * Returned cluster must resolve hardlink pointers
@@ -205,7 +213,6 @@ cycle_excl:
                          HAMMER2_CHAIN_DUPLICATED) == 0);
                KKASSERT(error == 0);
        }
-       cluster->focus = cluster->array[0];
 
        return (cluster);
 }
@@ -314,7 +321,7 @@ hammer2_inode_drop(hammer2_inode_t *ip)
                                ip->pmp = NULL;
 
                                /*
-                                * Cleaning out ip->chain isn't entirely
+                                * Cleaning out ip->cluster isn't entirely
                                 * trivial.
                                 */
                                hammer2_inode_repoint(ip, NULL, NULL);
@@ -360,7 +367,7 @@ hammer2_inode_drop(hammer2_inode_t *ip)
  * races.
  */
 struct vnode *
-hammer2_igetv(hammer2_inode_t *ip, int *errorp)
+hammer2_igetv(hammer2_inode_t *ip, hammer2_cluster_t *cparent, int *errorp)
 {
        hammer2_inode_data_t *ipdata;
        hammer2_pfsmount_t *pmp;
@@ -371,7 +378,7 @@ hammer2_igetv(hammer2_inode_t *ip, int *errorp)
        KKASSERT(pmp != NULL);
        *errorp = 0;
 
-       ipdata = &hammer2_cluster_data(&ip->cluster)->ipdata;
+       ipdata = &hammer2_cluster_data(cparent)->ipdata;
 
        for (;;) {
                /*
@@ -557,7 +564,9 @@ again:
         * Initialize nip's cluster
         */
        nip->cluster.refs = 1;
-       nip->flags = HAMMER2_CLUSTER_INODE;
+       nip->cluster.pmp = pmp;
+       nip->cluster.flags |= HAMMER2_CLUSTER_INODE;
+       hammer2_cluster_replace(&nip->cluster, cluster);
 
        nipdata = &hammer2_cluster_data(cluster)->ipdata;
        nip->inum = nipdata->inum;
@@ -884,6 +893,8 @@ retry:
         *
         * WARNING! Duplications (to a different parent) can cause indirect
         *          blocks to be inserted, refactor xcluster.
+        *
+        * WARNING! Only key and keybits is extracted from a passed-in bref.
         */
        hammer2_cluster_bref(cluster, &bref);
        bref.key = lhc;                 /* invisible dir entry key */
@@ -1100,11 +1111,9 @@ hammer2_inode_connect(hammer2_trans_t *trans,
 }
 
 /*
- * Repoint ip->chain to nchain.  Caller must hold the inode exclusively
- * locked.
- *
- * ip->chain is set to nchain.  The prior chain in ip->chain is dropped
- * and nchain is ref'd.
+ * Repoint ip->cluster's chains to cluster's chains.  Caller must hold
+ * the inode exclusively locked.  cluster may be NULL to clean out any
+ * chains in ip->cluster.
  */
 void
 hammer2_inode_repoint(hammer2_inode_t *ip, hammer2_inode_t *pip,
@@ -1115,10 +1124,15 @@ hammer2_inode_repoint(hammer2_inode_t *ip, hammer2_inode_t *pip,
        hammer2_inode_t *opip;
        int i;
 
-       for (i = 0; i < cluster->nchains; ++i) {
-               /*
-                * Get possible replacement chain, loop if nothing to do.
-                */
+       /*
+        * Replace chains in ip->cluster with chains from cluster and
+        * adjust the focus if necessary.
+        *
+        * NOTE: nchain and/or ochain can be NULL due to gaps
+        *       in the cluster arrays.
+        */
+       ip->cluster.focus = NULL;
+       for (i = 0; cluster && i < cluster->nchains; ++i) {
                nchain = cluster->array[i];
                if (i < ip->cluster.nchains) {
                        ochain = ip->cluster.array[i];
@@ -1129,15 +1143,29 @@ hammer2_inode_repoint(hammer2_inode_t *ip, hammer2_inode_t *pip,
                }
 
                /*
-                * Make adjustment
+                * Make adjustments
                 */
                ip->cluster.array[i] = nchain;
+               if (ip->cluster.focus == NULL)
+                       ip->cluster.focus = nchain;
                if (nchain)
                        hammer2_chain_ref(nchain);
                if (ochain)
                        hammer2_chain_drop(ochain);
        }
-       ip->cluster.focus = ip->cluster.array[0];
+
+       /*
+        * Release any left-over chains in ip->cluster.
+        */
+       while (i < ip->cluster.nchains) {
+               nchain = ip->cluster.array[i];
+               if (nchain) {
+                       ip->cluster.array[i] = NULL;
+                       hammer2_chain_drop(nchain);
+               }
+               ++i;
+       }
+       ip->cluster.nchains = cluster ? cluster->nchains : 0;
 
        /*
         * Repoint ip->pip if requested (non-NULL pip).
@@ -1259,7 +1287,8 @@ hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip,
                cparent = NULL;
 
                ocluster = cluster;
-               cluster = hammer2_cluster_copy(ocluster, 1);
+               cluster = hammer2_cluster_copy(ocluster,
+                                              HAMMER2_CLUSTER_COPY_CHAINS);
                error = hammer2_hardlink_find(dip, cluster);
                KKASSERT(error == 0);
        }
@@ -1364,8 +1393,9 @@ done:
                hammer2_cluster_unlock(cluster);
        if (cparent)
                hammer2_cluster_lookup_done(cparent);
-       if (ocluster)
+       if (ocluster) {
                hammer2_cluster_drop(ocluster);
+       }
 
        return error;
 }
@@ -1552,7 +1582,9 @@ hammer2_hardlink_consolidate(hammer2_trans_t *trans,
                 * we are delete-duplicating ncluster here it might decide not
                 * to reallocate the block.  Set FORCECOW to force it to.
                 */
-               ncluster = cluster;
+               ncluster = hammer2_cluster_copy(cluster,
+                                               HAMMER2_CLUSTER_COPY_CHAINS |
+                                               HAMMER2_CLUSTER_COPY_NOREF);
                hammer2_cluster_lock(ncluster, HAMMER2_RESOLVE_ALWAYS);
                hammer2_cluster_set_chainflags(ncluster,
                                               HAMMER2_CHAIN_FORCECOW);
@@ -1611,8 +1643,8 @@ hammer2_hardlink_consolidate(hammer2_trans_t *trans,
                hammer2_inode_repoint(ip, cdip, cluster);
 
        /*
-        * Unlock the original cluster last as the lock blocked races against
-        * the creation of the new hardlink target.
+        * Unlock and destroy ncluster.
+        * Return the shifted cluster in *clusterp.
         */
        if (ncluster)
                hammer2_cluster_unlock(ncluster);
index 2190873..4983350 100644 (file)
@@ -880,6 +880,7 @@ hammer2_assign_physical(hammer2_trans_t *trans,
                        hammer2_key_t lbase, int pblksize, int *errorp)
 {
        hammer2_cluster_t *cluster;
+       hammer2_cluster_t *dparent;
        hammer2_key_t key_dummy;
        int pradix = hammer2_getradix(pblksize);
        int ddflag;
@@ -893,8 +894,8 @@ hammer2_assign_physical(hammer2_trans_t *trans,
        *errorp = 0;
        KKASSERT(pblksize >= HAMMER2_MIN_ALLOC);
 retry:
-       hammer2_cluster_lock(cparent, HAMMER2_RESOLVE_ALWAYS); /* extra lock */
-       cluster = hammer2_cluster_lookup(cparent, &key_dummy,
+       dparent = hammer2_cluster_lookup_init(cparent, 0);
+       cluster = hammer2_cluster_lookup(dparent, &key_dummy,
                                     lbase, lbase,
                                     HAMMER2_LOOKUP_NODATA, &ddflag);
 
@@ -905,14 +906,14 @@ retry:
                 * NOTE: DATA chains are created without device backing
                 *       store (nor do we want any).
                 */
-               *errorp = hammer2_cluster_create(trans, cparent, &cluster,
+               *errorp = hammer2_cluster_create(trans, dparent, &cluster,
                                               lbase, HAMMER2_PBUFRADIX,
                                               HAMMER2_BREF_TYPE_DATA,
                                               pblksize);
                if (cluster == NULL) {
-                       hammer2_cluster_lookup_done(cparent);
+                       hammer2_cluster_lookup_done(dparent);
                        panic("hammer2_cluster_create: par=%p error=%d\n",
-                               cparent->focus, *errorp);
+                               dparent->focus, *errorp);
                        goto retry;
                }
                /*ip->delta_dcount += pblksize;*/
@@ -929,7 +930,7 @@ retry:
                case HAMMER2_BREF_TYPE_DATA:
                        if (hammer2_cluster_bytes(cluster) != pblksize) {
                                hammer2_cluster_resize(trans, ip,
-                                                    cparent, cluster,
+                                                    dparent, cluster,
                                                     pradix,
                                                     HAMMER2_MODIFY_OPTDATA);
                        }
@@ -944,15 +945,14 @@ retry:
        }
 
        /*
-        * Cleanup.  If chain wound up being the inode (i.e. DIRECTDATA),
-        * we need to update cparent.  The caller expects cparent to not
-        * become stale.
+        * Cleanup.  If cluster wound up being the inode itself, i.e.
+        * the DIRECTDATA case for offset 0, then we need to update cparent.
+        * The caller expects cparent to not become stale.
         */
-       hammer2_cluster_lookup_done(cparent);
-       if (cluster && ddflag) {
-               kprintf("replace parent XXX\n");
+       hammer2_cluster_lookup_done(dparent);
+       /* dparent = NULL; safety */
+       if (cluster && ddflag)
                hammer2_cluster_replace_locked(cparent, cluster);
-       }
        return (cluster);
 }
 
@@ -1135,7 +1135,7 @@ hammer2_compress_and_write(struct buf *bp, hammer2_trans_t *trans,
        cluster = hammer2_assign_physical(trans, ip, cparent,
                                          lbase, comp_block_size,
                                          errorp);
-       ipdata = &hammer2_cluster_data(&ip->cluster)->ipdata;
+       ipdata = &hammer2_cluster_data(cparent)->ipdata;
 
        if (*errorp) {
                kprintf("WRITE PATH: An error occurred while "
@@ -1663,7 +1663,7 @@ hammer2_vfs_root(struct mount *mp, struct vnode **vpp)
                error = EINVAL;
        } else {
                cparent = hammer2_inode_lock_sh(pmp->iroot);
-               vp = hammer2_igetv(pmp->iroot, &error);
+               vp = hammer2_igetv(pmp->iroot, cparent, &error);
                hammer2_inode_unlock_sh(pmp->iroot, cparent);
                *vpp = vp;
                if (vp == NULL)
index 68d97d3..fa7dab6 100644 (file)
@@ -1210,6 +1210,23 @@ hammer2_vop_nresolve(struct vop_nresolve_args *ap)
        hammer2_inode_unlock_sh(dip, cparent);
 
        /*
+        * Resolve hardlink entries before acquiring the inode.
+        */
+       if (cluster) {
+               ipdata = &hammer2_cluster_data(cluster)->ipdata;
+               if (ipdata->type == HAMMER2_OBJTYPE_HARDLINK) {
+                       hammer2_tid_t inum = ipdata->inum;
+                       error = hammer2_hardlink_find(dip, cluster);
+                       if (error) {
+                               kprintf("hammer2: unable to find hardlink "
+                                       "0x%016jx\n", inum);
+                               hammer2_cluster_unlock(cluster);
+                               return error;
+                       }
+               }
+       }
+
+       /*
         * nresolve needs to resolve hardlinks, the original cluster is not
         * sufficient.
         */
@@ -1264,7 +1281,7 @@ hammer2_vop_nresolve(struct vop_nresolve_args *ap)
         *          will handle it properly.
         */
        if (cluster) {
-               vp = hammer2_igetv(ip, &error);
+               vp = hammer2_igetv(ip, cluster, &error);
                if (error == 0) {
                        vn_unlock(vp);
                        cache_setvp(ap->a_nch, vp);
@@ -1306,7 +1323,7 @@ hammer2_vop_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
                return ENOENT;
        }
        cparent = hammer2_inode_lock_ex(ip);
-       *ap->a_vpp = hammer2_igetv(ip, &error);
+       *ap->a_vpp = hammer2_igetv(ip, cparent, &error);
        hammer2_inode_unlock_ex(ip, cparent);
 
        return error;
@@ -1343,7 +1360,7 @@ hammer2_vop_nmkdir(struct vop_nmkdir_args *ap)
                KKASSERT(nip == NULL);
                *ap->a_vpp = NULL;
        } else {
-               *ap->a_vpp = hammer2_igetv(nip, &error);
+               *ap->a_vpp = hammer2_igetv(nip, cluster, &error);
                hammer2_inode_unlock_ex(nip, cluster);
        }
        hammer2_trans_done(&trans);
@@ -1531,7 +1548,7 @@ hammer2_vop_ncreate(struct vop_ncreate_args *ap)
                KKASSERT(nip == NULL);
                *ap->a_vpp = NULL;
        } else {
-               *ap->a_vpp = hammer2_igetv(nip, &error);
+               *ap->a_vpp = hammer2_igetv(nip, ncluster, &error);
                hammer2_inode_unlock_ex(nip, ncluster);
        }
        hammer2_trans_done(&trans);
@@ -1577,7 +1594,7 @@ hammer2_vop_nmknod(struct vop_nmknod_args *ap)
                KKASSERT(nip == NULL);
                *ap->a_vpp = NULL;
        } else {
-               *ap->a_vpp = hammer2_igetv(nip, &error);
+               *ap->a_vpp = hammer2_igetv(nip, ncluster, &error);
                hammer2_inode_unlock_ex(nip, ncluster);
        }
        hammer2_trans_done(&trans);
@@ -1627,7 +1644,7 @@ hammer2_vop_nsymlink(struct vop_nsymlink_args *ap)
                hammer2_trans_done(&trans);
                return error;
        }
-       *ap->a_vpp = hammer2_igetv(nip, &error);
+       *ap->a_vpp = hammer2_igetv(nip, ncparent, &error);
 
        /*
         * Build the softlink (~like file data) and finalize the namecache.
@@ -2021,7 +2038,6 @@ hammer2_strategy_read(struct vop_strategy_args *ap)
            btype != HAMMER2_BREF_TYPE_DATA) {
                panic("READ PATH: hammer2_strategy_read: unknown bref type");
        }
-
        hammer2_chain_load_async(cluster, hammer2_strategy_read_callback, nbio);
        return(0);
 }
@@ -2052,6 +2068,7 @@ hammer2_strategy_read_callback(hammer2_io_t *dio,
                                bp->b_flags |= B_ERROR;
                                bp->b_error = dio->bp->b_error;
                                biodone(bio);
+                               hammer2_cluster_unlock(cluster);
                        } else {
                                chain = cluster->array[i];
                                kprintf("hammer2: IO CHAIN-%d %p\n", i, chain);
@@ -2106,7 +2123,6 @@ hammer2_strategy_read_callback(hammer2_io_t *dio,
                        bp->b_flags |= B_NOTMETA;
                        bp->b_resid = 0;
                        bp->b_error = 0;
-                       hammer2_chain_unlock(chain);
                        break;
                default:
                        panic("hammer2_strategy_read: "
@@ -2117,8 +2133,6 @@ hammer2_strategy_read_callback(hammer2_io_t *dio,
                if (dio)
                        hammer2_io_bqrelse(&dio);
                panic("hammer2_strategy_read: unknown bref type");
-               /*hammer2_chain_unlock(chain);*/
-               /*chain = NULL;*/
        }
        hammer2_cluster_unlock(cluster);
        biodone(bio);