Module Name: src
Committed By: ad
Date: Fri Jan 24 16:48:59 UTC 2020
Modified Files:
src/sys/fs/tmpfs [ad-namecache]: tmpfs_subr.c tmpfs_vfsops.c
src/sys/kern [ad-namecache]: vfs_cache.c
src/sys/sys [ad-namecache]: namei.src
src/sys/ufs/ffs [ad-namecache]: ffs_vfsops.c
src/sys/ufs/ufs [ad-namecache]: ufs_vnops.c
Log Message:
- Put all the namecache stuff back into vnode_impl_t.
- Tidy vfs_cache.c up, finish the comments.
- Finalise how ID information is entered to the cache.
- Handle very small/old systems.
To generate a diff of this commit:
cvs rdiff -u -r1.105.2.1 -r1.105.2.2 src/sys/fs/tmpfs/tmpfs_subr.c
cvs rdiff -u -r1.75.2.2 -r1.75.2.3 src/sys/fs/tmpfs/tmpfs_vfsops.c
cvs rdiff -u -r1.126.2.10 -r1.126.2.11 src/sys/kern/vfs_cache.c
cvs rdiff -u -r1.47.2.5 -r1.47.2.6 src/sys/sys/namei.src
cvs rdiff -u -r1.362.4.3 -r1.362.4.4 src/sys/ufs/ffs/ffs_vfsops.c
cvs rdiff -u -r1.248.2.1 -r1.248.2.2 src/sys/ufs/ufs/ufs_vnops.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/fs/tmpfs/tmpfs_subr.c
diff -u src/sys/fs/tmpfs/tmpfs_subr.c:1.105.2.1 src/sys/fs/tmpfs/tmpfs_subr.c:1.105.2.2
--- src/sys/fs/tmpfs/tmpfs_subr.c:1.105.2.1 Fri Jan 17 22:26:25 2020
+++ src/sys/fs/tmpfs/tmpfs_subr.c Fri Jan 24 16:48:58 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: tmpfs_subr.c,v 1.105.2.1 2020/01/17 22:26:25 ad Exp $ */
+/* $NetBSD: tmpfs_subr.c,v 1.105.2.2 2020/01/24 16:48:58 ad Exp $ */
/*
* Copyright (c) 2005-2013 The NetBSD Foundation, Inc.
@@ -73,7 +73,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.105.2.1 2020/01/17 22:26:25 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.105.2.2 2020/01/24 16:48:58 ad Exp $");
#include <sys/param.h>
#include <sys/cprng.h>
@@ -148,7 +148,7 @@ tmpfs_init_vnode(struct vnode *vp, tmpfs
node->tn_vnode = vp;
uvm_vnp_setsize(vp, node->tn_size);
KASSERT(node->tn_mode != VNOVAL);
- cache_set_id(vp, node->tn_mode, node->tn_uid, node->tn_gid);
+ cache_enter_id(vp, node->tn_mode, node->tn_uid, node->tn_gid);
}
/*
@@ -1039,7 +1039,7 @@ tmpfs_chmod(vnode_t *vp, mode_t mode, ka
node->tn_mode = (mode & ALLPERMS);
tmpfs_update(vp, TMPFS_UPDATE_CTIME);
VN_KNOTE(vp, NOTE_ATTRIB);
- cache_update_id(vp, node->tn_mode, node->tn_uid, node->tn_gid);
+ cache_enter_id(vp, node->tn_mode, node->tn_uid, node->tn_gid);
return 0;
}
@@ -1084,7 +1084,7 @@ tmpfs_chown(vnode_t *vp, uid_t uid, gid_
node->tn_gid = gid;
tmpfs_update(vp, TMPFS_UPDATE_CTIME);
VN_KNOTE(vp, NOTE_ATTRIB);
- cache_update_id(vp, node->tn_mode, node->tn_uid, node->tn_gid);
+ cache_enter_id(vp, node->tn_mode, node->tn_uid, node->tn_gid);
return 0;
}
Index: src/sys/fs/tmpfs/tmpfs_vfsops.c
diff -u src/sys/fs/tmpfs/tmpfs_vfsops.c:1.75.2.2 src/sys/fs/tmpfs/tmpfs_vfsops.c:1.75.2.3
--- src/sys/fs/tmpfs/tmpfs_vfsops.c:1.75.2.2 Sun Jan 19 21:21:54 2020
+++ src/sys/fs/tmpfs/tmpfs_vfsops.c Fri Jan 24 16:48:58 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: tmpfs_vfsops.c,v 1.75.2.2 2020/01/19 21:21:54 ad Exp $ */
+/* $NetBSD: tmpfs_vfsops.c,v 1.75.2.3 2020/01/24 16:48:58 ad Exp $ */
/*
* Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
@@ -42,7 +42,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.75.2.2 2020/01/19 21:21:54 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.75.2.3 2020/01/24 16:48:58 ad Exp $");
#include <sys/param.h>
#include <sys/atomic.h>
@@ -182,7 +182,8 @@ tmpfs_mount(struct mount *mp, const char
mp->mnt_stat.f_namemax = TMPFS_MAXNAMLEN;
mp->mnt_fs_bshift = PAGE_SHIFT;
mp->mnt_dev_bshift = DEV_BSHIFT;
- mp->mnt_iflag |= IMNT_MPSAFE | IMNT_CAN_RWTORO | IMNT_SHRLOOKUP;
+ mp->mnt_iflag |= IMNT_MPSAFE | IMNT_CAN_RWTORO | IMNT_SHRLOOKUP |
+ IMNT_NCLOOKUP;
vfs_getnewfsid(mp);
/* Allocate the tmpfs mount structure and fill it. */
Index: src/sys/kern/vfs_cache.c
diff -u src/sys/kern/vfs_cache.c:1.126.2.10 src/sys/kern/vfs_cache.c:1.126.2.11
--- src/sys/kern/vfs_cache.c:1.126.2.10 Thu Jan 23 12:33:18 2020
+++ src/sys/kern/vfs_cache.c Fri Jan 24 16:48:59 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: vfs_cache.c,v 1.126.2.10 2020/01/23 12:33:18 ad Exp $ */
+/* $NetBSD: vfs_cache.c,v 1.126.2.11 2020/01/24 16:48:59 ad Exp $ */
/*-
* Copyright (c) 2008, 2019, 2020 The NetBSD Foundation, Inc.
@@ -64,45 +64,46 @@
* Name caching:
*
* Names found by directory scans are retained in a cache for future
- * reference. It is managed pseudo-LRU, so frequently used names will
- * hang around. The cache is indexed by directory.
+ * reference. It is managed LRU, so frequently used names will hang
+ * around. The cache is indexed by hash value obtained from the name.
*
- * Background:
+ * The name cache (or directory name lookup cache) is the brainchild of
+ * Robert Elz and made its first appearance in 4.3BSD.
*
- * XXX add a bit of history
+ * Data structures:
*
- * Data structures
- *
- * Typically DNLCs use a global hash table for lookup, which works very
- * well but can cause challenges for the CPU cache on modern CPUs, and
- * concurrency headaches for the kernel hacker. The focus of interest
- * in this implementation is the directory itself: a per-directory data
- * structure is used to index names. Currently this is a red-black
- * tree. There are no special concurrency requirements placed on the
- * index, so it should not be difficult to experiment with other
- * structures.
+ * Most Unix namecaches very sensibly use a global hash table to index
+ * names. The global hash table works well, but can cause concurrency
+ * headaches for the kernel hacker. In the NetBSD 10.0 implementation
+ * we are not sensible, and use a per-directory data structure to index
+ * names, but the cache otherwise functions the same.
+ *
+ * The index is a red-black tree. There are no special concurrency
+ * requirements placed on it, because it's per-directory and protected
+ * by the namecache's per-directory locks. It should therefore not be
+ * difficult to experiment with other data structures.
*
* Each cached name is stored in a struct namecache, along with a
- * pointer the associated vnode (nc_vp). For simplicity (and economy
- * of storage), names longer than a maximum length of NCHNAMLEN are
- * allocated with kmem_alloc(); they occur infrequently. Names shorter
- * than this are stored directly in struct namecache. If it is a
- * "negative" entry, (i.e. for a name that is known NOT to exist) the
- * vnode pointer will be NULL.
+ * pointer to the associated vnode (nc_vp). Names longer than a
+ * maximum length of NCHNAMLEN are allocated with kmem_alloc(); they
+ * occur infrequently, and names shorter than this are stored directly
+ * in struct namecache. If it is a "negative" entry, (i.e. for a name
+ * that is known NOT to exist) the vnode pointer will be NULL.
*
- * The nchnode (XXXAD vnode) and namecache structures are connected together thusly
+ * For a directory with 3 cached names for 3 distinct vnodes, the
+ * various vnodes and namecache structs would be connected like this
* (the root is at the bottom of the diagram):
*
* ...
* ^
- * |- nn_tree
+ * |- vi_nc_tree
* |
* +----o----+ +---------+ +---------+
* | VDIR | | VCHR | | VREG |
- * | nchnode o-----+ | nchnode o-----+ | nchnode o------+
+ * | vnode o-----+ | vnode o-----+ | vnode o------+
* +---------+ | +---------+ | +---------+ |
* ^ | ^ | ^ |
- * |- nc_nn |- nn_list |- nc_nn |- nn_list |- nc_nn |
+ * |- nc_vp |- vi_nc_list |- nc_vp |- vi_nc_list |- nc_vp |
* | | | | | |
* +----o----+ | +----o----+ | +----o----+ |
* +---onamecache|<----+ +---onamecache|<----+ +---onamecache|<-----+
@@ -110,12 +111,12 @@
* | ^ | ^ | ^
* | | | | | |
* | | +----------------------+ | |
- * |-nc_dnn | +-------------------------------------------------+
- * | |/- nn_tree | |
- * | | |- nc_dnn |- nc_dnn
+ * |-nc_dvp | +-------------------------------------------------+
+ * | |/- vi_nc_tree | |
+ * | | |- nc_dvp |- nc_dvp
* | +----o----+ | |
* +-->| VDIR |<----------+ |
- * | nchnode |<------------------------------------+
+ * | vnode |<------------------------------------+
* +---------+
*
* START HERE
@@ -123,44 +124,53 @@
* Replacement:
*
* As the cache becomes full, old and unused entries are purged as new
- * entries are added. It would be too expensive to maintain a strict
- * LRU list based on last access, so instead we ape the VM system and
- * maintain lits of active and inactive entries. New entries go to the
- * tail of the active list. After they age out, they are placed on the
- * tail of the inactive list. Any subsequent use of the cache entry
- * reactivates it, saving it from impending doom; if not reactivated,
- * the entry will eventually be purged.
+ * entries are added. The synchronization overhead in maintaining a
+ * strict ordering would be prohibitive, so the VM system's "clock" or
+ * "second chance" page replacement algorithm is aped here. New
+ * entries go to the tail of the active list. After they age out and
+ * reach the head of the list, they are moved to the tail of the
+ * inactive list. Any use of the deactivated cache entry reactivates
+ * it, saving it from impending doom; if not reactivated, the entry
+ * eventually reaches the head of the inactive list and is purged.
*
* Concurrency:
*
- * From a performance perspective, cache_lookup(nameiop == LOOKUP) and
- * its siblings are what really matters; insertion of new entries with
- * cache_enter() is comparatively infrequent. This dictates the
- * concurrency strategy: lookups must be easy, never difficult.
+ * From a performance perspective, cache_lookup(nameiop == LOOKUP) is
+ * what really matters; insertion of new entries with cache_enter() is
+ * comparatively infrequent, and overshadowed by the cost of expensive
+ * file system metadata operations (which may involve disk I/O). We
+ * therefore want to make everything simplest in the lookup path.
*
* struct namecache is mostly stable except for list and tree related
- * entries, which don't affect the cached name or vnode, and entries
- * are purged in preference to modifying them. Read access to
- * namecache entries is made via tree, list, or LRU list. A lock
- * corresponding to the direction of access should be held. See
- * definition of "struct namecache" in src/sys/namei.src, and the
- * definition of "struct nchnode" XXXAD vnode for the particulars.
+ * entries, changes to which don't affect the cached name or vnode.
+ * For changes to name+vnode, entries are purged in preference to
+ * modifying them.
+ *
+ * Read access to namecache entries is made via tree, list, or LRU
+ * list. A lock corresponding to the direction of access should be
+ * held. See definition of "struct namecache" in src/sys/namei.src,
+ * and the definition of "struct vnode" for the particulars.
*
- * Per-CPU statistics, and "numcache" are read unlocked, since an
- * approximate value is OK. We maintain uintptr_t sized per-CPU
+ * Per-CPU statistics, and LRU list totals are read unlocked, since
+ * an approximate value is OK. We maintain uintptr_t sized per-CPU
* counters and 64-bit global counters under the theory that uintptr_t
* sized counters are less likely to be hosed by nonatomic increment.
*
* The lock order is:
*
- * 1) nn->nn_lock (tree or parent->child direction)
- * 2) nn->nn_listlock (list or child->parent direction)
- * 3) cache_lru_lock (LRU list direction)
- * 4) vp->v_interlock
+ * 1) vi->vi_nc_lock (tree or parent -> child direction,
+ * used during forward lookup)
+ *
+ * 2) vi->vi_nc_listlock (list or child -> parent direction,
+ * used during reverse lookup)
+ *
+ * 3) cache_lru_lock (LRU list direction, used during reclaim)
+ *
+ * 4) vp->v_interlock (what the cache entry points to)
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,v 1.126.2.10 2020/01/23 12:33:18 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,v 1.126.2.11 2020/01/24 16:48:59 ad Exp $");
#define __NAMECACHE_PRIVATE
#ifdef _KERNEL_OPT
@@ -186,31 +196,6 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,
#include <miscfs/genfs/genfs.h>
-/*
- * Per-vnode state for the namecache. Field markings and corresponding
- * locks. XXXAD this needs to be folded back into struct vnode, but
- * struct vnode_impl_t needs to be folded back into struct vnode first.
- *
- * - stable throught the lifetime of the vnode
- * n protected by nn_lock
- * l protected by nn_listlock
- */
-struct nchnode {
- /* First cache line: frequently used stuff. */
- rb_tree_t nn_tree; /* n namecache tree */
- TAILQ_HEAD(,namecache) nn_list; /* l namecaches (parent) */
- mode_t nn_mode; /* n cached mode or VNOVAL */
- uid_t nn_uid; /* n cached UID or VNOVAL */
- gid_t nn_gid; /* n cached GID or VNOVAL */
- uint32_t nn_spare; /* - spare (padding) */
-
- /* Second cache line: locks and infrequenly used stuff. */
- krwlock_t nn_lock /* - lock on node */
- __aligned(COHERENCY_UNIT);
- krwlock_t nn_listlock; /* - lock on nn_list */
- struct vnode *nn_vp; /* - backpointer to vnode */
-};
-
static void cache_activate(struct namecache *);
static int cache_compare_key(void *, const void *, const void *);
static int cache_compare_nodes(void *, const void *, const void *);
@@ -223,7 +208,6 @@ struct nchstats_percpu _NAMEI_CACHE_STAT
/* Global pool cache. */
static pool_cache_t cache_pool __read_mostly;
-static pool_cache_t cache_node_pool __read_mostly;
/* LRU replacement. */
enum cache_lru_id {
@@ -250,7 +234,7 @@ struct nchstats nchstats __cacheline_ali
/* Tunables */
static const int cache_lru_maxdeact = 2; /* max # to deactivate */
-static const int cache_lru_maxscan = 128; /* max # to scan/reclaim */
+static const int cache_lru_maxscan = 64; /* max # to scan/reclaim */
static int doingcache = 1; /* 1 => enable the cache */
/* sysctl */
@@ -312,23 +296,22 @@ cache_compare_nodes(void *context, const
static int
cache_compare_key(void *context, const void *n, const void *k)
{
- const struct namecache *nc = n;
+ const struct namecache *ncp = n;
const int64_t key = *(const int64_t *)k;
- if (nc->nc_key < key) {
+ if (ncp->nc_key < key) {
return -1;
}
- if (nc->nc_key > key) {
+ if (ncp->nc_key > key) {
return 1;
}
return 0;
}
/*
- * Compute a (with luck) unique key value for the given name. The name
- * length is encoded in the key value to try and improve uniqueness, and so
- * that length doesn't need to be compared separately for subsequent string
- * comparisons.
+ * Compute a key value for the given name. The name length is encoded in
+ * the key value to try and improve uniqueness, and so that length doesn't
+ * need to be compared separately for string comparisons.
*/
static int64_t
cache_key(const char *name, size_t nlen)
@@ -349,104 +332,101 @@ cache_key(const char *name, size_t nlen)
* - small strings
*/
static inline int
-cache_namecmp(struct namecache *nc, const char *name, size_t namelen)
+cache_namecmp(struct namecache *ncp, const char *name, size_t namelen)
{
size_t i;
int d;
- KASSERT(nc->nc_nlen == namelen);
+ KASSERT(ncp->nc_nlen == namelen);
for (d = 0, i = 0; i < namelen; i++) {
- d |= (nc->nc_name[i] ^ name[i]);
+ d |= (ncp->nc_name[i] ^ name[i]);
}
return d;
}
/*
- * Remove an entry from the cache. The directory lock must be held, and if
- * "dir2node" is true, then we're locking in the conventional direction and
- * the list lock will be acquired when removing the entry from the vnode
- * list.
+ * Remove an entry from the cache. vi_nc_lock must be held, and if dir2node
+ * is true, then we're locking in the conventional direction and the list
+ * lock will be acquired when removing the entry from the vnode list.
*/
static void
-cache_remove(struct namecache *nc, const bool dir2node)
+cache_remove(struct namecache *ncp, const bool dir2node)
{
- struct nchnode *dnn = nc->nc_dnn;
- struct nchnode *nn;
+ struct vnode *vp, *dvp = ncp->nc_dvp;
+ vnode_impl_t *dvi = VNODE_TO_VIMPL(dvp);
- KASSERT(rw_write_held(&dnn->nn_lock));
- KASSERT(cache_key(nc->nc_name, nc->nc_nlen) == nc->nc_key);
- KASSERT(rb_tree_find_node(&dnn->nn_tree, &nc->nc_key) == nc);
+ KASSERT(rw_write_held(&dvi->vi_nc_lock));
+ KASSERT(cache_key(ncp->nc_name, ncp->nc_nlen) == ncp->nc_key);
+ KASSERT(rb_tree_find_node(&dvi->vi_nc_tree, &ncp->nc_key) == ncp);
- SDT_PROBE(vfs, namecache, invalidate, done, nc,
+ SDT_PROBE(vfs, namecache, invalidate, done, ncp,
0, 0, 0, 0);
/* First remove from the directory's rbtree. */
- rb_tree_remove_node(&dnn->nn_tree, nc);
+ rb_tree_remove_node(&dvi->vi_nc_tree, ncp);
/* Then remove from the LRU lists. */
mutex_enter(&cache_lru_lock);
- TAILQ_REMOVE(&cache_lru.list[nc->nc_lrulist], nc, nc_lru);
- cache_lru.count[nc->nc_lrulist]--;
+ TAILQ_REMOVE(&cache_lru.list[ncp->nc_lrulist], ncp, nc_lru);
+ cache_lru.count[ncp->nc_lrulist]--;
mutex_exit(&cache_lru_lock);
/* Then remove from the node's list. */
- if ((nn = nc->nc_nn) != NULL) {
+ if ((vp = ncp->nc_vp) != NULL) {
+ vnode_impl_t *vi = VNODE_TO_VIMPL(vp);
if (__predict_true(dir2node)) {
- rw_enter(&nn->nn_listlock, RW_WRITER);
- TAILQ_REMOVE(&nn->nn_list, nc, nc_list);
- rw_exit(&nn->nn_listlock);
+ rw_enter(&vi->vi_nc_listlock, RW_WRITER);
+ TAILQ_REMOVE(&vi->vi_nc_list, ncp, nc_list);
+ rw_exit(&vi->vi_nc_listlock);
} else {
- TAILQ_REMOVE(&nn->nn_list, nc, nc_list);
+ TAILQ_REMOVE(&vi->vi_nc_list, ncp, nc_list);
}
}
/* Finally, free it. */
- if (nc->nc_nlen > NCHNAMLEN) {
- size_t sz = offsetof(struct namecache, nc_name[nc->nc_nlen]);
- kmem_free(nc, sz);
+ if (ncp->nc_nlen > NCHNAMLEN) {
+ size_t sz = offsetof(struct namecache, nc_name[ncp->nc_nlen]);
+ kmem_free(ncp, sz);
} else {
- pool_cache_put(cache_pool, nc);
+ pool_cache_put(cache_pool, ncp);
}
}
/*
- * Find a single cache entry and return it. The nchnode lock must be held.
- *
- * Marked __noinline, and with everything unnecessary excluded, the compiler
- * (gcc 8.3.0 x86_64) does a good job on this.
+ * Find a single cache entry and return it. The vnode lock must be held.
*/
static struct namecache * __noinline
-cache_lookup_entry(struct nchnode *dnn, const char *name, size_t namelen,
+cache_lookup_entry(struct vnode *dvp, const char *name, size_t namelen,
int64_t key)
{
- struct rb_node *node = dnn->nn_tree.rbt_root;
- struct namecache *nc;
+ vnode_impl_t *dvi = VNODE_TO_VIMPL(dvp);
+ struct rb_node *node = dvi->vi_nc_tree.rbt_root;
+ struct namecache *ncp;
- KASSERT(rw_lock_held(&dnn->nn_lock));
+ KASSERT(rw_lock_held(&dvi->vi_nc_lock));
/*
- * Search the RB tree for the key. File lookup is very performance
- * sensitive, so here is an inlined lookup tailored for exactly
- * what's needed here (64-bit key and so on) that is much faster
- * than using rb_tree_find_node(). Elsewhere during entry/removal
- * the generic functions are used as it doesn't matter so much
- * there.
+ * Search the RB tree for the key. This is an inlined lookup
+ * tailored for exactly what's needed here (64-bit key and so on)
+ * that is quite a bit faster than using rb_tree_find_node().
+ * Elsewhere during entry/removal the usual functions are used as it
+ * doesn't matter there.
*/
for (;;) {
if (__predict_false(RB_SENTINEL_P(node))) {
return NULL;
}
- KASSERT((void *)&nc->nc_tree == (void *)nc);
- nc = (struct namecache *)node;
- KASSERT(nc->nc_dnn == dnn);
- if (nc->nc_key == key) {
+ KASSERT((void *)&ncp->nc_tree == (void *)ncp);
+ ncp = (struct namecache *)node;
+ KASSERT(ncp->nc_dvp == dvp);
+ if (ncp->nc_key == key) {
break;
}
- node = node->rb_nodes[nc->nc_key < key];
+ node = node->rb_nodes[ncp->nc_key < key];
}
/* Exclude collisions. */
- if (__predict_false(cache_namecmp(nc, name, namelen))) {
+ if (__predict_false(cache_namecmp(ncp, name, namelen))) {
return NULL;
}
@@ -455,10 +435,10 @@ cache_lookup_entry(struct nchnode *dnn,
* unlocked check, but it will rarely be wrong and even then there
* will be no harm caused.
*/
- if (__predict_false(nc->nc_lrulist != LRU_ACTIVE)) {
- cache_activate(nc);
+ if (__predict_false(ncp->nc_lrulist != LRU_ACTIVE)) {
+ cache_activate(ncp);
}
- return nc;
+ return ncp;
}
/*
@@ -516,8 +496,8 @@ cache_lookup(struct vnode *dvp, const ch
uint32_t nameiop, uint32_t cnflags,
int *iswht_ret, struct vnode **vn_ret)
{
- struct nchnode *dnn = VNODE_TO_VIMPL(dvp)->vi_ncache;
- struct namecache *nc;
+ vnode_impl_t *dvi = VNODE_TO_VIMPL(dvp);
+ struct namecache *ncp;
struct vnode *vp;
int64_t key;
int error;
@@ -553,10 +533,10 @@ cache_lookup(struct vnode *dvp, const ch
key = cache_key(name, namelen);
/* Now look for the name. */
- rw_enter(&dnn->nn_lock, op);
- nc = cache_lookup_entry(dnn, name, namelen, key);
- if (__predict_false(nc == NULL)) {
- rw_exit(&dnn->nn_lock);
+ rw_enter(&dvi->vi_nc_lock, op);
+ ncp = cache_lookup_entry(dvp, name, namelen, key);
+ if (__predict_false(ncp == NULL)) {
+ rw_exit(&dvi->vi_nc_lock);
COUNT(ncs_miss);
SDT_PROBE(vfs, namecache, lookup, miss, dvp,
name, namelen, 0, 0);
@@ -569,12 +549,12 @@ cache_lookup(struct vnode *dvp, const ch
* want cache entry to exist.
*/
KASSERT((cnflags & ISLASTCN) != 0);
- cache_remove(nc, true);
- rw_exit(&dnn->nn_lock);
+ cache_remove(ncp, true);
+ rw_exit(&dvi->vi_nc_lock);
COUNT(ncs_badhits);
return false;
}
- if (nc->nc_nn == NULL) {
+ if (ncp->nc_vp == NULL) {
if (nameiop == CREATE && (cnflags & ISLASTCN) != 0) {
/*
* Last component and we are preparing to create
@@ -582,7 +562,7 @@ cache_lookup(struct vnode *dvp, const ch
* entry.
*/
COUNT(ncs_badhits);
- cache_remove(nc, true);
+ cache_remove(ncp, true);
hit = false;
} else {
COUNT(ncs_neghits);
@@ -595,16 +575,16 @@ cache_lookup(struct vnode *dvp, const ch
/*
* Restore the ISWHITEOUT flag saved earlier.
*/
- *iswht_ret = nc->nc_whiteout;
+ *iswht_ret = ncp->nc_whiteout;
} else {
- KASSERT(!nc->nc_whiteout);
+ KASSERT(!ncp->nc_whiteout);
}
- rw_exit(&dnn->nn_lock);
+ rw_exit(&dvi->vi_nc_lock);
return hit;
}
- vp = nc->nc_vp;
+ vp = ncp->nc_vp;
mutex_enter(vp->v_interlock);
- rw_exit(&dnn->nn_lock);
+ rw_exit(&dvi->vi_nc_lock);
/*
* Unlocked except for the vnode interlock. Call vcache_tryvget().
@@ -650,20 +630,17 @@ cache_lookup_linked(struct vnode *dvp, c
struct vnode **vn_ret, krwlock_t **plock,
kauth_cred_t cred)
{
- struct nchnode *dnn = VNODE_TO_VIMPL(dvp)->vi_ncache;
- struct namecache *nc;
+ vnode_impl_t *dvi = VNODE_TO_VIMPL(dvp);
+ struct namecache *ncp;
int64_t key;
int error;
/* Establish default results. */
*vn_ret = NULL;
- /*
- * If disabled, or FS doesn't support us, bail out. This is an
- * unlocked check of dnn->nn_mode, but an incorrect answer doesn't
- * have any ill consequences.
- */
- if (__predict_false(!doingcache || dnn->nn_mode == VNOVAL)) {
+ /* If disabled, or file system doesn't support this, bail out. */
+ if (__predict_false(!doingcache ||
+ (dvp->v_mount->mnt_iflag & IMNT_NCLOOKUP) == 0)) {
return false;
}
@@ -680,22 +657,23 @@ cache_lookup_linked(struct vnode *dvp, c
* previous one (if any).
*
* The two lock holds mean that the directory can't go away while
- * here: the directory must be purged first, and both parent &
- * child's locks must be taken to do that.
+ * here: the directory must be purged with cache_purge() before
+ * being freed, and both parent & child's vi_nc_lock must be taken
+ * before that point is passed.
*
* However if there's no previous lock, like at the root of the
- * chain, then "dvp" must be referenced to prevent it going away
+ * chain, then "dvp" must be referenced to prevent dvp going away
* before we get its lock.
*
* Note that the two locks can be the same if looking up a dot, for
* example: /usr/bin/.
*/
- if (*plock != &dnn->nn_lock) {
- rw_enter(&dnn->nn_lock, RW_READER);
+ if (*plock != &dvi->vi_nc_lock) {
+ rw_enter(&dvi->vi_nc_lock, RW_READER);
if (*plock != NULL) {
rw_exit(*plock);
}
- *plock = &dnn->nn_lock;
+ *plock = &dvi->vi_nc_lock;
} else if (*plock == NULL) {
KASSERT(dvp->v_usecount > 0);
}
@@ -704,12 +682,12 @@ cache_lookup_linked(struct vnode *dvp, c
* First up check if the user is allowed to look up files in this
* directory.
*/
- KASSERT(dnn->nn_mode != VNOVAL && dnn->nn_uid != VNOVAL &&
- dnn->nn_gid != VNOVAL);
+ KASSERT(dvi->vi_nc_mode != VNOVAL && dvi->vi_nc_uid != VNOVAL &&
+ dvi->vi_nc_gid != VNOVAL);
error = kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(VEXEC,
- dvp->v_type, dnn->nn_mode & ALLPERMS), dvp, NULL,
- genfs_can_access(dvp->v_type, dnn->nn_mode & ALLPERMS,
- dnn->nn_uid, dnn->nn_gid, VEXEC, cred));
+ dvp->v_type, dvi->vi_nc_mode & ALLPERMS), dvp, NULL,
+ genfs_can_access(dvp->v_type, dvi->vi_nc_mode & ALLPERMS,
+ dvi->vi_nc_uid, dvi->vi_nc_gid, VEXEC, cred));
if (error != 0) {
COUNT(ncs_denied);
return false;
@@ -718,14 +696,14 @@ cache_lookup_linked(struct vnode *dvp, c
/*
* Now look for a matching cache entry.
*/
- nc = cache_lookup_entry(dnn, name, namelen, key);
- if (__predict_false(nc == NULL)) {
+ ncp = cache_lookup_entry(dvp, name, namelen, key);
+ if (__predict_false(ncp == NULL)) {
COUNT(ncs_miss);
SDT_PROBE(vfs, namecache, lookup, miss, dvp,
name, namelen, 0, 0);
return false;
}
- if (nc->nc_nn == NULL) {
+ if (ncp->nc_vp == NULL) {
/* found negative entry; vn is already null from above */
COUNT(ncs_neghits);
SDT_PROBE(vfs, namecache, lookup, hit, dvp, name, namelen, 0, 0);
@@ -741,7 +719,7 @@ cache_lookup_linked(struct vnode *dvp, c
* looking up the next component, or the caller will release it
* manually when finished.
*/
- *vn_ret = nc->nc_vp;
+ *vn_ret = ncp->nc_vp;
return true;
}
@@ -762,8 +740,8 @@ int
cache_revlookup(struct vnode *vp, struct vnode **dvpp, char **bpp, char *bufp,
bool checkaccess, int perms)
{
- struct nchnode *nn = VNODE_TO_VIMPL(vp)->vi_ncache;
- struct namecache *nc;
+ vnode_impl_t *vi = VNODE_TO_VIMPL(vp);
+ struct namecache *ncp;
struct vnode *dvp;
char *bp;
int error, nlen;
@@ -773,46 +751,49 @@ cache_revlookup(struct vnode *vp, struct
if (!doingcache)
goto out;
- rw_enter(&nn->nn_listlock, RW_READER);
+ rw_enter(&vi->vi_nc_listlock, RW_READER);
if (checkaccess) {
/*
* Check if the user is allowed to see. NOTE: this is
* checking for access on the "wrong" directory. getcwd()
* wants to see that there is access on every component
* along the way, not that there is access to any individual
- * component.
+ * component. Don't use this to check you can look in vp.
+ *
+ * I don't like it, I didn't come up with it, don't blame me!
*/
- KASSERT(nn->nn_mode != VNOVAL && nn->nn_uid != VNOVAL &&
- nn->nn_gid != VNOVAL);
+ KASSERT(vi->vi_nc_mode != VNOVAL && vi->vi_nc_uid != VNOVAL &&
+ vi->vi_nc_gid != VNOVAL);
error = kauth_authorize_vnode(curlwp->l_cred,
- KAUTH_ACCESS_ACTION(VEXEC, vp->v_type, nn->nn_mode &
+ KAUTH_ACCESS_ACTION(VEXEC, vp->v_type, vi->vi_nc_mode &
ALLPERMS), vp, NULL, genfs_can_access(vp->v_type,
- nn->nn_mode & ALLPERMS, nn->nn_uid, nn->nn_gid,
+ vi->vi_nc_mode & ALLPERMS, vi->vi_nc_uid, vi->vi_nc_gid,
perms, curlwp->l_cred));
if (error != 0) {
- rw_exit(&nn->nn_listlock);
+ rw_exit(&vi->vi_nc_listlock);
COUNT(ncs_denied);
return EACCES;
}
}
- TAILQ_FOREACH(nc, &nn->nn_list, nc_list) {
- KASSERT(nc->nc_nn == nn);
- KASSERT(nc->nc_dnn != NULL);
- nlen = nc->nc_nlen;
+ TAILQ_FOREACH(ncp, &vi->vi_nc_list, nc_list) {
+ KASSERT(ncp->nc_vp == vp);
+ KASSERT(ncp->nc_dvp != NULL);
+ nlen = ncp->nc_nlen;
+
/*
* The queue is partially sorted. Once we hit dots, nothing
* else remains but dots and dotdots, so bail out.
*/
- if (nc->nc_name[0] == '.') {
+ if (ncp->nc_name[0] == '.') {
if (nlen == 1 ||
- (nlen == 2 && nc->nc_name[1] == '.')) {
+ (nlen == 2 && ncp->nc_name[1] == '.')) {
break;
}
}
/* Record a hit on the entry. This is an unlocked read. */
- if (nc->nc_lrulist != LRU_ACTIVE) {
- cache_activate(nc);
+ if (ncp->nc_lrulist != LRU_ACTIVE) {
+ cache_activate(ncp);
}
if (bufp) {
@@ -820,18 +801,18 @@ cache_revlookup(struct vnode *vp, struct
bp -= nlen;
if (bp <= bufp) {
*dvpp = NULL;
- rw_exit(&nn->nn_listlock);
+ rw_exit(&vi->vi_nc_listlock);
SDT_PROBE(vfs, namecache, revlookup,
fail, vp, ERANGE, 0, 0, 0);
return (ERANGE);
}
- memcpy(bp, nc->nc_name, nlen);
+ memcpy(bp, ncp->nc_name, nlen);
*bpp = bp;
}
- dvp = nc->nc_dnn->nn_vp;
+ dvp = ncp->nc_dvp;
mutex_enter(dvp->v_interlock);
- rw_exit(&nn->nn_listlock);
+ rw_exit(&vi->vi_nc_listlock);
error = vcache_tryvget(dvp);
if (error) {
KASSERT(error == EBUSY);
@@ -848,7 +829,7 @@ cache_revlookup(struct vnode *vp, struct
COUNT(ncs_revhits);
return (0);
}
- rw_exit(&nn->nn_listlock);
+ rw_exit(&vi->vi_nc_listlock);
COUNT(ncs_revmiss);
out:
*dvpp = NULL;
@@ -862,10 +843,8 @@ void
cache_enter(struct vnode *dvp, struct vnode *vp,
const char *name, size_t namelen, uint32_t cnflags)
{
- struct nchnode *dnn = VNODE_TO_VIMPL(dvp)->vi_ncache;
- struct nchnode *nn;
- struct namecache *nc;
- struct namecache *onc;
+ vnode_impl_t *dvi = VNODE_TO_VIMPL(dvp);
+ struct namecache *ncp, *oncp;
int total;
/* First, check whether we can/should add a cache entry. */
@@ -890,33 +869,33 @@ cache_enter(struct vnode *dvp, struct vn
/* Now allocate a fresh entry. */
if (__predict_true(namelen <= NCHNAMLEN)) {
- nc = pool_cache_get(cache_pool, PR_WAITOK);
+ ncp = pool_cache_get(cache_pool, PR_WAITOK);
} else {
size_t sz = offsetof(struct namecache, nc_name[namelen]);
- nc = kmem_alloc(sz, KM_SLEEP);
+ ncp = kmem_alloc(sz, KM_SLEEP);
}
/* Fill in cache info. */
- nc->nc_dnn = dnn;
- nc->nc_key = cache_key(name, namelen);
- nc->nc_nlen = namelen;
- memcpy(nc->nc_name, name, namelen);
+ ncp->nc_dvp = dvp;
+ ncp->nc_key = cache_key(name, namelen);
+ ncp->nc_nlen = namelen;
+ memcpy(ncp->nc_name, name, namelen);
/*
* Insert to the directory. Concurrent lookups in the same
* directory may race for a cache entry. There can also be hash
* value collisions. If there's a entry there already, free it.
*/
- rw_enter(&dnn->nn_lock, RW_WRITER);
- onc = rb_tree_find_node(&dnn->nn_tree, &nc->nc_key);
- if (onc) {
- KASSERT(onc->nc_nlen == nc->nc_nlen);
- if (cache_namecmp(onc, name, namelen)) {
+ rw_enter(&dvi->vi_nc_lock, RW_WRITER);
+ oncp = rb_tree_find_node(&dvi->vi_nc_tree, &ncp->nc_key);
+ if (oncp) {
+ KASSERT(oncp->nc_nlen == ncp->nc_nlen);
+ if (cache_namecmp(oncp, name, namelen)) {
COUNT(ncs_collisions);
}
- cache_remove(onc, true);
+ cache_remove(oncp, true);
}
- rb_tree_insert_node(&dnn->nn_tree, nc);
+ rb_tree_insert_node(&dvi->vi_nc_tree, ncp);
/* Then insert to the vnode. */
if (vp == NULL) {
@@ -924,96 +903,79 @@ cache_enter(struct vnode *dvp, struct vn
* For negative hits, save the ISWHITEOUT flag so we can
* restore it later when the cache entry is used again.
*/
- nc->nc_vp = NULL;
- nc->nc_nn = NULL;
- nc->nc_whiteout = ((cnflags & ISWHITEOUT) != 0);
+ ncp->nc_vp = NULL;
+ ncp->nc_whiteout = ((cnflags & ISWHITEOUT) != 0);
} else {
- nn = VNODE_TO_VIMPL(vp)->vi_ncache;
/* Partially sort the per-vnode list: dots go to back. */
- rw_enter(&nn->nn_listlock, RW_WRITER);
+ vnode_impl_t *vi = VNODE_TO_VIMPL(vp);
+ rw_enter(&vi->vi_nc_listlock, RW_WRITER);
if ((namelen == 1 && name[0] == '.') ||
(namelen == 2 && name[0] == '.' && name[1] == '.')) {
- TAILQ_INSERT_TAIL(&nn->nn_list, nc, nc_list);
+ TAILQ_INSERT_TAIL(&vi->vi_nc_list, ncp, nc_list);
} else {
- TAILQ_INSERT_HEAD(&nn->nn_list, nc, nc_list);
+ TAILQ_INSERT_HEAD(&vi->vi_nc_list, ncp, nc_list);
}
- rw_exit(&nn->nn_listlock);
- nc->nc_vp = vp;
- nc->nc_nn = nn;
- nc->nc_whiteout = false;
+ rw_exit(&vi->vi_nc_listlock);
+ ncp->nc_vp = vp;
+ ncp->nc_whiteout = false;
}
/*
- * Finally, insert to the tail of the ACTIVE LRU list (new) and with
- * the LRU lock held take the to opportunity to incrementally
+ * Finally, insert to the tail of the ACTIVE LRU list (new) and
+ * with the LRU lock held take the to opportunity to incrementally
* balance the lists.
*/
mutex_enter(&cache_lru_lock);
- nc->nc_lrulist = LRU_ACTIVE;
+ ncp->nc_lrulist = LRU_ACTIVE;
cache_lru.count[LRU_ACTIVE]++;
- TAILQ_INSERT_TAIL(&cache_lru.list[LRU_ACTIVE], nc, nc_lru);
+ TAILQ_INSERT_TAIL(&cache_lru.list[LRU_ACTIVE], ncp, nc_lru);
cache_deactivate();
mutex_exit(&cache_lru_lock);
- rw_exit(&dnn->nn_lock);
-}
-
-/*
- * First set of identity info in cache for a vnode. If a file system calls
- * this, it means the FS supports in-cache lookup. We only care about
- * directories so ignore other updates.
- */
-void
-cache_set_id(struct vnode *dvp, mode_t mode, uid_t uid, gid_t gid)
-{
- struct nchnode *nn = VNODE_TO_VIMPL(dvp)->vi_ncache;
-
- if (dvp->v_type == VDIR) {
- rw_enter(&nn->nn_lock, RW_WRITER);
- rw_enter(&nn->nn_listlock, RW_WRITER);
- KASSERT(nn->nn_mode == VNOVAL);
- KASSERT(nn->nn_uid == VNOVAL);
- KASSERT(nn->nn_gid == VNOVAL);
- nn->nn_mode = mode;
- nn->nn_uid = uid;
- nn->nn_gid = gid;
- rw_exit(&nn->nn_listlock);
- rw_exit(&nn->nn_lock);
- }
+ rw_exit(&dvi->vi_nc_lock);
}
/*
- * Update of identity info in cache for a vnode. If we didn't get ID info
- * to begin with, then the file system doesn't support in-cache lookup,
- * so ignore the update. We only care about directories.
+ * Set identity info in cache for a vnode. We only care about directories
+ * so ignore other updates.
*/
void
-cache_update_id(struct vnode *dvp, mode_t mode, uid_t uid, gid_t gid)
+cache_enter_id(struct vnode *vp, mode_t mode, uid_t uid, gid_t gid)
{
- struct nchnode *nn = VNODE_TO_VIMPL(dvp)->vi_ncache;
+ vnode_impl_t *vi = VNODE_TO_VIMPL(vp);
- if (dvp->v_type == VDIR) {
- rw_enter(&nn->nn_lock, RW_WRITER);
- rw_enter(&nn->nn_listlock, RW_WRITER);
- if (nn->nn_mode != VNOVAL) {
- nn->nn_mode = mode;
- nn->nn_uid = uid;
- nn->nn_gid = gid;
- }
- rw_exit(&nn->nn_listlock);
- rw_exit(&nn->nn_lock);
+ if (vp->v_type == VDIR) {
+ /* Grab both locks, for forward & reverse lookup. */
+ rw_enter(&vi->vi_nc_lock, RW_WRITER);
+ rw_enter(&vi->vi_nc_listlock, RW_WRITER);
+ vi->vi_nc_mode = mode;
+ vi->vi_nc_uid = uid;
+ vi->vi_nc_gid = gid;
+ rw_exit(&vi->vi_nc_listlock);
+ rw_exit(&vi->vi_nc_lock);
}
}
/*
- * Return true if we have identity for the given vnode.
+ * Return true if we have identity for the given vnode, and use as an
+ * opportunity to confirm that everything squares up.
+ *
+ * Because of shared code, some file systems could provide partial
+ * information, missing some updates, so always check the mount flag
+ * instead of looking for !VNOVAL.
*/
bool
-cache_have_id(struct vnode *dvp)
+cache_have_id(struct vnode *vp)
{
- struct nchnode *nn = VNODE_TO_VIMPL(dvp)->vi_ncache;
- /* Unlocked check. Only goes VNOVAL -> valid, never back. */
- return nn->nn_mode != VNOVAL;
+ if (vp->v_type == VDIR &&
+ (vp->v_mount->mnt_iflag & IMNT_NCLOOKUP) != 0) {
+ KASSERT(VNODE_TO_VIMPL(vp)->vi_nc_mode != VNOVAL);
+ KASSERT(VNODE_TO_VIMPL(vp)->vi_nc_uid != VNOVAL);
+ KASSERT(VNODE_TO_VIMPL(vp)->vi_nc_gid != VNOVAL);
+ return true;
+ } else {
+ return false;
+ }
}
/*
@@ -1028,11 +990,6 @@ nchinit(void)
NULL, NULL);
KASSERT(cache_pool != NULL);
- cache_node_pool = pool_cache_init(sizeof(struct nchnode),
- coherency_unit, 0, 0, "nchnode", NULL, IPL_NONE, NULL,
- NULL, NULL);
- KASSERT(cache_node_pool != NULL);
-
mutex_init(&cache_lru_lock, MUTEX_DEFAULT, IPL_NONE);
TAILQ_INIT(&cache_lru.list[LRU_ACTIVE]);
TAILQ_INIT(&cache_lru.list[LRU_INACTIVE]);
@@ -1067,19 +1024,15 @@ cache_cpu_init(struct cpu_info *ci)
void
cache_vnode_init(struct vnode *vp)
{
- struct nchnode *nn;
+ vnode_impl_t *vi = VNODE_TO_VIMPL(vp);
- nn = pool_cache_get(cache_node_pool, PR_WAITOK);
- rw_init(&nn->nn_lock);
- rw_init(&nn->nn_listlock);
- rb_tree_init(&nn->nn_tree, &cache_rbtree_ops);
- TAILQ_INIT(&nn->nn_list);
- nn->nn_mode = VNOVAL;
- nn->nn_uid = VNOVAL;
- nn->nn_gid = VNOVAL;
- nn->nn_vp = vp;
-
- VNODE_TO_VIMPL(vp)->vi_ncache = nn;
+ rw_init(&vi->vi_nc_lock);
+ rw_init(&vi->vi_nc_listlock);
+ rb_tree_init(&vi->vi_nc_tree, &cache_rbtree_ops);
+ TAILQ_INIT(&vi->vi_nc_list);
+ vi->vi_nc_mode = VNOVAL;
+ vi->vi_nc_uid = VNOVAL;
+ vi->vi_nc_gid = VNOVAL;
}
/*
@@ -1088,16 +1041,12 @@ cache_vnode_init(struct vnode *vp)
void
cache_vnode_fini(struct vnode *vp)
{
- struct nchnode *nn = VNODE_TO_VIMPL(vp)->vi_ncache;
+ vnode_impl_t *vi = VNODE_TO_VIMPL(vp);
- KASSERT(nn != NULL);
- KASSERT(nn->nn_vp == vp);
- KASSERT(RB_TREE_MIN(&nn->nn_tree) == NULL);
- KASSERT(TAILQ_EMPTY(&nn->nn_list));
-
- rw_destroy(&nn->nn_lock);
- rw_destroy(&nn->nn_listlock);
- pool_cache_put(cache_node_pool, nn);
+ KASSERT(RB_TREE_MIN(&vi->vi_nc_tree) == NULL);
+ KASSERT(TAILQ_EMPTY(&vi->vi_nc_list));
+ rw_destroy(&vi->vi_nc_lock);
+ rw_destroy(&vi->vi_nc_listlock);
}
/*
@@ -1107,26 +1056,26 @@ cache_vnode_fini(struct vnode *vp)
static void
cache_purge_parents(struct vnode *vp)
{
- struct nchnode *nn = VNODE_TO_VIMPL(vp)->vi_ncache;
- struct nchnode *dnn, *blocked;
- struct namecache *nc;
- struct vnode *dvp;
+ vnode_impl_t *dvi, *vi = VNODE_TO_VIMPL(vp);
+ struct vnode *dvp, *blocked;
+ struct namecache *ncp;
SDT_PROBE(vfs, namecache, purge, parents, vp, 0, 0, 0, 0);
blocked = NULL;
- rw_enter(&nn->nn_listlock, RW_WRITER);
- while ((nc = TAILQ_FIRST(&nn->nn_list)) != NULL) {
+ rw_enter(&vi->vi_nc_listlock, RW_WRITER);
+ while ((ncp = TAILQ_FIRST(&vi->vi_nc_list)) != NULL) {
/*
* Locking in the wrong direction. Try for a hold on the
* directory node's lock, and if we get it then all good,
* nuke the entry and move on to the next.
*/
- dnn = nc->nc_dnn;
- if (rw_tryenter(&dnn->nn_lock, RW_WRITER)) {
- cache_remove(nc, false);
- rw_exit(&dnn->nn_lock);
+ dvp = ncp->nc_dvp;
+ dvi = VNODE_TO_VIMPL(dvp);
+ if (rw_tryenter(&dvi->vi_nc_lock, RW_WRITER)) {
+ cache_remove(ncp, false);
+ rw_exit(&dvi->vi_nc_lock);
blocked = NULL;
continue;
}
@@ -1136,27 +1085,26 @@ cache_purge_parents(struct vnode *vp)
* lock held or the system could deadlock.
*
* Take a hold on the directory vnode to prevent it from
- * being freed (taking the nchnode & lock with it). Then
+ * being freed (taking the vnode & lock with it). Then
* wait for the lock to become available with no other locks
* held, and retry.
*
* If this happens twice in a row, give the other side a
* breather; we can do nothing until it lets go.
*/
- dvp = dnn->nn_vp;
vhold(dvp);
- rw_exit(&nn->nn_listlock);
- rw_enter(&dnn->nn_lock, RW_WRITER);
+ rw_exit(&vi->vi_nc_listlock);
+ rw_enter(&dvi->vi_nc_lock, RW_WRITER);
/* Do nothing. */
- rw_exit(&dnn->nn_lock);
+ rw_exit(&dvi->vi_nc_lock);
holdrele(dvp);
- if (blocked == dnn) {
+ if (blocked == dvp) {
kpause("ncpurge", false, 1, NULL);
}
- rw_enter(&nn->nn_listlock, RW_WRITER);
- blocked = dnn;
+ rw_enter(&vi->vi_nc_listlock, RW_WRITER);
+ blocked = dvp;
}
- rw_exit(&nn->nn_listlock);
+ rw_exit(&vi->vi_nc_listlock);
}
/*
@@ -1166,17 +1114,20 @@ cache_purge_parents(struct vnode *vp)
static void
cache_purge_children(struct vnode *dvp)
{
- struct nchnode *dnn = VNODE_TO_VIMPL(dvp)->vi_ncache;
- struct namecache *nc;
+ vnode_impl_t *dvi = VNODE_TO_VIMPL(dvp);
+ struct namecache *ncp;
SDT_PROBE(vfs, namecache, purge, children, dvp, 0, 0, 0, 0);
- rw_enter(&dnn->nn_lock, RW_WRITER);
- while ((nc = rb_tree_iterate(&dnn->nn_tree, NULL, RB_DIR_RIGHT))
- != NULL) {
- cache_remove(nc, true);
+ rw_enter(&dvi->vi_nc_lock, RW_WRITER);
+ for (;;) {
+ ncp = rb_tree_iterate(&dvi->vi_nc_tree, NULL, RB_DIR_RIGHT);
+ if (ncp == NULL) {
+ break;
+ }
+ cache_remove(ncp, true);
}
- rw_exit(&dnn->nn_lock);
+ rw_exit(&dvi->vi_nc_lock);
}
/*
@@ -1186,19 +1137,19 @@ cache_purge_children(struct vnode *dvp)
static void
cache_purge_name(struct vnode *dvp, const char *name, size_t namelen)
{
- struct nchnode *dnn = VNODE_TO_VIMPL(dvp)->vi_ncache;
- struct namecache *nc;
+ vnode_impl_t *dvi = VNODE_TO_VIMPL(dvp);
+ struct namecache *ncp;
int64_t key;
SDT_PROBE(vfs, namecache, purge, name, name, namelen, 0, 0, 0);
key = cache_key(name, namelen);
- rw_enter(&dnn->nn_lock, RW_WRITER);
- nc = cache_lookup_entry(dnn, name, namelen, key);
- if (nc) {
- cache_remove(nc, true);
+ rw_enter(&dvi->vi_nc_lock, RW_WRITER);
+ ncp = cache_lookup_entry(dvp, name, namelen, key);
+ if (ncp) {
+ cache_remove(ncp, true);
}
- rw_exit(&dnn->nn_lock);
+ rw_exit(&dvi->vi_nc_lock);
}
/*
@@ -1221,55 +1172,62 @@ cache_purge1(struct vnode *vp, const cha
}
/*
+ * vnode filter for cache_purgevfs().
+ */
+static bool
+cache_vdir_filter(void *cookie, vnode_t *vp)
+{
+
+ return vp->v_type == VDIR;
+}
+
+/*
* Cache flush, a whole filesystem; called when filesys is umounted to
* remove entries that would now be invalid.
- *
- * In the BSD implementation this traversed the LRU list. To avoid locking
- * difficulties, and avoid scanning a ton of namecache entries that we have
- * no interest in, scan the mount's vnode list instead.
*/
void
cache_purgevfs(struct mount *mp)
{
- struct vnode_iterator *marker;
- vnode_t *vp;
+ struct vnode_iterator *iter;
+ vnode_t *dvp;
- vfs_vnode_iterator_init(mp, &marker);
- while ((vp = vfs_vnode_iterator_next(marker, NULL, NULL))) {
- cache_purge_children(vp);
- if ((vp->v_vflag & VV_ROOT) != 0) {
- cache_purge_parents(vp);
+ vfs_vnode_iterator_init(mp, &iter);
+ for (;;) {
+ dvp = vfs_vnode_iterator_next(iter, cache_vdir_filter, NULL);
+ if (dvp == NULL) {
+ break;
}
- vrele(vp);
+ cache_purge_children(dvp);
+ vrele(dvp);
}
- vfs_vnode_iterator_destroy(marker);
+ vfs_vnode_iterator_destroy(iter);
}
/*
* Re-queue an entry onto the correct LRU list, after it has scored a hit.
*/
static void
-cache_activate(struct namecache *nc)
+cache_activate(struct namecache *ncp)
{
mutex_enter(&cache_lru_lock);
/* Put on tail of ACTIVE list, since it just scored a hit. */
- TAILQ_REMOVE(&cache_lru.list[nc->nc_lrulist], nc, nc_lru);
- TAILQ_INSERT_TAIL(&cache_lru.list[LRU_ACTIVE], nc, nc_lru);
- cache_lru.count[nc->nc_lrulist]--;
+ TAILQ_REMOVE(&cache_lru.list[ncp->nc_lrulist], ncp, nc_lru);
+ TAILQ_INSERT_TAIL(&cache_lru.list[LRU_ACTIVE], ncp, nc_lru);
+ cache_lru.count[ncp->nc_lrulist]--;
cache_lru.count[LRU_ACTIVE]++;
- nc->nc_lrulist = LRU_ACTIVE;
+ ncp->nc_lrulist = LRU_ACTIVE;
mutex_exit(&cache_lru_lock);
}
/*
* Try to balance the LRU lists. Pick some victim entries, and re-queue
- * them from the head of the ACTIVE list to the tail of the INACTIVE list.
+ * them from the head of the active list to the tail of the inactive list.
*/
static void
cache_deactivate(void)
{
- struct namecache *nc;
+ struct namecache *ncp;
int total, i;
KASSERT(mutex_owned(&cache_lru_lock));
@@ -1280,21 +1238,27 @@ cache_deactivate(void)
return;
}
- /* If not out of balance, don't bother. */
+ /*
+ * Aim for a 1:1 ratio of active to inactive. It's a bit wet finger
+ * in the air here, but this is to allow each potential victim a
+ * reasonable amount of time to cycle through the inactive list in
+ * order to score a hit and be reactivated, while trying not to
+ * cause reactivations too frequently.
+ */
if (cache_lru.count[LRU_ACTIVE] < cache_lru.count[LRU_INACTIVE]) {
return;
}
- /* Move victim from head of ACTIVE list, to tail of INACTIVE. */
+ /* Move only a few at a time; will catch up eventually. */
for (i = 0; i < cache_lru_maxdeact; i++) {
- nc = TAILQ_FIRST(&cache_lru.list[LRU_ACTIVE]);
- if (nc == NULL) {
+ ncp = TAILQ_FIRST(&cache_lru.list[LRU_ACTIVE]);
+ if (ncp == NULL) {
break;
}
- KASSERT(nc->nc_lrulist == LRU_ACTIVE);
- nc->nc_lrulist = LRU_INACTIVE;
- TAILQ_REMOVE(&cache_lru.list[LRU_ACTIVE], nc, nc_lru);
- TAILQ_INSERT_TAIL(&cache_lru.list[LRU_INACTIVE], nc, nc_lru);
+ KASSERT(ncp->nc_lrulist == LRU_ACTIVE);
+ ncp->nc_lrulist = LRU_INACTIVE;
+ TAILQ_REMOVE(&cache_lru.list[LRU_ACTIVE], ncp, nc_lru);
+ TAILQ_INSERT_TAIL(&cache_lru.list[LRU_INACTIVE], ncp, nc_lru);
cache_lru.count[LRU_ACTIVE]--;
cache_lru.count[LRU_INACTIVE]++;
}
@@ -1312,27 +1276,34 @@ cache_deactivate(void)
static void
cache_reclaim(void)
{
- struct namecache *nc;
- struct nchnode *dnn;
- int toscan, total;
+ struct namecache *ncp;
+ vnode_impl_t *dvi;
+ int toscan;
- /* Scan up to a preset maxium number of entries. */
+ /*
+ * Scan up to a preset maxium number of entries, but no more than
+ * 0.8% of the total at once (to allow for very small systems).
+ *
+ * On bigger systems, do a larger chunk of work to reduce the number
+ * of times that cache_lru_lock is held for any length of time.
+ */
mutex_enter(&cache_lru_lock);
- toscan = cache_lru_maxscan;
- total = cache_lru.count[LRU_ACTIVE] + cache_lru.count[LRU_INACTIVE];
- SDT_PROBE(vfs, namecache, prune, done, total, toscan, 0, 0, 0);
+ toscan = MIN(cache_lru_maxscan, desiredvnodes >> 7);
+ toscan = MAX(toscan, 1);
+ SDT_PROBE(vfs, namecache, prune, done, cache_lru.count[LRU_ACTIVE] +
+ cache_lru.count[LRU_INACTIVE], toscan, 0, 0, 0);
while (toscan-- != 0) {
/* First try to balance the lists. */
cache_deactivate();
/* Now look for a victim on head of inactive list (old). */
- nc = TAILQ_FIRST(&cache_lru.list[LRU_INACTIVE]);
- if (nc == NULL) {
+ ncp = TAILQ_FIRST(&cache_lru.list[LRU_INACTIVE]);
+ if (ncp == NULL) {
break;
}
- dnn = nc->nc_dnn;
- KASSERT(nc->nc_lrulist == LRU_INACTIVE);
- KASSERT(dnn != NULL);
+ dvi = VNODE_TO_VIMPL(ncp->nc_dvp);
+ KASSERT(ncp->nc_lrulist == LRU_INACTIVE);
+ KASSERT(dvi != NULL);
/*
* Locking in the wrong direction. If we can't get the
@@ -1340,24 +1311,24 @@ cache_reclaim(void)
* cause problems for the next guy in here, so send the
* entry to the back of the list.
*/
- if (!rw_tryenter(&dnn->nn_lock, RW_WRITER)) {
+ if (!rw_tryenter(&dvi->vi_nc_lock, RW_WRITER)) {
TAILQ_REMOVE(&cache_lru.list[LRU_INACTIVE],
- nc, nc_lru);
+ ncp, nc_lru);
TAILQ_INSERT_TAIL(&cache_lru.list[LRU_INACTIVE],
- nc, nc_lru);
+ ncp, nc_lru);
continue;
}
/*
* Now have the victim entry locked. Drop the LRU list
- * lock, purge the entry, and start over. The hold on the
- * directory lock will prevent the vnode from vanishing
- * until finished (the vnode owner will call cache_purge()
- * on dvp before it disappears).
+ * lock, purge the entry, and start over. The hold on
+ * vi_nc_lock will prevent the vnode from vanishing until
+ * finished (cache_purge() will be called on dvp before it
+ * disappears, and that will wait on vi_nc_lock).
*/
mutex_exit(&cache_lru_lock);
- cache_remove(nc, true);
- rw_exit(&dnn->nn_lock);
+ cache_remove(ncp, true);
+ rw_exit(&dvi->vi_nc_lock);
mutex_enter(&cache_lru_lock);
}
mutex_exit(&cache_lru_lock);
@@ -1394,6 +1365,7 @@ static int
cache_stat_sysctl(SYSCTLFN_ARGS)
{
CPU_INFO_ITERATOR cii;
+ struct nchstats stats;
struct cpu_info *ci;
if (oldp == NULL) {
@@ -1407,31 +1379,30 @@ cache_stat_sysctl(SYSCTLFN_ARGS)
}
sysctl_unlock();
- mutex_enter(&cache_lru_lock);
- memset(&nchstats, 0, sizeof(nchstats));
+ memset(&stats, 0, sizeof(nchstats));
for (CPU_INFO_FOREACH(cii, ci)) {
struct nchstats_percpu *np = ci->ci_data.cpu_nch;
- nchstats.ncs_goodhits += np->ncs_goodhits;
- nchstats.ncs_neghits += np->ncs_neghits;
- nchstats.ncs_badhits += np->ncs_badhits;
- nchstats.ncs_falsehits += np->ncs_falsehits;
- nchstats.ncs_miss += np->ncs_miss;
- nchstats.ncs_long += np->ncs_long;
- nchstats.ncs_pass2 += np->ncs_pass2;
- nchstats.ncs_2passes += np->ncs_2passes;
- nchstats.ncs_revhits += np->ncs_revhits;
- nchstats.ncs_revmiss += np->ncs_revmiss;
- nchstats.ncs_collisions += np->ncs_collisions;
- nchstats.ncs_denied += np->ncs_denied;
+ stats.ncs_goodhits += np->ncs_goodhits;
+ stats.ncs_neghits += np->ncs_neghits;
+ stats.ncs_badhits += np->ncs_badhits;
+ stats.ncs_falsehits += np->ncs_falsehits;
+ stats.ncs_miss += np->ncs_miss;
+ stats.ncs_long += np->ncs_long;
+ stats.ncs_pass2 += np->ncs_pass2;
+ stats.ncs_2passes += np->ncs_2passes;
+ stats.ncs_revhits += np->ncs_revhits;
+ stats.ncs_revmiss += np->ncs_revmiss;
+ stats.ncs_collisions += np->ncs_collisions;
+ stats.ncs_denied += np->ncs_denied;
}
- nchstats.ncs_active = cache_lru.count[LRU_ACTIVE];
- nchstats.ncs_inactive = cache_lru.count[LRU_INACTIVE];
+ mutex_enter(&cache_lru_lock);
+ memcpy(&nchstats, &stats, sizeof(nchstats));
mutex_exit(&cache_lru_lock);
sysctl_relock();
- *oldlenp = MIN(sizeof(nchstats), *oldlenp);
- return sysctl_copyout(l, &nchstats, oldp, *oldlenp);
+ *oldlenp = MIN(sizeof(stats), *oldlenp);
+ return sysctl_copyout(l, &stats, oldp, *oldlenp);
}
/*
@@ -1442,28 +1413,28 @@ cache_stat_sysctl(SYSCTLFN_ARGS)
void
namecache_print(struct vnode *vp, void (*pr)(const char *, ...))
{
- struct nchnode *dnn = NULL;
- struct namecache *nc;
+ struct vnode *dvp = NULL;
+ struct namecache *ncp;
enum cache_lru_id id;
for (id = 0; id < LRU_COUNT; id++) {
- TAILQ_FOREACH(nc, &cache_lru.list[id], nc_lru) {
- if (nc->nc_vp == vp) {
- (*pr)("name %.*s\n", nc->nc_nlen,
- nc->nc_name);
- dnn = nc->nc_dnn;
+ TAILQ_FOREACH(ncp, &cache_lru.list[id], nc_lru) {
+ if (ncp->nc_vp == vp) {
+ (*pr)("name %.*s\n", ncp->nc_nlen,
+ ncp->nc_name);
+ dvp = ncp->nc_dvp;
}
}
}
- if (dnn == NULL) {
+ if (dvp == NULL) {
(*pr)("name not found\n");
return;
}
for (id = 0; id < LRU_COUNT; id++) {
- TAILQ_FOREACH(nc, &cache_lru.list[id], nc_lru) {
- if (nc->nc_nn == dnn) {
- (*pr)("parent %.*s\n", nc->nc_nlen,
- nc->nc_name);
+ TAILQ_FOREACH(ncp, &cache_lru.list[id], nc_lru) {
+ if (ncp->nc_vp == dvp) {
+ (*pr)("parent %.*s\n", ncp->nc_nlen,
+ ncp->nc_name);
}
}
}
Index: src/sys/sys/namei.src
diff -u src/sys/sys/namei.src:1.47.2.5 src/sys/sys/namei.src:1.47.2.6
--- src/sys/sys/namei.src:1.47.2.5 Sun Jan 19 21:19:25 2020
+++ src/sys/sys/namei.src Fri Jan 24 16:48:59 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: namei.src,v 1.47.2.5 2020/01/19 21:19:25 ad Exp $ */
+/* $NetBSD: namei.src,v 1.47.2.6 2020/01/24 16:48:59 ad Exp $ */
/*
* Copyright (c) 1985, 1989, 1991, 1993
@@ -219,14 +219,13 @@ struct namecache {
struct rb_node nc_tree; /* d red-black tree, must be first */
TAILQ_ENTRY(namecache) nc_list; /* v vp's list of cache entries */
TAILQ_ENTRY(namecache) nc_lru; /* l pseudo-lru chain */
- struct nchnode *nc_dnn; /* - nchnode of parent of name */
- struct nchnode *nc_nn; /* - nchnode the name refers to */
+ struct vnode *nc_dvp; /* - vnode of parent of name */
struct vnode *nc_vp; /* - vnode the name refers to */
int64_t nc_key; /* - hash key */
int nc_lrulist; /* l which LRU list its on */
short nc_nlen; /* - length of the name */
char nc_whiteout; /* - true if a whiteout */
- char nc_name[33]; /* - segment name */
+ char nc_name[41]; /* - segment name */
};
#endif
@@ -297,8 +296,7 @@ int cache_revlookup(struct vnode *, stru
int cache_diraccess(struct vnode *, int);
void cache_enter(struct vnode *, struct vnode *,
const char *, size_t, uint32_t);
-void cache_set_id(struct vnode *, mode_t, uid_t, gid_t);
-void cache_update_id(struct vnode *, mode_t, uid_t, gid_t);
+void cache_enter_id(struct vnode *, mode_t, uid_t, gid_t);
bool cache_have_id(struct vnode *);
void cache_vnode_init(struct vnode * );
void cache_vnode_fini(struct vnode * );
@@ -332,8 +330,6 @@ void namecache_print(struct vnode *, voi
type ncs_revhits; /* reverse-cache hits */ \
type ncs_revmiss; /* reverse-cache misses */ \
type ncs_collisions; /* hash value collisions */ \
- type ncs_active; /* active cache entries */ \
- type ncs_inactive; /* inactive cache entries */ \
type ncs_denied; /* access denied */ \
}
Index: src/sys/ufs/ffs/ffs_vfsops.c
diff -u src/sys/ufs/ffs/ffs_vfsops.c:1.362.4.3 src/sys/ufs/ffs/ffs_vfsops.c:1.362.4.4
--- src/sys/ufs/ffs/ffs_vfsops.c:1.362.4.3 Sun Jan 19 21:21:55 2020
+++ src/sys/ufs/ffs/ffs_vfsops.c Fri Jan 24 16:48:59 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: ffs_vfsops.c,v 1.362.4.3 2020/01/19 21:21:55 ad Exp $ */
+/* $NetBSD: ffs_vfsops.c,v 1.362.4.4 2020/01/24 16:48:59 ad Exp $ */
/*-
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -61,7 +61,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.362.4.3 2020/01/19 21:21:55 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.362.4.4 2020/01/24 16:48:59 ad Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ffs.h"
@@ -1453,7 +1453,8 @@ ffs_mountfs(struct vnode *devvp, struct
mp->mnt_fs_bshift = fs->fs_bshift;
mp->mnt_dev_bshift = DEV_BSHIFT; /* XXX */
mp->mnt_flag |= MNT_LOCAL;
- mp->mnt_iflag |= IMNT_MPSAFE | IMNT_CAN_RWTORO | IMNT_SHRLOOKUP;
+ mp->mnt_iflag |= IMNT_MPSAFE | IMNT_CAN_RWTORO | IMNT_SHRLOOKUP |
+ IMNT_NCLOOKUP;
#ifdef FFS_EI
if (needswap)
ump->um_flags |= UFS_NEEDSWAP;
@@ -2082,7 +2083,7 @@ ffs_loadvnode(struct mount *mp, struct v
ip->i_gid = ip->i_ffs1_ogid; /* XXX */
} /* XXX */
uvm_vnp_setsize(vp, ip->i_size);
- cache_set_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
+ cache_enter_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
*new_key = &ip->i_number;
return 0;
}
@@ -2204,7 +2205,7 @@ ffs_newvnode(struct mount *mp, struct vn
}
uvm_vnp_setsize(vp, ip->i_size);
- cache_set_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
+ cache_enter_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
*new_key = &ip->i_number;
return 0;
}
Index: src/sys/ufs/ufs/ufs_vnops.c
diff -u src/sys/ufs/ufs/ufs_vnops.c:1.248.2.1 src/sys/ufs/ufs/ufs_vnops.c:1.248.2.2
--- src/sys/ufs/ufs/ufs_vnops.c:1.248.2.1 Fri Jan 17 22:26:26 2020
+++ src/sys/ufs/ufs/ufs_vnops.c Fri Jan 24 16:48:59 2020
@@ -1,7 +1,7 @@
-/* $NetBSD: ufs_vnops.c,v 1.248.2.1 2020/01/17 22:26:26 ad Exp $ */
+/* $NetBSD: ufs_vnops.c,v 1.248.2.2 2020/01/24 16:48:59 ad Exp $ */
/*-
- * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * Copyright (c) 2008, 2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.248.2.1 2020/01/17 22:26:26 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.248.2.2 2020/01/24 16:48:59 ad Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ffs.h"
@@ -621,7 +621,7 @@ ufs_setattr(void *v)
}
VN_KNOTE(vp, NOTE_ATTRIB);
out:
- cache_update_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
+ cache_enter_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
return (error);
}
@@ -649,7 +649,7 @@ ufs_chmod(struct vnode *vp, int mode, ka
ip->i_flag |= IN_CHANGE;
DIP_ASSIGN(ip, mode, ip->i_mode);
UFS_WAPBL_UPDATE(vp, NULL, NULL, 0);
- cache_update_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
+ cache_enter_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
return (0);
}
@@ -710,7 +710,7 @@ ufs_chown(struct vnode *vp, uid_t uid, g
#endif /* QUOTA || QUOTA2 */
ip->i_flag |= IN_CHANGE;
UFS_WAPBL_UPDATE(vp, NULL, NULL, 0);
- cache_update_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
+ cache_enter_id(vp, ip->i_mode, ip->i_uid, ip->i_gid);
return (0);
}