hammer2 - general stabilization, flusher, mmap, etc
authorMatthew Dillon <dillon@apollo.backplane.com>
Sat, 19 May 2012 00:19:17 +0000 (17:19 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sat, 19 May 2012 00:35:39 +0000 (17:35 -0700)
* Revamp the flush logic.  Flushes now stage the blockref related to the
  data written out to the media.  Higher level chains save the staged
  blockref instead of the current blockref.

* This allows flushes to occur concurrent with active modification of the
  topology without having to restart the flush.  Modifications made after
  the flush has started running will remain intact and not be committed
  to media until the next flush (see note).

  NOTE: Currently chain deletions break this, but this is the only issue
  currently.

* Fix lost chains during unmount.  Deleted chains can still have the MOVED
  and/or MODIFIED bits set, which add additional refs and prevents them
  from being freed.

  Detect when a chain is being deleted permanently (verses temporarily due
  to a rename) and clean out the bits in question.

  NOTE: Currently deletions are removed from the in-memory topology, which
is why the previous NOTE above is still a problem, so we will need
to fix this and to retain at least the MOVED for flushes in
progress.

* Fix data corruption related to unflagged chains which wind up not getting
  flushed and also due to a bug in the indirect block management code.

* Fix a mmap() access failure for cached direct-data (less than 512 bytes).
  nvextendbuf() was not being called for the direct-data case during the
  write().

* Buildworld with a HAMMER2 /usr/obj now succeeds.

* 'hammer2 pfs-create <label>' now defaults to a pfstype of MASTER,
  instead of requiring that the pfstype always be specified.

sbin/hammer2/cmd_pfs.c
sys/vfs/hammer2/hammer2.h
sys/vfs/hammer2/hammer2_chain.c
sys/vfs/hammer2/hammer2_freemap.c
sys/vfs/hammer2/hammer2_inode.c
sys/vfs/hammer2/hammer2_ioctl.c
sys/vfs/hammer2/hammer2_subr.c
sys/vfs/hammer2/hammer2_vfsops.c
sys/vfs/hammer2/hammer2_vnops.c

index c830bfa..46df7cf 100644 (file)
@@ -110,9 +110,11 @@ cmd_pfs_create(const char *sel_path, const char *name,
        int fd;
        uint32_t status;
 
+       /*
+        * Default to MASTER
+        */
        if (pfs_type == HAMMER2_PFSTYPE_NONE) {
-               fprintf(stderr, "hammer2: pfs_create: requires -t pfs_type\n");
-               return(1);
+               pfs_type = HAMMER2_PFSTYPE_MASTER;
        }
 
        if ((fd = hammer2_ioctl_handle(sel_path)) < 0)
index d0a6a0f..265c2c8 100644 (file)
@@ -99,6 +99,7 @@ SPLAY_HEAD(hammer2_chain_splay, hammer2_chain);
 
 struct hammer2_chain {
        struct hammer2_blockref bref;
+       struct hammer2_blockref bref_flush;     /* synchronized w/MOVED bit */
        struct hammer2_chain *parent;           /* return chain to root */
        struct hammer2_chain_splay shead;
        SPLAY_ENTRY(hammer2_chain) snode;
@@ -253,6 +254,13 @@ struct hammer2_data {
 
 typedef struct hammer2_data hammer2_data_t;
 
+struct hammer2_freecache {
+       hammer2_off_t   bulk;
+       hammer2_off_t   single;
+};
+
+typedef struct hammer2_freecache hammer2_freecache_t;
+
 /*
  * Global (per device) mount structure for device (aka vp->v_mount->hmp)
  */
@@ -275,7 +283,7 @@ struct hammer2_mount {
        struct lock     voldatalk;      /* lockmgr lock */
 
        hammer2_volume_data_t voldata;
-       hammer2_off_t   freecache[HAMMER2_FREECACHE_TYPES][HAMMER2_MAX_RADIX+1];
+       hammer2_freecache_t freecache[HAMMER2_FREECACHE_TYPES][HAMMER2_MAX_RADIX+1];
 };
 
 typedef struct hammer2_mount hammer2_mount_t;
@@ -393,7 +401,8 @@ int hammer2_inode_connect(hammer2_inode_t *dip, hammer2_inode_t *oip,
                        const uint8_t *name, size_t name_len);
 
 int hammer2_unlink_file(hammer2_inode_t *dip,
-                       const uint8_t *name, size_t name_len, int isdir);
+                       const uint8_t *name, size_t name_len,
+                       int isdir, hammer2_inode_t *retain_ip);
 int hammer2_hardlink_consolidate(hammer2_inode_t **ipp, hammer2_inode_t *tdip);
 int hammer2_hardlink_deconsolidate(hammer2_inode_t *dip,
                        hammer2_chain_t **chainp, hammer2_inode_t **ipp);
@@ -436,7 +445,7 @@ hammer2_chain_t *hammer2_chain_create(hammer2_mount_t *hmp,
                                hammer2_key_t key, int keybits,
                                int type, size_t bytes);
 void hammer2_chain_delete(hammer2_mount_t *hmp, hammer2_chain_t *parent,
-                               hammer2_chain_t *chain);
+                               hammer2_chain_t *chain, int retain);
 void hammer2_chain_flush(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                                hammer2_tid_t modify_tid);
 void hammer2_chain_commit(hammer2_mount_t *hmp, hammer2_chain_t *chain);
@@ -452,6 +461,8 @@ int hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data,
  */
 hammer2_off_t hammer2_freemap_alloc(hammer2_mount_t *hmp,
                                int type, size_t bytes);
+void hammer2_freemap_free(hammer2_mount_t *hmp, hammer2_off_t data_off,
+                               int type);
 
 #endif /* !_KERNEL */
 #endif /* !_VFS_HAMMER2_HAMMER2_H_ */
index 00b9ab1..91972e7 100644 (file)
@@ -67,12 +67,11 @@ hammer2_chain_cmp(hammer2_chain_t *chain1, hammer2_chain_t *chain2)
 
 /*
  * Recursively mark the parent chain elements so flushes can find
- * modified elements.
+ * modified elements.  Stop when we hit a chain already flagged
+ * SUBMODIFIED, but ignore the SUBMODIFIED bit that might be set
+ * in chain itself.
  *
- * NOTE: The flush code will modify a SUBMODIFIED-flagged chain
- *      during the flush recursion after clearing the parent's
- *      SUBMODIFIED bit.  We don't want to re-set the parent's
- *      SUBMODIFIED bit in this case!
+ * SUBMODIFIED is not set on the chain passed in.
  *
  * XXX rename of parent can create a SMP race
  */
@@ -81,14 +80,10 @@ hammer2_chain_parent_setsubmod(hammer2_mount_t *hmp, hammer2_chain_t *chain)
 {
        hammer2_chain_t *parent;
 
-       if ((chain->flags & HAMMER2_CHAIN_SUBMODIFIED) == 0) {
-               parent = chain->parent;
-               while (parent &&
-                      (parent->flags & HAMMER2_CHAIN_SUBMODIFIED) == 0) {
-                       atomic_set_int(&parent->flags,
-                                      HAMMER2_CHAIN_SUBMODIFIED);
-                       parent = parent->parent;
-               }
+       parent = chain->parent;
+       while (parent && (parent->flags & HAMMER2_CHAIN_SUBMODIFIED) == 0) {
+               atomic_set_int(&parent->flags, HAMMER2_CHAIN_SUBMODIFIED);
+               parent = parent->parent;
        }
 }
 
@@ -139,8 +134,16 @@ hammer2_chain_alloc(hammer2_mount_t *hmp, hammer2_blockref_t *bref)
                panic("hammer2_chain_alloc: unrecognized blockref type: %d",
                      bref->type);
        }
+
+       /*
+        * Only set bref_flush if the bref has a real media offset, otherwise
+        * the caller has to wait for the chain to be modified/block-allocated
+        * before a blockref can be synchronized with its (future) parent.
+        */
        chain->bref = *bref;
-       chain->index = -1;      /* not yet assigned */
+       if (bref->data_off & ~HAMMER2_OFF_MASK_RADIX)
+               chain->bref_flush = *bref;
+       chain->index = -1;              /* not yet assigned */
        chain->refs = 1;
        chain->bytes = bytes;
        lockmgr(&chain->lk, LK_EXCLUSIVE);
@@ -192,8 +195,8 @@ hammer2_chain_ref(hammer2_mount_t *hmp, hammer2_chain_t *chain)
  * inode or indirect block) will be freed and the parent will be
  * recursively dropped.
  *
- * Modified elements hold an additional reference so it should not be
- * possible for the count on a modified element to drop to 0.
+ * MOVED and MODIFIED elements hold additional references so it should not
+ * be possible for the count on a modified element to drop to 0.
  *
  * The chain element must NOT be locked by the caller.
  *
@@ -222,6 +225,9 @@ hammer2_chain_drop(hammer2_mount_t *hmp, hammer2_chain_t *chain)
                                 * so no delta data or inode count updates
                                 * should be needed.
                                 */
+                               KKASSERT((chain->flags &
+                                         (HAMMER2_CHAIN_MOVED |
+                                          HAMMER2_CHAIN_MODIFIED)) == 0);
                                if (!(chain->flags & HAMMER2_CHAIN_DELETED)) {
                                        SPLAY_REMOVE(hammer2_chain_splay,
                                                     &parent->shead, chain);
@@ -545,9 +551,14 @@ hammer2_chain_unlock(hammer2_mount_t *hmp, hammer2_chain_t *chain)
  * smaller without reallocating the storage.  Resizing larger will reallocate
  * the storage.
  *
- * Must be passed a locked chain.  If you want the resize to copy the data
- * you should lock the chain with RESOLVE_MAYBE or RESOLVE_ALWAYS, otherwise
- * the resize operation will not copy the data.
+ * Must be passed a locked chain.
+ *
+ * If you want the resize code to copy the data to the new block then the
+ * caller should lock the chain RESOLVE_MAYBE or RESOLVE_ALWAYS.
+ *
+ * If the caller already holds a logical buffer containing the data and
+ * intends to bdwrite() that buffer resolve with RESOLVE_NEVER.  The resize
+ * operation will then not copy the data.
  *
  * This function is mostly used with DATA blocks locked RESOLVE_NEVER in order
  * to avoid instantiating a device buffer that conflicts with the vnode
@@ -587,11 +598,18 @@ hammer2_chain_resize(hammer2_inode_t *ip, hammer2_chain_t *chain,
        /*
         * Set MODIFIED and add a chain ref to prevent destruction.  Both
         * modified flags share the same ref.
+        *
+        * If the chain is already marked MODIFIED then we can safely
+        * return the previous allocation to the pool without having to
+        * worry about snapshots.
         */
        if ((chain->flags & HAMMER2_CHAIN_MODIFIED) == 0) {
                atomic_set_int(&chain->flags, HAMMER2_CHAIN_MODIFIED |
                                              HAMMER2_CHAIN_MODIFY_TID);
                hammer2_chain_ref(hmp, chain);
+       } else {
+               hammer2_freemap_free(hmp, chain->bref.data_off,
+                                    chain->bref.type);
        }
 
        /*
@@ -645,8 +663,13 @@ hammer2_chain_resize(hammer2_inode_t *ip, hammer2_chain_t *chain,
                 *
                 * NOTE: Because of the reallocation we have to set DIRTYBP
                 *       if INITIAL is not set.
+                *
+                * NOTE: We set B_NOCACHE to throw away the previous bp and
+                *       any VM backing store, even if it was dirty.
+                *       Otherwise we run the risk of a logical/device
+                *       conflict on reallocation.
                 */
-               chain->bp->b_flags |= B_RELBUF;
+               chain->bp->b_flags |= B_RELBUF | B_NOCACHE;
                brelse(chain->bp);
                chain->bp = nbp;
                chain->data = (void *)bdata;
@@ -1472,7 +1495,7 @@ again:
        }
 
        /*
-        * If no free blockref count be found we must create an indirect
+        * If no free blockref could be found we must create an indirect
         * block and move a number of blockrefs into it.  With the parent
         * locked we can safely lock each child in order to move it without
         * causing a deadlock.
@@ -1574,11 +1597,12 @@ again:
                 * to ensure that its state propagates up the newly
                 * connected parent.
                 *
-                * We cannot depend on the chain being in a MODIFIED
-                * state, or it might already be in that state, so
-                * even if the parent calls hammer2_chain_modify()
-                * MOVED might not get set.  Thus we have to set it
-                * here, too.
+                * Make sure MOVED is set but do not update bref_flush.  If
+                * the chain is undergoing modification bref_flush will be
+                * updated when it gets flushed.  If it is not then the
+                * bref may not have been flushed yet and we do not want to
+                * set MODIFIED here as this could result in unnecessary
+                * reallocations.
                 */
                if ((chain->flags & HAMMER2_CHAIN_MOVED) == 0) {
                        hammer2_chain_ref(hmp, chain);
@@ -1596,8 +1620,10 @@ done:
 /*
  * Create an indirect block that covers one or more of the elements in the
  * current parent.  Either returns the existing parent with no locking or
- * ref changes or returns the new indirect block locked and referenced,
- * depending on what the specified key falls into.
+ * ref changes or returns the new indirect block locked and referenced
+ * and leaving the original parent lock/ref intact as well.
+ *
+ * The returned chain depends on where the specified key falls.
  *
  * The key/keybits for the indirect mode only needs to follow three rules:
  *
@@ -1648,15 +1674,13 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
 
        /*
         * Calculate the base blockref pointer or NULL if the chain
-        * is known to be empty.
+        * is known to be empty.  We need to calculate the array count
+        * for SPLAY lookups either way.
         */
        hammer2_chain_modify(hmp, parent, HAMMER2_MODIFY_OPTDATA);
        if (parent->flags & HAMMER2_CHAIN_INITIAL) {
                base = NULL;
 
-               /*
-                * We still need to calculate the count for SPLAY lookups
-                */
                switch(parent->bref.type) {
                case HAMMER2_BREF_TYPE_INODE:
                        count = HAMMER2_SET_COUNT;
@@ -1675,9 +1699,6 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                        break;
                }
        } else {
-               /*
-                * Locate a free blockref in the parent's array
-                */
                switch(parent->bref.type) {
                case HAMMER2_BREF_TYPE_INODE:
                        base = &parent->data->ipdata.u.blockset.blockref[0];
@@ -1702,26 +1723,21 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
 
        /*
         * Scan for an unallocated bref, also skipping any slots occupied
-        * by in-memory chain elements that may not yet have been updated
+        * by in-memory chain elements which may not yet have been updated
         * in the parent's bref array.
         */
        bzero(&dummy, sizeof(dummy));
        for (i = 0; i < count; ++i) {
                int nkeybits;
 
-               /*
-                * Optimize the case where the parent is still in its
-                * initially created state.
-                */
-               if (base == NULL || base[i].type == 0) {
-                       dummy.index = i;
-                       chain = SPLAY_FIND(hammer2_chain_splay,
-                                          &parent->shead, &dummy);
-                       if (chain == NULL)
-                               continue;
+               dummy.index = i;
+               chain = SPLAY_FIND(hammer2_chain_splay, &parent->shead, &dummy);
+               if (chain) {
                        bref = &chain->bref;
-               } else {
+               } else if (base && base[i].type) {
                        bref = &base[i];
+               } else {
+                       continue;
                }
 
                /*
@@ -1767,7 +1783,7 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
 
        /*
         * Adjust keybits to represent half of the full range calculated
-        * above.
+        * above (radix 63 max)
         */
        --keybits;
 
@@ -1833,21 +1849,16 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                 * anyway so we can avoid checking the cache when the media
                 * has a key.
                 */
-               if (base == NULL || base[i].type == 0) {
-                       dummy.index = i;
-                       chain = SPLAY_FIND(hammer2_chain_splay,
-                                          &parent->shead, &dummy);
-                       if (chain == NULL) {
-                               /*
-                                * Select index indirect block is placed in
-                                */
-                               if (ichain->index < 0)
-                                       ichain->index = i;
-                               continue;
-                       }
+               dummy.index = i;
+               chain = SPLAY_FIND(hammer2_chain_splay, &parent->shead, &dummy);
+               if (chain) {
                        bref = &chain->bref;
-               } else {
+               } else if (base && base[i].type) {
                        bref = &base[i];
+               } else {
+                       if (ichain->index < 0)
+                               ichain->index = i;
+                       continue;
                }
 
                /*
@@ -1861,8 +1872,8 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                }
 
                /*
-                * This element is being moved, its slot is available
-                * for our indirect block.
+                * This element is being moved from the parent, its slot
+                * is available for our new indirect block.
                 */
                if (ichain->index < 0)
                        ichain->index = i;
@@ -1873,9 +1884,14 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                 * new parent (ichain).
                 *
                 * When adjusting the parent/child relationship we must
-                * set the MOVED bit if we do not otherwise set the
-                * MODIFIED bit, and call setsubmod() to ensure that the
-                * parent sees the bref adjustment.
+                * set the MOVED bit but we do NOT update bref_flush
+                * because otherwise we might synchronize a bref that has
+                * not yet been flushed.  We depend on chain's bref_flush
+                * either being correct or the chain being in a MODIFIED
+                * state.
+                *
+                * We do not want to set MODIFIED here as this would result
+                * in unnecessary reallocations.
                 *
                 * We must still set SUBMODIFIED in the parent but we do
                 * that after the loop.
@@ -1930,13 +1946,13 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
         * recursively.
         */
        hammer2_chain_modify(hmp, ichain, HAMMER2_MODIFY_OPTDATA);
-       atomic_set_int(&ichain->flags, HAMMER2_CHAIN_SUBMODIFIED);
        hammer2_chain_parent_setsubmod(hmp, ichain);
+       atomic_set_int(&ichain->flags, HAMMER2_CHAIN_SUBMODIFIED);
 
        /*
         * Figure out what to return.
         */
-       if (create_bits >= keybits) {
+       if (create_bits > keybits) {
                /*
                 * Key being created is way outside the key range,
                 * return the original parent.
@@ -1952,6 +1968,7 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
        } else {
                /*
                 * Otherwise its in the range, return the new parent.
+                * (leave both the new and old parent locked).
                 */
                parent = ichain;
        }
@@ -1971,10 +1988,14 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
  * The caller must pass a pointer to the chain's parent, also locked and
  * referenced.  (*parentp) will be modified in a manner similar to a lookup
  * or iteration when indirect blocks are also deleted as a side effect.
+ *
+ * XXX This currently does not adhere to the MOVED flag protocol in that
+ *     the removal is immediately indicated in the parent's blockref[]
+ *     array.
  */
 void
 hammer2_chain_delete(hammer2_mount_t *hmp, hammer2_chain_t *parent,
-                    hammer2_chain_t *chain)
+                    hammer2_chain_t *chain, int retain)
 {
        hammer2_blockref_t *base;
        hammer2_inode_t *ip;
@@ -2054,6 +2075,30 @@ hammer2_chain_delete(hammer2_mount_t *hmp, hammer2_chain_t *parent,
        }
 
        /*
+        * If retain is 0 the deletion is permanent.  Because the chain is
+        * no longer connected to the topology a flush will have no
+        * visibility into it.  We must dispose of the references related
+        * to the MODIFIED and MOVED flags, otherwise the ref count will
+        * never transition to 0.
+        *
+        * If retain is non-zero the deleted element is likely an inode
+        * which the vnops frontend will mark DESTROYED and flush.  In that
+        * situation we must retain the flags for any open file descriptors
+        * on the (removed) inode.  The final close will destroy the
+        * disconnected chain.
+        */
+       if (retain == 0) {
+               if (chain->flags & HAMMER2_CHAIN_MODIFIED) {
+                       atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MODIFIED);
+                       hammer2_chain_drop(hmp, chain);
+               }
+               if (chain->flags & HAMMER2_CHAIN_MOVED) {
+                       atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MOVED);
+                       hammer2_chain_drop(hmp, chain);
+               }
+       }
+
+       /*
         * The chain is still likely referenced, possibly even by a vnode
         * (if an inode), so defer further action until the chain gets
         * dropped.
@@ -2090,6 +2135,7 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
        char *bdata;
        struct buf *bp;
        int error;
+       int wasmodified;
 
        /*
         * If we hit the stack recursion depth limit defer the operation.
@@ -2119,33 +2165,27 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                        chain, chain->refs, chain->flags);
 
        /*
-        * Flush any children of this chain.
+        * If SUBMODIFIED is set we recurse the flush and adjust the
+        * blockrefs accordingly.
         *
-        * NOTE: If we use a while() here an active filesystem can
-        *       prevent the flush from ever finishing.
+        * NOTE: Looping on SUBMODIFIED can prevent a flush from ever
+        *       finishing in the face of filesystem activity.
         */
        if (chain->flags & HAMMER2_CHAIN_SUBMODIFIED) {
-               hammer2_blockref_t *base;
                hammer2_chain_t *child;
                hammer2_chain_t *next;
+               hammer2_blockref_t *base;
                int count;
-               int submodified = 0;
-               int submoved = 0;
 
                /*
-                * Clear SUBMODIFIED now.  Flag any races during the flush
-                * with the (submodified) local variable and re-arm it
-                * as necessary after the loop is done.
+                * Clear SUBMODIFIED to catch races.  Note that if any
+                * child has to be flushed SUBMODIFIED will wind up being
+                * set again (for next time), but this does not stop us from
+                * synchronizing block updates which occurred.
                 *
-                * Delaying the setting of the chain to MODIFIED can reduce
-                * unnecessary I/O.
-                *
-                * Modifications to the children will propagate up, forcing
-                * us to become modified and copy-on-write too.  Be sure
-                * to modify chain (as a side effect of the recursive
-                * flush) ONLY if it is actually being modified by the
-                * recursive flush.
+                * We don't want to set our chain to MODIFIED gratuitously.
                 */
+               /* XXX SUBMODIFIED not interlocked, can race */
                atomic_clear_int(&chain->flags, HAMMER2_CHAIN_SUBMODIFIED);
 
                /*
@@ -2153,9 +2193,13 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                 * Be careful of ripouts during the loop.
                 */
                next = SPLAY_MIN(hammer2_chain_splay, &chain->shead);
+               if (next)
+                       hammer2_chain_ref(hmp, next);
                while ((child = next) != NULL) {
                        next = SPLAY_NEXT(hammer2_chain_splay,
                                          &chain->shead, child);
+                       if (next)
+                               hammer2_chain_ref(hmp, next);
                        /*
                         * We only recurse if SUBMODIFIED (internal node)
                         * or MODIFIED (internal node or leaf) is set.
@@ -2163,11 +2207,19 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                         * entries are present to determine if the chain's
                         * blockref's need updating or not.
                         */
-                       if (child->flags & HAMMER2_CHAIN_MOVED)
-                               submoved = 1;
                        if ((child->flags & (HAMMER2_CHAIN_SUBMODIFIED |
                                             HAMMER2_CHAIN_MODIFIED |
                                            HAMMER2_CHAIN_MODIFIED_AUX)) == 0) {
+                               hammer2_chain_drop(hmp, child);
+                               continue;
+                       }
+                       hammer2_chain_lock(hmp, child, HAMMER2_RESOLVE_MAYBE);
+                       hammer2_chain_drop(hmp, child);
+                       if (child->parent != chain ||
+                           (child->flags & (HAMMER2_CHAIN_SUBMODIFIED |
+                                            HAMMER2_CHAIN_MODIFIED |
+                                           HAMMER2_CHAIN_MODIFIED_AUX)) == 0) {
+                               hammer2_chain_unlock(hmp, child);
                                continue;
                        }
 
@@ -2175,7 +2227,6 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                         * Propagate the DESTROYED flag if found set, then
                         * recurse the flush.
                         */
-                       hammer2_chain_lock(hmp, child, HAMMER2_RESOLVE_MAYBE);
                        if ((chain->flags & HAMMER2_CHAIN_DESTROYED) &&
                            (child->flags & HAMMER2_CHAIN_DESTROYED) == 0) {
                                atomic_set_int(&child->flags,
@@ -2185,73 +2236,33 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                        ++info->depth;
                        hammer2_chain_flush_pass1(hmp, child, info);
                        --info->depth;
-
-                       /*
-                        * No point loading blockrefs yet if the
-                        * child (recursively) is still dirty.
-                        */
-                       if (child->flags & (HAMMER2_CHAIN_SUBMODIFIED |
-                                          HAMMER2_CHAIN_MODIFIED |
-                                          HAMMER2_CHAIN_MODIFIED_AUX)) {
-                               submodified = 1;
-                               if (hammer2_debug & 0x0008)
-                                       kprintf("s");
-                       }
-                       if (child->flags & HAMMER2_CHAIN_MOVED) {
-                               if (hammer2_debug & 0x0008)
-                                       kprintf("m");
-                               submoved = 1;
-                       }
-                       if (hammer2_debug & 0x0008)
-                               kprintf("\n");
                        hammer2_chain_unlock(hmp, child);
                }
 
                /*
-                * If the sub-tree was not completely synced we currently do
-                * not attempt to propagate the bref all the way back up.
-                * Our bref pointers to the children are not updated yet in
-                * this situation but the children will have CHAIN_MOVED set
-                * and cannot be destroyed until the parent synchronizes
-                * the brefs.
-                *
-                * If the sub-tree had to be recursed the bref propagates
-                * back up and may require 'chain' to become modified.
-                *
+                * Now synchronize any block updates.
                 */
-               if (submodified ||
-                   (chain->flags & HAMMER2_CHAIN_SUBMODIFIED)) {
-                       /*
-                        * No point loading up the blockrefs if submodified
-                        * got re-set.  The modified and flushed children
-                        * will have set HAMMER2_CHAIN_MOVED and cannot be
-                        * freed until we've synchronized the case.
-                        *
-                        * NOTE: Even though we cleared the SUBMODIFIED flag
-                        *       it can still get re-set by operations
-                        *       occuring under our chain, so check both.
-                        */
-                       atomic_set_int(&chain->flags,
-                                      HAMMER2_CHAIN_SUBMODIFIED);
-               } else if (submoved) {
-                       /*
-                        * Ok, we can modify the blockrefs in this chain
-                        * entry.  Mark it modified.  Calculate the
-                        * blockref array after marking it modified (since
-                        * that may change the underlying data ptr).
-                        *
-                        * NOTE: We only do this if submoved != 0, otherwise
-                        *       there may not be any changes and setting
-                        *       the chain modified will re-arm the MOVED
-                        *       bit recursively, resulting in O(N^2)
-                        *       flushes.
-                        *
-                        * NOTE: We don't want hammer2_chain_modify() to
-                        *       recursively set the SUBMODIFIED flag
-                        *       upward in this case!
-                        */
+               next = SPLAY_MIN(hammer2_chain_splay, &chain->shead);
+               if (next)
+                       hammer2_chain_ref(hmp, next);
+               while ((child = next) != NULL) {
+                       next = SPLAY_NEXT(hammer2_chain_splay,
+                                         &chain->shead, child);
+                       if (next)
+                               hammer2_chain_ref(hmp, next);
+                       if ((child->flags & HAMMER2_CHAIN_MOVED) == 0) {
+                               hammer2_chain_drop(hmp, child);
+                               continue;
+                       }
+                       hammer2_chain_lock(hmp, child, HAMMER2_RESOLVE_NEVER);
+                       hammer2_chain_drop(hmp, child);
+                       if (child->parent != chain ||
+                           (child->flags & HAMMER2_CHAIN_MOVED) == 0) {
+                               hammer2_chain_unlock(hmp, child);
+                               continue;
+                       }
+
                        hammer2_chain_modify(hmp, chain,
-                                            HAMMER2_MODIFY_NOSUB |
                                             HAMMER2_MODIFY_NO_MODIFY_TID);
 
                        switch(chain->bref.type) {
@@ -2278,47 +2289,24 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                                      chain->bref.type);
                        }
 
-                       /*
-                        * Update the blockrefs.
-                        *
-                        * When updating the blockset embedded in the volume
-                        * header we must also update voldata.mirror_tid.
-                        */
-                       next = SPLAY_MIN(hammer2_chain_splay, &chain->shead);
-                       while ((child = next) != NULL) {
-                               next = SPLAY_NEXT(hammer2_chain_splay,
-                                                 &chain->shead, child);
-                               KKASSERT(child->index >= 0 &&
-                                        child->index < count);
-                               hammer2_chain_lock(hmp, child,
-                                                  HAMMER2_RESOLVE_NEVER);
-                               KKASSERT(child->parent == chain);
-                               if (child->flags & HAMMER2_CHAIN_MOVED) {
-                                       base[child->index] = child->bref;
-                                       if (chain->bref.mirror_tid <
-                                           child->bref.mirror_tid) {
-                                               chain->bref.mirror_tid =
-                                                       child->bref.mirror_tid;
-                                       }
-                                       if (chain->bref.type ==
-                                            HAMMER2_BREF_TYPE_VOLUME &&
-                                           hmp->voldata.mirror_tid <
-                                            child->bref.mirror_tid) {
-                                               hmp->voldata.mirror_tid =
-                                                       child->bref.mirror_tid;
-                                       }
-                                       atomic_clear_int(&child->flags,
-                                                HAMMER2_CHAIN_MOVED);
-                                       hammer2_chain_drop(hmp, child);
-                               } else if (bcmp(&base[child->index],
-                                          &child->bref,
-                                          sizeof(child->bref)) != 0) {
-                                       kprintf("child %p index %d\n",
-                                               child, child->index);
-                                       panic("hammer2: unflagged bref update");
-                               }
-                               hammer2_chain_unlock(hmp, child);
+                       KKASSERT(child->index >= 0);
+                       base[child->index] = child->bref_flush;
+
+                       if (chain->bref.mirror_tid <
+                           child->bref_flush.mirror_tid) {
+                               chain->bref.mirror_tid =
+                                       child->bref_flush.mirror_tid;
+                       }
+
+                       if (chain->bref.type == HAMMER2_BREF_TYPE_VOLUME &&
+                           hmp->voldata.mirror_tid <
+                           child->bref_flush.mirror_tid) {
+                               hmp->voldata.mirror_tid =
+                                       child->bref_flush.mirror_tid;
                        }
+                       atomic_clear_int(&child->flags, HAMMER2_CHAIN_MOVED);
+                       hammer2_chain_drop(hmp, child); /* MOVED flag */
+                       hammer2_chain_unlock(hmp, child);
                }
        }
 
@@ -2385,6 +2373,16 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
        }
 
        /*
+        * Flush if MODIFIED or MODIFIED_AUX is set.  MODIFIED_AUX is only
+        * used by the volume header (&hmp->vchain).
+        */
+       if ((chain->flags & (HAMMER2_CHAIN_MODIFIED |
+                            HAMMER2_CHAIN_MODIFIED_AUX)) == 0) {
+               goto done;
+       }
+       atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MODIFIED_AUX);
+
+       /*
         * Clear MODIFIED and set HAMMER2_CHAIN_MOVED.  The caller
         * will re-test the MOVED bit.  We must also update the mirror_tid
         * and modify_tid fields as appropriate.
@@ -2392,20 +2390,29 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
         * bits own a single chain ref and the MOVED bit owns its own
         * chain ref.
         */
-       if (chain->flags & HAMMER2_CHAIN_MODIFIED) {
-               chain->bref.mirror_tid = info->modify_tid;
-               if (chain->flags & HAMMER2_CHAIN_MODIFY_TID)
-                       chain->bref.modify_tid = info->modify_tid;
-               atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MODIFIED |
-                                               HAMMER2_CHAIN_MODIFY_TID);
-               if (chain->flags & HAMMER2_CHAIN_MOVED) {
+       chain->bref.mirror_tid = info->modify_tid;
+       if (chain->flags & HAMMER2_CHAIN_MODIFY_TID)
+               chain->bref.modify_tid = info->modify_tid;
+       wasmodified = (chain->flags & HAMMER2_CHAIN_MODIFIED) != 0;
+       atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MODIFIED |
+                                       HAMMER2_CHAIN_MODIFY_TID);
+
+       if (chain->flags & HAMMER2_CHAIN_MOVED) {
+               /*
+                * Drop the ref from the MODIFIED bit we cleared.
+                */
+               if (wasmodified)
                        hammer2_chain_drop(hmp, chain);
-               } else {
-                       /* inherit ref from the MODIFIED we cleared */
-                       atomic_set_int(&chain->flags, HAMMER2_CHAIN_MOVED);
-               }
+       } else {
+               /*
+                * If we were MODIFIED we inherit the ref from clearing
+                * that bit, otherwise we need another ref.
+                */
+               if (wasmodified == 0)
+                       hammer2_chain_ref(hmp, chain);
+               atomic_set_int(&chain->flags, HAMMER2_CHAIN_MOVED);
        }
-       atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MODIFIED_AUX);
+       chain->bref_flush = chain->bref;
 
        /*
         * If this is part of a recursive flush we can go ahead and write
@@ -2592,6 +2599,7 @@ hammer2_chain_flush(hammer2_mount_t *hmp, hammer2_chain_t *chain,
        if (modify_tid == 0) {
                hammer2_voldata_lock(hmp);
                info.modify_tid = hmp->voldata.alloc_tid++;
+               atomic_set_int(&hmp->vchain.flags, HAMMER2_CHAIN_MODIFIED_AUX);
                hammer2_voldata_unlock(hmp);
        } else {
                info.modify_tid = modify_tid;
@@ -2730,13 +2738,12 @@ hammer2_chain_flush(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                 chain->index < count);
        KKASSERT(chain->parent == parent);
        if (chain->flags & HAMMER2_CHAIN_MOVED) {
-               base[chain->index] = chain->bref;
-               if (parent->bref.mirror_tid < chain->bref.mirror_tid)
-                       parent->bref.mirror_tid = chain->bref.mirror_tid;
+               base[chain->index] = chain->bref_flush;
+               if (parent->bref.mirror_tid < chain->bref_flush.mirror_tid)
+                       parent->bref.mirror_tid = chain->bref_flush.mirror_tid;
                atomic_clear_int(&chain->flags, HAMMER2_CHAIN_MOVED);
                hammer2_chain_drop(hmp, chain);
-       } else if (bcmp(&base[chain->index],
-                  &chain->bref,
+       } else if (bcmp(&base[chain->index], &chain->bref_flush,
                   sizeof(chain->bref)) != 0) {
                panic("hammer2: unflagged bref update(2)");
        }
index 242f7c4..13a160c 100644 (file)
@@ -57,6 +57,7 @@ hammer2_freemap_alloc(hammer2_mount_t *hmp, int type, size_t bytes)
 {
        hammer2_off_t data_off;
        hammer2_off_t data_next;
+       hammer2_freecache_t *fc;
        /*struct buf *bp;*/
        int radix;
        int fctype;
@@ -82,15 +83,26 @@ hammer2_freemap_alloc(hammer2_mount_t *hmp, int type, size_t bytes)
        radix = hammer2_bytes_to_radix(bytes);
        bytes = 1 << radix;
 
+       if (radix <= HAMMER2_MAX_RADIX)
+               fc = &hmp->freecache[fctype][radix];
+       else
+               fc = NULL;
+
        lockmgr(&hmp->alloclk, LK_EXCLUSIVE);
-       if (radix <= HAMMER2_MAX_RADIX && hmp->freecache[fctype][radix]) {
+       if (fc && fc->single) {
+               /*
+                * Allocate from our single-block cache.
+                */
+               data_off = fc->single;
+               fc->single = 0;
+       } else if (fc && fc->bulk) {
                /*
-                * Allocate from our packing cache
+                * Allocate from our packing cache.
                 */
-               data_off = hmp->freecache[fctype][radix];
-               hmp->freecache[fctype][radix] += bytes;
-               if ((hmp->freecache[fctype][radix] & HAMMER2_SEGMASK) == 0)
-                       hmp->freecache[fctype][radix] = 0;
+               data_off = fc->bulk;
+               fc->bulk += bytes;
+               if ((fc->bulk & HAMMER2_SEGMASK) == 0)
+                       fc->bulk = 0;
        } else {
                /*
                 * Allocate from the allocation iterator using a SEGSIZE
@@ -98,6 +110,7 @@ hammer2_freemap_alloc(hammer2_mount_t *hmp, int type, size_t bytes)
                 *
                 * Skip reserved areas at the beginning of each zone.
                 */
+               hammer2_voldata_lock(hmp);
                data_off = hmp->voldata.allocator_beg;
                data_off = (data_off + HAMMER2_SEGMASK64) & ~HAMMER2_SEGMASK64;
                if ((data_off & HAMMER2_ZONE_MASK64) < HAMMER2_ZONE_SEG) {
@@ -113,8 +126,10 @@ hammer2_freemap_alloc(hammer2_mount_t *hmp, int type, size_t bytes)
                        hmp->voldata.allocator_beg =
                                        (data_next + HAMMER2_SEGMASK64) &
                                        ~HAMMER2_SEGMASK64;
-                       hmp->freecache[fctype][radix] = data_next;
+                       fc->bulk = data_next;
                }
+               atomic_set_int(&hmp->vchain.flags, HAMMER2_CHAIN_MODIFIED_AUX);
+               hammer2_voldata_unlock(hmp);
        }
        lockmgr(&hmp->alloclk, LK_RELEASE);
 
@@ -149,6 +164,40 @@ hammer2_freemap_alloc(hammer2_mount_t *hmp, int type, size_t bytes)
        return (data_off | radix);
 }
 
+void
+hammer2_freemap_free(hammer2_mount_t *hmp, hammer2_off_t data_off, int type)
+{
+       hammer2_freecache_t *fc;
+       int radix;
+       int fctype;
+
+       switch(type) {
+       case HAMMER2_BREF_TYPE_INODE:
+               fctype = HAMMER2_FREECACHE_INODE;
+               break;
+       case HAMMER2_BREF_TYPE_INDIRECT:
+               fctype = HAMMER2_FREECACHE_INODE;
+               break;
+       case HAMMER2_BREF_TYPE_DATA:
+               fctype = HAMMER2_FREECACHE_DATA;
+               break;
+       default:
+               fctype = HAMMER2_FREECACHE_DATA;
+               break;
+       }
+       radix = (int)data_off & HAMMER2_OFF_MASK_RADIX;
+       data_off &= ~HAMMER2_OFF_MASK_RADIX;
+       if (radix >= HAMMER2_MAX_RADIX)
+               return;
+
+       fc = &hmp->freecache[fctype][radix];
+       if (fc->single == 0) {
+               lockmgr(&hmp->alloclk, LK_EXCLUSIVE);
+               fc->single = data_off;
+               lockmgr(&hmp->alloclk, LK_RELEASE);
+       }
+}
+
 #if 0
 /*
  * Allocate media space, returning a combined data offset and radix.
index 8ae794d..45f541f 100644 (file)
@@ -327,6 +327,8 @@ hammer2_inode_create(hammer2_inode_t *dip,
  * If name is NULL the inode is duplicated as a hidden directory entry.
  *
  * Returns the new inode.  The old inode is left alone.
+ *
+ * XXX name needs to be NULL for now.
  */
 int
 hammer2_inode_duplicate(hammer2_inode_t *dip, hammer2_inode_t *oip,
@@ -337,7 +339,6 @@ hammer2_inode_duplicate(hammer2_inode_t *dip, hammer2_inode_t *oip,
        hammer2_inode_t *nip;
        hammer2_chain_t *parent;
        hammer2_chain_t *chain;
-       hammer2_chain_t *scan;
        hammer2_key_t lhc;
        int error;
 
@@ -362,17 +363,18 @@ hammer2_inode_duplicate(hammer2_inode_t *dip, hammer2_inode_t *oip,
                chain = hammer2_chain_lookup(hmp, &parent, lhc, lhc, 0);
                if (chain == NULL)
                        break;
+               /* XXX bcmp name if not NULL */
                if ((lhc & HAMMER2_DIRHASH_LOMASK) == HAMMER2_DIRHASH_LOMASK)
                        error = ENOSPC;
+               if ((lhc & HAMMER2_DIRHASH_VISIBLE) == 0) /* shouldn't happen */
+                       error = ENOSPC;
                hammer2_chain_unlock(hmp, chain);
                chain = NULL;
                ++lhc;
        }
 
        /*
-        * Passing a non-NULL chain to hammer2_chain_create() reconnects the
-        * existing chain instead of creating a new one.  The chain's bref
-        * will be properly updated.
+        * Create entry in common parent directory.
         */
        if (error == 0) {
                chain = hammer2_chain_create(hmp, parent, NULL, lhc, 0,
@@ -390,9 +392,6 @@ hammer2_inode_duplicate(hammer2_inode_t *dip, hammer2_inode_t *oip,
                KKASSERT(chain == NULL);
                return (error);
        }
-       nip = chain->u.ip;
-       hammer2_chain_modify(hmp, chain, 0);
-       nip->ip_data = oip->ip_data;
 
        /*
         * XXX This is currently a horrible hack.  Well, if we wanted to
@@ -417,6 +416,10 @@ hammer2_inode_duplicate(hammer2_inode_t *dip, hammer2_inode_t *oip,
        hammer2_inode_unlock_ex(oip);
        KKASSERT(SPLAY_EMPTY(&oip->chain.shead));
 
+       nip = chain->u.ip;
+       hammer2_chain_modify(hmp, chain, 0);
+       nip->ip_data = oip->ip_data;    /* sync media data after flush */
+
        if (name) {
                /*
                 * Directory entries are inodes so if the name has changed
@@ -467,7 +470,13 @@ hammer2_inode_connect(hammer2_inode_t *dip, hammer2_inode_t *oip,
        int hlink;
 
        lhc = hammer2_dirhash(name, name_len);
-       hlink = ((oip->chain.flags & HAMMER2_CHAIN_DELETED) == 0);
+       hlink = (oip->chain.parent != NULL);
+
+       /*
+        * In fake mode flush oip so we can just snapshot it downbelow.
+        */
+       if (hlink && hammer2_hardlink_enable < 0)
+               hammer2_chain_flush(hmp, &oip->chain, 0);
 
        /*
         * Locate the inode or indirect block to create the new
@@ -529,7 +538,7 @@ hammer2_inode_connect(hammer2_inode_t *dip, hammer2_inode_t *oip,
         * chain, the caller will access the hardlink via the actual hardlink
         * target file and not the hardlink pointer entry.
         */
-       if (hlink) {
+       if (hlink && hammer2_hardlink_enable >= 0) {
                /*
                 * Create the HARDLINK pointer.  oip represents the hardlink
                 * target in this situation.
@@ -547,9 +556,25 @@ hammer2_inode_connect(hammer2_inode_t *dip, hammer2_inode_t *oip,
                kprintf("created hardlink %*.*s\n",
                        (int)name_len, (int)name_len, name);
                hammer2_chain_unlock(hmp, chain);
+       } else if (hlink && hammer2_hardlink_enable < 0) {
+               /*
+                * Create a snapshot (hardlink fake mode for debugging).
+                */
+               nip = chain->u.ip;
+               nip->ip_data = oip->ip_data;
+               hammer2_chain_modify(hmp, chain, 0);
+               KKASSERT(name_len < HAMMER2_INODE_MAXNAME);
+               bcopy(name, nip->ip_data.filename, name_len);
+               nip->ip_data.name_key = lhc;
+               nip->ip_data.name_len = name_len;
+               kprintf("created fake hardlink %*.*s\n",
+                       (int)name_len, (int)name_len, name);
+               hammer2_chain_unlock(hmp, chain);
        } else {
                /*
-                * The name may have changed on reconnect, adjust oip.
+                * Normally disconnected inode (e.g. during a rename) that
+                * was reconnected.  We must fixup the name stored in
+                * oip.
                 *
                 * We are using oip as chain, already locked by caller,
                 * do not unlock it.
@@ -577,7 +602,8 @@ hammer2_inode_connect(hammer2_inode_t *dip, hammer2_inode_t *oip,
  */
 int
 hammer2_unlink_file(hammer2_inode_t *dip,
-                   const uint8_t *name, size_t name_len, int isdir)
+                   const uint8_t *name, size_t name_len,
+                   int isdir, hammer2_inode_t *retain_ip)
 {
        hammer2_mount_t *hmp;
        hammer2_chain_t *parent;
@@ -671,9 +697,10 @@ hammer2_unlink_file(hammer2_inode_t *dip,
         * if the entry can be deleted in case someone has the file open and
         * does an fstat().
         *
-        * The chain itself will no longer have a media reference.  When the
-        * last vnode/ip ref goes away the chain will be marked unmodified,
-        * avoiding unnecessary I/O.
+        * The chain itself will no longer be in the on-media topology but
+        * can still be flushed to the media (e.g. if an open descriptor
+        * remains).  When the last vnode/ip ref goes away the chain will
+        * be marked unmodified, avoiding any further (now unnecesary) I/O.
         */
        if (oip) {
                /*
@@ -683,7 +710,8 @@ hammer2_unlink_file(hammer2_inode_t *dip,
                parent = oip->chain.parent;
                hammer2_chain_lock(hmp, parent, HAMMER2_RESOLVE_ALWAYS);
                hammer2_chain_lock(hmp, &oip->chain, HAMMER2_RESOLVE_ALWAYS);
-               hammer2_chain_delete(hmp, parent, &oip->chain);
+               hammer2_chain_delete(hmp, parent, &oip->chain,
+                                   (retain_ip == oip));
                hammer2_chain_unlock(hmp, &oip->chain);
                hammer2_chain_unlock(hmp, parent);
                parent = NULL;
@@ -702,7 +730,7 @@ hammer2_unlink_file(hammer2_inode_t *dip,
                        hammer2_chain_drop(hmp, chain);
                        hammer2_chain_modify(hmp, chain, 0);
                        --ip->ip_data.nlinks;
-                       hammer2_chain_delete(hmp, dparent, chain);
+                       hammer2_chain_delete(hmp, dparent, chain, 0);
                        hammer2_chain_unlock(hmp, dparent);
                } else {
                        hammer2_chain_modify(hmp, chain, 0);
@@ -716,7 +744,8 @@ hammer2_unlink_file(hammer2_inode_t *dip,
                ip = chain->u.ip;
                hammer2_chain_modify(hmp, chain, 0);
                --ip->ip_data.nlinks;
-               hammer2_chain_delete(hmp, parent, chain);
+               hammer2_chain_delete(hmp, parent, chain,
+                                    (retain_ip == ip));
        }
 
        error = 0;
@@ -899,7 +928,7 @@ hammer2_hardlink_consolidate(hammer2_inode_t **ipp, hammer2_inode_t *tdip)
                                           HAMMER2_RESOLVE_ALWAYS);
                        hammer2_chain_lock(hmp, &oip->chain,
                                           HAMMER2_RESOLVE_ALWAYS);
-                       hammer2_chain_delete(hmp, parent, &oip->chain);
+                       hammer2_chain_delete(hmp, parent, &oip->chain, 0);
                        hammer2_chain_unlock(hmp, &oip->chain);
                        hammer2_chain_unlock(hmp, parent);
                }
@@ -958,10 +987,6 @@ hammer2_hardlink_find(hammer2_inode_t *dip, hammer2_chain_t **chainp,
 
                hammer2_chain_lock(hmp, parent, HAMMER2_RESOLVE_ALWAYS);
                chain = hammer2_chain_lookup(hmp, &parent, lhc, lhc, 0);
-               if (chain) {
-                       kprintf("found hardlink in dir %s\n",
-                               pip->ip_data.filename);
-               }
                hammer2_chain_unlock(hmp, parent);
                if (chain)
                        break;
index 3683e1a..b1d7f06 100644 (file)
@@ -379,6 +379,7 @@ hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
        int error;
 
        error = hammer2_unlink_file(hmp->schain->u.ip,
-                                   pfs->name, strlen(pfs->name), 0);
+                                   pfs->name, strlen(pfs->name),
+                                   0, NULL);
        return (error);
 }
index 51ed554..c356bb5 100644 (file)
@@ -369,9 +369,9 @@ hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
        int radix;
 
        *lbasep = uoff & ~HAMMER2_PBUFMASK64;
-       *leofp = ip->ip_data.size & ~HAMMER2_PBUFMASK;
+       *leofp = ip->ip_data.size & ~HAMMER2_PBUFMASK64;
        KKASSERT(*lbasep <= *leofp);
-       if (*lbasep == *leofp) {
+       if (*lbasep == *leofp /*&& *leofp < 1024 * 1024*/) {
                radix = hammer2_bytes_to_radix(
                                (size_t)(ip->ip_data.size - *leofp));
                if (radix < HAMMER2_MINALLOCRADIX)
index 7c6bf97..0760f18 100644 (file)
@@ -365,10 +365,11 @@ hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data,
                 * vchain setup. vchain.data is special cased to NULL.
                 * vchain.refs is initialized and will never drop to 0.
                 */
-               hmp->vchain.bref.type = HAMMER2_BREF_TYPE_VOLUME;
                hmp->vchain.refs = 1;
                hmp->vchain.data = (void *)&hmp->voldata;
+               hmp->vchain.bref.type = HAMMER2_BREF_TYPE_VOLUME;
                hmp->vchain.bref.data_off = 0 | HAMMER2_PBUFRADIX;
+               hmp->vchain.bref_flush = hmp->vchain.bref;
                /* hmp->vchain.u.xxx is left NULL */
                lockinit(&hmp->vchain.lk, "volume", 0, LK_CANRECURSE);
                lockinit(&hmp->alloclk, "h2alloc", 0, 0);
@@ -570,8 +571,6 @@ hammer2_vfs_unmount(struct mount *mp, int mntflags)
                        vrele(devvp);
                        devvp = NULL;
                }
-               kmalloc_destroy(&hmp->minode);
-               kmalloc_destroy(&hmp->mchain);
        }
        hammer2_mount_unlock(hmp);
 
@@ -582,6 +581,8 @@ hammer2_vfs_unmount(struct mount *mp, int mntflags)
        kfree(pmp, M_HAMMER2);
        if (hmp->pmp_count == 0) {
                TAILQ_REMOVE(&hammer2_mntlist, hmp, mntentry);
+               kmalloc_destroy(&hmp->minode);
+               kmalloc_destroy(&hmp->mchain);
                kfree(hmp, M_HAMMER2);
        }
        lockmgr(&hammer2_mntlk, LK_RELEASE);
index 307b130..d46be99 100644 (file)
@@ -876,15 +876,18 @@ hammer2_write_file(hammer2_inode_t *ip, struct uio *uio,
                if (ioflag & IO_SYNC) {
                        bwrite(bp);
                } else if ((ioflag & IO_DIRECT) && loff + n == lblksize) {
-                       bp->b_flags |= B_CLUSTEROK;
+                       if (bp->b_bcount == HAMMER2_PBUFSIZE)
+                               bp->b_flags |= B_CLUSTEROK;
                        bdwrite(bp);
                } else if (ioflag & IO_ASYNC) {
                        bawrite(bp);
                } else if (hammer2_cluster_enable) {
-                       bp->b_flags |= B_CLUSTEROK;
+                       if (bp->b_bcount == HAMMER2_PBUFSIZE)
+                               bp->b_flags |= B_CLUSTEROK;
                        cluster_write(bp, leof, HAMMER2_PBUFSIZE, seqcount);
                } else {
-                       bp->b_flags |= B_CLUSTEROK;
+                       if (bp->b_bcount == HAMMER2_PBUFSIZE)
+                               bp->b_flags |= B_CLUSTEROK;
                        bdwrite(bp);
                }
        }
@@ -1079,7 +1082,8 @@ hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize)
                                break;
                        }
                        hammer2_chain_unlock(hmp, chain);
-                       bp->b_flags |= B_CLUSTEROK;
+                       if (bp->b_bcount == HAMMER2_PBUFSIZE)
+                               bp->b_flags |= B_CLUSTEROK;
                        bdwrite(bp);
                } else {
                        /*
@@ -1095,9 +1099,12 @@ hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize)
                /*
                 * WARNING: This utilizes a device buffer for the data.
                 *
-                * XXX case should not occur
+                * This case should not occur because file truncations without
+                * a vnode (and hence no logical buffer cache) should only
+                * always truncate to 0-length.
                 */
                panic("hammer2_truncate_file: non-zero truncation, no-vnode");
+#if 0
                chain = hammer2_chain_lookup(hmp, &parent, lbase, lbase, 0);
                if (chain) {
                        switch(chain->bref.type) {
@@ -1118,6 +1125,7 @@ hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize)
                        }
                        hammer2_chain_unlock(hmp, chain);
                }
+#endif
        }
 
        /*
@@ -1152,7 +1160,7 @@ hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize)
                 */
                if (chain->bref.type == HAMMER2_BREF_TYPE_DATA) {
                        ip->delta_dcount -= chain->bytes;
-                       hammer2_chain_delete(hmp, parent, chain);
+                       hammer2_chain_delete(hmp, parent, chain, 0);
                }
                /* XXX check parent if empty indirect block & delete */
                chain = hammer2_chain_next(hmp, &parent, chain,
@@ -1195,6 +1203,11 @@ hammer2_extend_file(hammer2_inode_t *ip, hammer2_key_t nsize)
        if ((ip->ip_data.op_flags & HAMMER2_OPFLAG_DIRECTDATA) &&
            nsize <= HAMMER2_EMBEDDED_BYTES) {
                ip->ip_data.size = nsize;
+               nvextendbuf(ip->vp,
+                           ip->ip_data.size, nsize,
+                           0, HAMMER2_EMBEDDED_BYTES,
+                           0, (int)nsize,
+                           1);
                return;
        }
 
@@ -1227,7 +1240,7 @@ hammer2_extend_file(hammer2_inode_t *ip, hammer2_key_t nsize)
 
        /*
         * We have work to do, including possibly resizing the buffer
-        * at the EOF point and turning off DIRECTDATA mode.
+        * at the previous EOF point and turning off DIRECTDATA mode.
         */
        bp = NULL;
        if (((int)osize & HAMMER2_PBUFMASK)) {
@@ -1235,11 +1248,12 @@ hammer2_extend_file(hammer2_inode_t *ip, hammer2_key_t nsize)
                KKASSERT(error == 0);
 
                if (obase != nbase) {
-                       allocbuf(bp, HAMMER2_PBUFSIZE);
+                       if (oblksize != HAMMER2_PBUFSIZE)
+                               allocbuf(bp, HAMMER2_PBUFSIZE);
                } else {
-                       allocbuf(bp, nblksize);
+                       if (oblksize != nblksize)
+                               allocbuf(bp, nblksize);
                }
-               vfs_bio_clrbuf(bp);
        }
 
        /*
@@ -1279,7 +1293,8 @@ hammer2_extend_file(hammer2_inode_t *ip, hammer2_key_t nsize)
                bp->b_bio2.bio_offset = chain->bref.data_off &
                                        HAMMER2_OFF_MASK;
                hammer2_chain_unlock(hmp, chain);
-               bp->b_flags |= B_CLUSTEROK;
+               if (bp->b_bcount == HAMMER2_PBUFSIZE)
+                       bp->b_flags |= B_CLUSTEROK;
                bdwrite(bp);
                hammer2_chain_unlock(hmp, parent);
        }
@@ -1339,10 +1354,9 @@ hammer2_vop_nresolve(struct vop_nresolve_args *ap)
         */
        ip = NULL;
        if (chain && chain->u.ip->ip_data.type == HAMMER2_OBJTYPE_HARDLINK) {
-               kprintf("hammer2: need to find hardlink for %s\n",
-                       chain->u.ip->ip_data.filename);
                error = hammer2_hardlink_find(dip, &chain, &ip);
                if (error) {
+                       kprintf("hammer2: unable to find hardlink\n");
                        if (chain) {
                                hammer2_chain_unlock(hmp, chain);
                                chain = NULL;
@@ -1796,7 +1810,7 @@ hammer2_vop_nremove(struct vop_nremove_args *ap)
        name = ncp->nc_name;
        name_len = ncp->nc_nlen;
 
-       error = hammer2_unlink_file(dip, name, name_len, 0);
+       error = hammer2_unlink_file(dip, name, name_len, 0, NULL);
 
        if (error == 0) {
                cache_setunresolved(ap->a_nch);
@@ -1828,7 +1842,7 @@ hammer2_vop_nrmdir(struct vop_nrmdir_args *ap)
        name = ncp->nc_name;
        name_len = ncp->nc_nlen;
 
-       error = hammer2_unlink_file(dip, name, name_len, 1);
+       error = hammer2_unlink_file(dip, name, name_len, 1, NULL);
 
        if (error == 0) {
                cache_setunresolved(ap->a_nch);
@@ -1895,7 +1909,7 @@ hammer2_vop_nrename(struct vop_nrename_args *ap)
        /*
         * Remove target if it exists
         */
-       error = hammer2_unlink_file(tdip, tname, tname_len, -1);
+       error = hammer2_unlink_file(tdip, tname, tname_len, -1, NULL);
        if (error && error != ENOENT)
                goto done;
        cache_setunresolved(ap->a_tnch);
@@ -1921,7 +1935,7 @@ hammer2_vop_nrename(struct vop_nrename_args *ap)
                if (error)
                        goto done;
        }
-       error = hammer2_unlink_file(fdip, fname, fname_len, -1);
+       error = hammer2_unlink_file(fdip, fname, fname_len, -1, ip);
        if (error)
                goto done;