From 044541cda0f600de451eb349ab34dda763328d00 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Wed, 11 Dec 2013 13:33:11 -0800 Subject: [PATCH] hammer2 - Refactor file unlink w/open descriptor * Remove the H2 special cases for the HAMMER2_CHAIN_DELETED flag. These special cases existed to deal with the unlink-open-file case, but hackish and didn't deal with all the issues (such as bulk-free scan code losing track of the unlinked open file). * Change the unlink case when the file is still open or mmap'd. Instead of deleting the inode H2 will now move the inode to a hidden directory in the mount root. This way the bulk freeing code still sees it. When the inode is moved it is re-keyed to its inode number, thus guaranteed not to conflict with anything else sitting in the hidden directory. * Add code to create the hidden directory in the mount root if it does not exist. * Add code to wipe the contents of the hidden directory on-mount (as a crash recovery mechanic). --- sys/vfs/hammer2/hammer2.h | 10 +- sys/vfs/hammer2/hammer2_chain.c | 5 +- sys/vfs/hammer2/hammer2_disk.h | 3 + sys/vfs/hammer2/hammer2_flush.c | 103 +++----------- sys/vfs/hammer2/hammer2_inode.c | 231 +++++++++++++++++++++++++------ sys/vfs/hammer2/hammer2_ioctl.c | 2 +- sys/vfs/hammer2/hammer2_vfsops.c | 51 ++++--- sys/vfs/hammer2/hammer2_vnops.c | 33 +++-- 8 files changed, 279 insertions(+), 159 deletions(-) diff --git a/sys/vfs/hammer2/hammer2.h b/sys/vfs/hammer2/hammer2.h index 70aacf0d19..1e0c7a01aa 100644 --- a/sys/vfs/hammer2/hammer2.h +++ b/sys/vfs/hammer2/hammer2.h @@ -240,7 +240,7 @@ RB_PROTOTYPE(hammer2_chain_tree, hammer2_chain, rbnode, hammer2_chain_cmp); #define HAMMER2_CHAIN_MOVED 0x00000080 /* bref changed */ #define HAMMER2_CHAIN_IOFLUSH 0x00000100 /* bawrite on put */ #define HAMMER2_CHAIN_DEFERRED 0x00000200 /* on a deferral list */ -#define HAMMER2_CHAIN_DESTROYED 0x00000400 /* destroying inode */ +#define HAMMER2_CHAIN_UNLINKED 0x00000400 /* delete on reclaim */ #define HAMMER2_CHAIN_VOLUMESYNC 0x00000800 /* needs volume sync */ #define HAMMER2_CHAIN_UNUSED01000 0x00001000 #define HAMMER2_CHAIN_MOUNTED 0x00002000 /* PFS is mounted */ @@ -562,6 +562,8 @@ struct hammer2_pfsmount { struct mount *mp; hammer2_cluster_t cluster; hammer2_inode_t *iroot; /* PFS root inode */ + hammer2_inode_t *ihidden; /* PFS hidden directory */ + struct lock lock; /* PFS lock for certain ops */ hammer2_off_t inode_count; /* copy of inode_count */ ccms_domain_t ccms_dom; struct netexport export; /* nfs export */ @@ -734,14 +736,15 @@ hammer2_inode_t *hammer2_inode_create(hammer2_trans_t *trans, hammer2_chain_t **chainp, int *errorp); int hammer2_inode_connect(hammer2_trans_t *trans, int hlink, hammer2_inode_t *dip, hammer2_chain_t **chainp, - const uint8_t *name, size_t name_len); + const uint8_t *name, size_t name_len, + hammer2_key_t key); hammer2_inode_t *hammer2_inode_common_parent(hammer2_inode_t *fdip, hammer2_inode_t *tdip); void hammer2_inode_fsync(hammer2_trans_t *trans, hammer2_inode_t *ip, hammer2_chain_t **parentp); int hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip, const uint8_t *name, size_t name_len, int isdir, - int *hlinkp); + int *hlinkp, struct nchandle *nch); int hammer2_hardlink_consolidate(hammer2_trans_t *trans, hammer2_inode_t *ip, hammer2_chain_t **chainp, hammer2_inode_t *tdip, int linkcnt); @@ -749,6 +752,7 @@ int hammer2_hardlink_deconsolidate(hammer2_trans_t *trans, hammer2_inode_t *dip, hammer2_chain_t **chainp, hammer2_chain_t **ochainp); int hammer2_hardlink_find(hammer2_inode_t *dip, hammer2_chain_t **chainp, hammer2_chain_t **ochainp); +void hammer2_inode_install_hidden(hammer2_pfsmount_t *pmp); /* * hammer2_chain.c diff --git a/sys/vfs/hammer2/hammer2_chain.c b/sys/vfs/hammer2/hammer2_chain.c index ef7b579370..8ea0e9c43b 100644 --- a/sys/vfs/hammer2/hammer2_chain.c +++ b/sys/vfs/hammer2/hammer2_chain.c @@ -2910,6 +2910,8 @@ hammer2_chain_duplicate(hammer2_trans_t *trans, hammer2_chain_t **parentp, nchain->inode_reason = ochain->inode_reason + 0x100000; if (ochain->flags & HAMMER2_CHAIN_INITIAL) atomic_set_int(&nchain->flags, HAMMER2_CHAIN_INITIAL); + if (ochain->flags & HAMMER2_CHAIN_UNLINKED) + atomic_set_int(&nchain->flags, HAMMER2_CHAIN_UNLINKED); /* * Fixup (copy) any embedded data. Non-embedded data relies on the @@ -3066,7 +3068,8 @@ hammer2_chain_delete_duplicate(hammer2_trans_t *trans, hammer2_chain_t **chainp, nchain->inode_count += ochain->inode_count; atomic_set_int(&nchain->flags, ochain->flags & (HAMMER2_CHAIN_INITIAL | - HAMMER2_CHAIN_FORCECOW)); + HAMMER2_CHAIN_FORCECOW | + HAMMER2_CHAIN_UNLINKED)); atomic_set_int(&ochain->flags, HAMMER2_CHAIN_FORCECOW); nchain->inode_reason = ochain->inode_reason + 0x1000; diff --git a/sys/vfs/hammer2/hammer2_disk.h b/sys/vfs/hammer2/hammer2_disk.h index d87d3810f3..4153148559 100644 --- a/sys/vfs/hammer2/hammer2_disk.h +++ b/sys/vfs/hammer2/hammer2_disk.h @@ -682,6 +682,9 @@ typedef struct hammer2_bmap_data hammer2_bmap_data_t; #define HAMMER2_INODE_MAXNAME 256 /* maximum name in bytes */ #define HAMMER2_INODE_VERSION_ONE 1 +#define HAMMER2_INODE_HIDDENDIR 16 /* special inode */ +#define HAMMER2_INODE_START 1024 /* dynamically allocated */ + struct hammer2_inode_data { uint16_t version; /* 0000 inode data version */ uint16_t reserved02; /* 0002 */ diff --git a/sys/vfs/hammer2/hammer2_flush.c b/sys/vfs/hammer2/hammer2_flush.c index da2e7c35a7..9ab2eb616a 100644 --- a/sys/vfs/hammer2/hammer2_flush.c +++ b/sys/vfs/hammer2/hammer2_flush.c @@ -78,28 +78,16 @@ static void hammer2_rollup_stats(hammer2_chain_t *parent, * Can we ignore a chain for the purposes of flushing modifications * to the media? * - * Normally we don't bother to flush deleted chains. However, a chain - * associated with a deleted inode may still be active due to open - * descriptors. A flush is needed in this situation to prevent unbounded - * memory use by the filesystem. - * - * This test is fragile as active inodes can still be delete-duplicated and - * the older deleted chains are, in fact, actual deletions. These will be - * flagged DUPLICATED. The live inode at the end of the del-dup chain - * will be marked destroyed on last close. - * - * XXX inode deletions need to be deleted normally and then duplicated to - * a special 'live-but-unlinked' spot somewhere until last-close. - * XXX bulk free code has a conniption fit too. + * This code is no degenerate. We used to have to distinguish between + * deleted chains and deleted chains associated with inodes that were + * still open. This mechanic has been fixed so the function is now + * a simple test. */ static __inline int h2ignore_deleted(hammer2_flush_info_t *info, hammer2_chain_t *chain) { - return (chain->delete_tid <= info->sync_tid && - (chain->bref.type != HAMMER2_BREF_TYPE_INODE || - (chain->flags & HAMMER2_CHAIN_DUPLICATED) || - (chain->flags & HAMMER2_CHAIN_DESTROYED))); + return (chain->delete_tid <= info->sync_tid); } #if 0 @@ -236,8 +224,11 @@ hammer2_trans_init(hammer2_trans_t *trans, hammer2_pfsmount_t *pmp, } } } - if (flags & HAMMER2_TRANS_NEWINODE) + if (flags & HAMMER2_TRANS_NEWINODE) { + if (hmp->voldata.inode_tid < HAMMER2_INODE_START) + hmp->voldata.inode_tid = HAMMER2_INODE_START; trans->inode_tid = hmp->voldata.inode_tid++; + } hammer2_voldata_unlock(hmp, 0); } @@ -919,7 +910,7 @@ retry: /* * Issue flush. * - * A DESTROYED node that reaches this point must be flushed for + * A DELETED node that reaches this point must be flushed for * synchronization point consistency. * * Update bref.mirror_tid, clear MODIFIED, and set MOVED. @@ -1178,53 +1169,6 @@ hammer2_chain_flush_scan1(hammer2_chain_t *child, void *data) hammer2_chain_unlock(parent); hammer2_chain_lock(child, HAMMER2_RESOLVE_MAYBE); -#if 0 - /* - * This isn't working atm, it seems to be causing necessary - * updates to be thrown away, probably due to aliasing, resulting - * base_insert/base_delete panics. - */ - /* - * The DESTROYED flag can only be initially set on an unreferenced - * deleted inode and will propagate downward via the mechanic below. - * Such inode chains have been deleted for good and should no longer - * be subject to delete/duplication. - * - * This optimization allows the inode reclaim (destroy unlinked file - * on vnode reclamation after last close) to be flagged by just - * setting HAMMER2_CHAIN_DESTROYED at the top level and then will - * cause the chains to be terminated and related buffers to be - * invalidated and not flushed out. - * - * We have to be careful not to propagate the DESTROYED flag if - * the destruction occurred after our flush sync_tid. - */ - if (parent->delete_tid <= trans->sync_tid && - (parent->flags & HAMMER2_CHAIN_DESTROYED) && - (child->flags & HAMMER2_CHAIN_DESTROYED) == 0) { - /* - * Force downward recursion by bringing update_hi up to - * at least sync_tid, and setting the DESTROYED flag. - * Parent's mirror_tid has not yet been updated. - * - * We do not mark the child DELETED because this would - * cause unnecessary modifications/updates. Instead, the - * DESTROYED flag propagates downward and causes the flush - * to ignore any pre-existing modified chains. - * - * Vnode reclamation may have forced update_hi to MAX_TID - * (we do this because there was no transaction at the time). - * In this situation bring it down to something reasonable - * so the elements being destroyed can be retired. - */ - atomic_set_int(&child->flags, HAMMER2_CHAIN_DESTROYED); - spin_lock(&child->core->cst.spin); - if (child->core->update_hi < trans->sync_tid) - child->core->update_hi = trans->sync_tid; - spin_unlock(&child->core->cst.spin); - } -#endif - /* * No recursion needed if neither the child or anything under it * was messed with. @@ -1237,6 +1181,12 @@ hammer2_chain_flush_scan1(hammer2_chain_t *child, void *data) goto skip; } + /* + * XXX delete child if parent is deleted. Propagate deletion + * downward. TODO + */ + + /* * Re-check original pre-lock conditions after locking. */ @@ -1283,9 +1233,6 @@ skip: * Special optimization matching similar tests done in * flush_core, scan1, and scan2. Avoid updating the block * table in the parent if the parent is no longer visible. - * A deleted parent is no longer visible unless it's an - * inode (in which case it might have an open fd).. the - * DESTROYED flag must also be checked for inodes. */ ; } else if (child->delete_tid <= trans->sync_tid && @@ -1409,7 +1356,7 @@ hammer2_chain_flush_scan2(hammer2_chain_t *child, void *data) /* * The parent's blockref to the child must be deleted or updated. * - * This point is not reached on successful DESTROYED optimizations + * This point is not reached on successful DELETED optimizations * but can be reached on recursive deletions and restricted flushes. * * The chain_modify here may delete-duplicate the block. This can @@ -1475,24 +1422,12 @@ hammer2_chain_flush_scan2(hammer2_chain_t *child, void *data) } /* - * Don't bother updating a deleted + destroyed parent's blockrefs. - * We MUST update deleted + non-destroyed parent's blockrefs since - * they could represent an open file. + * Don't bother updating a deleted parent's blockrefs. * * Otherwise, we need to be COUNTEDBREFS synchronized for the * hammer2_base_*() functions. * - * NOTE: We optimize this by noting that only 'inode' chains require - * this treatment. When a file with an open descriptor is - * deleted only its inode is marked deleted. Other deletions, - * such as indirect block deletions, will no longer be visible - * to the live filesystem and do not need to be updated. - * - * rm -rf's generally wind up setting DESTROYED on the way down - * and the result is typically that no disk I/O is needed at all - * when rm -rf'ing an entire directory topology. - * - * This test must match the similar one in flush_core. + * This test must match the similar one in flush_core. */ #if FLUSH_DEBUG kprintf("SCAN2 base=%p pass=%d PARENT %p.%d DTID=%016jx SYNC=%016jx\n", diff --git a/sys/vfs/hammer2/hammer2_inode.c b/sys/vfs/hammer2/hammer2_inode.c index 59d73c4f67..a4760638f9 100644 --- a/sys/vfs/hammer2/hammer2_inode.c +++ b/sys/vfs/hammer2/hammer2_inode.c @@ -41,6 +41,10 @@ #include "hammer2.h" +static void hammer2_inode_move_to_hidden(hammer2_trans_t *trans, + hammer2_chain_t **chainp, + hammer2_tid_t inum); + RB_GENERATE2(hammer2_inode_tree, hammer2_inode, rbnode, hammer2_inode_cmp, hammer2_tid_t, inum); @@ -902,19 +906,17 @@ retry: int hammer2_inode_connect(hammer2_trans_t *trans, int hlink, hammer2_inode_t *dip, hammer2_chain_t **chainp, - const uint8_t *name, size_t name_len) + const uint8_t *name, size_t name_len, + hammer2_key_t lhc) { hammer2_inode_data_t *ipdata; hammer2_chain_t *nchain; hammer2_chain_t *parent; hammer2_chain_t *ochain; hammer2_key_t key_dummy; - hammer2_key_t lhc; int cache_index = -1; int error; - ochain = *chainp; - /* * Since ochain is either disconnected from the topology or represents * a hardlink terminus which is always a parent of or equal to dip, @@ -923,27 +925,37 @@ hammer2_inode_connect(hammer2_trans_t *trans, int hlink, * WARNING! Must use inode_lock_ex() on dip to handle a stale * dip->chain cache. */ + ochain = *chainp; parent = hammer2_inode_lock_ex(dip); /*parent = hammer2_chain_lookup_init(dip->chain, 0);*/ - lhc = hammer2_dirhash(name, name_len); - /* - * Locate the inode or indirect block to create the new - * entry in. At the same time check for key collisions - * and iterate until we don't get one. + * If name is non-NULL we calculate lhc, else we use the passed-in + * lhc. */ - error = 0; - while (error == 0) { - nchain = hammer2_chain_lookup(&parent, &key_dummy, - lhc, lhc, &cache_index, 0); - if (nchain == NULL) - break; - if ((lhc & HAMMER2_DIRHASH_LOMASK) == HAMMER2_DIRHASH_LOMASK) - error = ENOSPC; - hammer2_chain_unlock(nchain); - nchain = NULL; - ++lhc; + if (name) { + lhc = hammer2_dirhash(name, name_len); + + /* + * Locate the inode or indirect block to create the new + * entry in. At the same time check for key collisions + * and iterate until we don't get one. + */ + error = 0; + while (error == 0) { + nchain = hammer2_chain_lookup(&parent, &key_dummy, + lhc, lhc, + &cache_index, 0); + if (nchain == NULL) + break; + if ((lhc & HAMMER2_DIRHASH_LOMASK) == + HAMMER2_DIRHASH_LOMASK) { + error = ENOSPC; + } + hammer2_chain_unlock(nchain); + nchain = NULL; + ++lhc; + } } if (error == 0) { @@ -1125,6 +1137,14 @@ hammer2_inode_repoint(hammer2_inode_t *ip, hammer2_inode_t *pip, * isdir determines whether a directory/non-directory check should be made. * No check is made if isdir is set to -1. * + * isopen specifies whether special unlink-with-open-descriptor handling + * must be performed. If set to -1 the caller is deleting a PFS and we + * check whether the chain is mounted or not (chain->pmp != NULL). 1 is + * implied if it is mounted. + * + * If isopen is 1 and nlinks drops to 0 this function must move the chain + * to a special hidden directory until last-close occurs on the file. + * * NOTE! The underlying file can still be active with open descriptors * or if the chain is being manually held (e.g. for rename). * @@ -1134,7 +1154,7 @@ hammer2_inode_repoint(hammer2_inode_t *ip, hammer2_inode_t *pip, int hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip, const uint8_t *name, size_t name_len, - int isdir, int *hlinkp) + int isdir, int *hlinkp, struct nchandle *nch) { hammer2_inode_data_t *ipdata; hammer2_chain_t *parent; @@ -1254,7 +1274,9 @@ hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip, */ if (ochain) { /* - * Delete the original hardlink pointer. + * Delete the original hardlink pointer unconditionally. + * (any open descriptors will migrate to the hardlink + * target and have no affect on this operation). * * NOTE: parent from above is NULL when ochain != NULL * so we can reuse it. @@ -1262,28 +1284,42 @@ hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip, hammer2_chain_lock(ochain, HAMMER2_RESOLVE_ALWAYS); hammer2_chain_delete(trans, ochain, 0); hammer2_chain_unlock(ochain); + } - /* - * Then decrement nlinks on hardlink target, deleting - * the target when nlinks drops to 0. - */ - hammer2_chain_modify(trans, &chain, 0); - --chain->data->ipdata.nlinks; - if (chain->data->ipdata.nlinks == 0) + /* + * Decrement nlinks on the hardlink target (or original file if + * there it was not hardlinked). Delete the target when nlinks + * reaches 0 with special handling if (isopen) is set. + * + * NOTE! In DragonFly the vnops function calls cache_unlink() after + * calling us here to clean out the namecache association, + * (which does not represent a ref for the open-test), and to + * force finalization of the vnode if/when the last ref gets + * dropped. + */ + hammer2_chain_modify(trans, &chain, 0); + ipdata = &chain->data->ipdata; + --ipdata->nlinks; + kprintf("file %s nlinks %ld\n", ipdata->filename, ipdata->nlinks); + if ((int64_t)ipdata->nlinks < 0) /* XXX debugging */ + ipdata->nlinks = 0; + if (ipdata->nlinks == 0) { + if ((chain->flags & HAMMER2_CHAIN_PFSROOT) && chain->pmp) { + error = EINVAL; + kprintf("hammer2: PFS \"%s\" cannot be deleted " + "while still mounted\n", + ipdata->filename); + goto done; + } + if (nch && cache_isopen(nch)) { + kprintf("WARNING: unlinking open file\n"); + atomic_set_int(&chain->flags, HAMMER2_CHAIN_UNLINKED); + hammer2_inode_move_to_hidden(trans, &chain, + ipdata->inum); + } else { hammer2_chain_delete(trans, chain, 0); - } else { - /* - * Otherwise this was not a hardlink and we can just - * remove the entry and decrement nlinks. - * - * NOTE: *_get() integrates chain's lock into the inode lock. - */ - hammer2_chain_modify(trans, &chain, 0); - ipdata = &chain->data->ipdata; - --ipdata->nlinks; - hammer2_chain_delete(trans, chain, 0); + } } - error = 0; done: if (chain) @@ -1296,6 +1332,119 @@ done: return error; } +/* + * This is called from the mount code to initialize pmp->ihidden + */ +void +hammer2_inode_install_hidden(hammer2_pfsmount_t *pmp) +{ + hammer2_trans_t trans; + hammer2_chain_t *parent; + hammer2_chain_t *chain; + hammer2_chain_t *scan; + hammer2_inode_data_t *ipdata; + hammer2_key_t key_dummy; + hammer2_key_t key_next; + int cache_index; + int error; + int count; + + if (pmp->ihidden) + return; + + /* + * Find the hidden directory + */ + bzero(&key_dummy, sizeof(key_dummy)); + hammer2_trans_init(&trans, pmp, NULL, 0); + + parent = hammer2_inode_lock_ex(pmp->iroot); + chain = hammer2_chain_lookup(&parent, &key_dummy, + HAMMER2_INODE_HIDDENDIR, + HAMMER2_INODE_HIDDENDIR, + &cache_index, 0); + if (chain) { + pmp->ihidden = hammer2_inode_get(pmp, pmp->iroot, chain); + hammer2_inode_ref(pmp->ihidden); + + /* + * Remove any unlinked files which were left open as-of + * any system crash. + */ + count = 0; + scan = hammer2_chain_lookup(&chain, &key_next, + 0, HAMMER2_MAX_TID, + &cache_index, + HAMMER2_LOOKUP_NODATA); + while (scan) { + if (scan->bref.type == HAMMER2_BREF_TYPE_INODE) { + hammer2_chain_delete(&trans, scan, 0); + ++count; + } + scan = hammer2_chain_next(&chain, scan, &key_next, + 0, HAMMER2_MAX_TID, + &cache_index, + HAMMER2_LOOKUP_NODATA); + } + + hammer2_inode_unlock_ex(pmp->ihidden, chain); + hammer2_inode_unlock_ex(pmp->iroot, parent); + hammer2_trans_done(&trans); + kprintf("hammer2: PFS loaded hidden dir, " + "removed %d dead entries\n", count); + return; + } + + /* + * Create the hidden directory + */ + error = hammer2_chain_create(&trans, &parent, &chain, + HAMMER2_INODE_HIDDENDIR, 0, + HAMMER2_BREF_TYPE_INODE, + HAMMER2_INODE_BYTES); + hammer2_inode_unlock_ex(pmp->iroot, parent); + hammer2_chain_modify(&trans, &chain, 0); + ipdata = &chain->data->ipdata; + ipdata->type = HAMMER2_OBJTYPE_DIRECTORY; + ipdata->inum = HAMMER2_INODE_HIDDENDIR; + ipdata->nlinks = 1; + kprintf("hammer2: PFS root missing hidden directory, creating\n"); + + pmp->ihidden = hammer2_inode_get(pmp, pmp->iroot, chain); + hammer2_inode_ref(pmp->ihidden); + hammer2_inode_unlock_ex(pmp->ihidden, chain); + hammer2_trans_done(&trans); +} + +/* + * If an open file is unlinked H2 needs to retain the file in the topology + * to ensure that its backing store is not recovered by the bulk free scan. + * This also allows us to avoid having to special-case the CHAIN_DELETED flag. + * + * To do this the file is moved to a hidden directory in the PFS root and + * renamed. The hidden directory must be created if it does not exist. + */ +static +void +hammer2_inode_move_to_hidden(hammer2_trans_t *trans, hammer2_chain_t **chainp, + hammer2_tid_t inum) +{ + hammer2_chain_t *chain; + hammer2_pfsmount_t *pmp; + int error; + + chain = *chainp; + pmp = chain->pmp; + KKASSERT(pmp != NULL); + KKASSERT(pmp->ihidden != NULL); + + hammer2_chain_delete(trans, chain, 0); + error = hammer2_inode_connect(trans, 0, + pmp->ihidden, chainp, + NULL, 0, inum); + KKASSERT(error == 0); +} + /* * Given an exclusively locked inode and chain we consolidate its chain * for hardlink creation, adding (nlinks) to the file's link count and diff --git a/sys/vfs/hammer2/hammer2_ioctl.c b/sys/vfs/hammer2/hammer2_ioctl.c index ae4b74a114..76c9724bf6 100644 --- a/sys/vfs/hammer2/hammer2_ioctl.c +++ b/sys/vfs/hammer2/hammer2_ioctl.c @@ -548,7 +548,7 @@ hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data) hammer2_trans_init(&trans, ip->pmp, NULL, 0); error = hammer2_unlink_file(&trans, hmp->sroot, pfs->name, strlen(pfs->name), - 2, NULL); + 2, NULL, NULL); hammer2_trans_done(&trans); return (error); diff --git a/sys/vfs/hammer2/hammer2_vfsops.c b/sys/vfs/hammer2/hammer2_vfsops.c index 012d35a256..0edeacdd89 100644 --- a/sys/vfs/hammer2/hammer2_vfsops.c +++ b/sys/vfs/hammer2/hammer2_vfsops.c @@ -405,6 +405,8 @@ hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data, if (error) break; } + hammer2_inode_install_hidden(pmp); + return error; } } @@ -582,7 +584,7 @@ hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data, kmalloc_create(&pmp->minode, "HAMMER2-inodes"); kmalloc_create(&pmp->mmsg, "HAMMER2-pfsmsg"); - + lockinit(&pmp->lock, "pfslk", 0, 0); spin_init(&pmp->inum_spin); RB_INIT(&pmp->inum_tree); @@ -707,6 +709,11 @@ hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data, } hammer2_cluster_reconnect(pmp, fp); + /* + * With the cluster operational install ihidden. + */ + hammer2_inode_install_hidden(pmp); + /* * Finish setup */ @@ -1428,27 +1435,35 @@ hammer2_vfs_unmount(struct mount *mp, int mntflags) pmp->wthread_td = NULL; } - for (i = 0; i < pmp->cluster.nchains; ++i) { - hmp = pmp->cluster.chains[i]->hmp; - - hammer2_vfs_unmount_hmp1(mp, hmp); + /* + * Cleanup our reference on ihidden. + */ + if (pmp->ihidden) { + hammer2_inode_drop(pmp->ihidden); + pmp->ihidden = NULL; + } - /* - * Cleanup the root and super-root chain elements - * (which should be clean). - */ - if (pmp->iroot) { + /* + * Cleanup our reference on iroot. iroot is (should) not be needed + * by the flush code. + */ + if (pmp->iroot) { #if REPORT_REFS_ERRORS - if (pmp->iroot->refs != 1) - kprintf("PMP->IROOT %p REFS WRONG %d\n", - pmp->iroot, pmp->iroot->refs); + if (pmp->iroot->refs != 1) + kprintf("PMP->IROOT %p REFS WRONG %d\n", + pmp->iroot, pmp->iroot->refs); #else - KKASSERT(pmp->iroot->refs == 1); + KKASSERT(pmp->iroot->refs == 1); #endif - /* ref for pmp->iroot */ - hammer2_inode_drop(pmp->iroot); - pmp->iroot = NULL; - } + /* ref for pmp->iroot */ + hammer2_inode_drop(pmp->iroot); + pmp->iroot = NULL; + } + + for (i = 0; i < pmp->cluster.nchains; ++i) { + hmp = pmp->cluster.chains[i]->hmp; + + hammer2_vfs_unmount_hmp1(mp, hmp); rchain = pmp->cluster.chains[i]; if (rchain) { diff --git a/sys/vfs/hammer2/hammer2_vnops.c b/sys/vfs/hammer2/hammer2_vnops.c index 8297b6d01f..bbf3d8ca26 100644 --- a/sys/vfs/hammer2/hammer2_vnops.c +++ b/sys/vfs/hammer2/hammer2_vnops.c @@ -277,7 +277,7 @@ hammer2_vop_inactive(struct vop_inactive_args *ap) /* * Check for deleted inodes and recycle immediately. */ - if (parent->flags & HAMMER2_CHAIN_DELETED) { + if (parent->flags & HAMMER2_CHAIN_UNLINKED) { hammer2_inode_unlock_ex(ip, parent); vrecycle(vp); } else { @@ -305,7 +305,7 @@ hammer2_vop_reclaim(struct vop_reclaim_args *ap) return(0); /* - * Set update_hi so we can detect and propagate the DESTROYED + * Set update_hi so we can detect and propagate the DELETED * bit in the flush code. * * ip->chain might be stale, correct it before checking as older @@ -322,7 +322,7 @@ hammer2_vop_reclaim(struct vop_reclaim_args *ap) /* * The final close of a deleted file or directory marks it for - * destruction. The DESTROYED flag allows the flusher to shortcut + * destruction. The DELETED flag allows the flusher to shortcut * any modified blocks still unflushed (that is, just ignore them). * * HAMMER2 usually does not try to optimize the freemap by returning @@ -335,10 +335,11 @@ hammer2_vop_reclaim(struct vop_reclaim_args *ap) */ vp->v_data = NULL; ip->vp = NULL; - if (chain->flags & HAMMER2_CHAIN_DELETED) { - atomic_set_int(&chain->flags, HAMMER2_CHAIN_DESTROYED); + if (chain->flags & HAMMER2_CHAIN_UNLINKED) { + kprintf("unlink on reclaim: %s\n", chain->data->ipdata.filename); hammer2_trans_init(&trans, ip->pmp, NULL, HAMMER2_TRANS_BUFCACHE); + hammer2_chain_delete(&trans, chain, 0); hammer2_chain_setsubmod(&trans, chain); spin_lock(&chain->core->cst.spin); if (chain->core->update_hi < trans.sync_tid) @@ -1536,7 +1537,7 @@ hammer2_vop_nlink(struct vop_nlink_args *ap) */ error = hammer2_inode_connect(&trans, 1, dip, &chain, - name, name_len); + name, name_len, 0); if (error == 0) { cache_setunresolved(ap->a_nch); cache_setvp(ap->a_nch, ap->a_vp); @@ -1753,11 +1754,14 @@ hammer2_vop_nremove(struct vop_nremove_args *ap) ncp = ap->a_nch->ncp; name = ncp->nc_name; name_len = ncp->nc_nlen; + hammer2_chain_memory_wait(dip->pmp); hammer2_trans_init(&trans, dip->pmp, NULL, 0); - error = hammer2_unlink_file(&trans, dip, name, name_len, 0, NULL); + error = hammer2_unlink_file(&trans, dip, name, name_len, + 0, NULL, ap->a_nch); hammer2_trans_done(&trans); if (error == 0) { + kprintf("cache_unlink\n"); cache_unlink(ap->a_nch); } return (error); @@ -1787,7 +1791,8 @@ hammer2_vop_nrmdir(struct vop_nrmdir_args *ap) hammer2_chain_memory_wait(dip->pmp); hammer2_trans_init(&trans, dip->pmp, NULL, 0); - error = hammer2_unlink_file(&trans, dip, name, name_len, 1, NULL); + error = hammer2_unlink_file(&trans, dip, name, name_len, + 1, NULL, ap->a_nch); hammer2_trans_done(&trans); if (error == 0) { cache_unlink(ap->a_nch); @@ -1859,7 +1864,8 @@ hammer2_vop_nrename(struct vop_nrename_args *ap) /* * Remove target if it exists */ - error = hammer2_unlink_file(&trans, tdip, tname, tname_len, -1, NULL); + error = hammer2_unlink_file(&trans, tdip, tname, tname_len, + -1, NULL, ap->a_tnch); if (error && error != ENOENT) goto done; cache_setunresolved(ap->a_tnch); @@ -1890,10 +1896,15 @@ hammer2_vop_nrename(struct vop_nrename_args *ap) * a hardlink the HARDLINK pointer object will be removed but the * hardlink will stay intact. * + * Always pass nch as NULL because we intend to reconnect the inode, + * so we don't want hammer2_unlink_file() to rename it to the hidden + * open-but-unlinked directory. + * * The target chain may be marked DELETED but will not be destroyed * since we retain our hold on ip and chain. */ - error = hammer2_unlink_file(&trans, fdip, fname, fname_len, -1, &hlink); + error = hammer2_unlink_file(&trans, fdip, fname, fname_len, + -1, &hlink, NULL); KKASSERT(error != EAGAIN); if (error) goto done; @@ -1915,7 +1926,7 @@ hammer2_vop_nrename(struct vop_nrename_args *ap) hammer2_chain_refactor(&chain); error = hammer2_inode_connect(&trans, hlink, tdip, &chain, - tname, tname_len); + tname, tname_len, 0); chain->inode_reason = 5; if (error == 0) { KKASSERT(chain != NULL); -- 2.41.0