diff --git a/sys/vfs/tmpfs/tmpfs.h b/sys/vfs/tmpfs/tmpfs.h index 7e8a7e6..2a4d76e 100644 --- a/sys/vfs/tmpfs/tmpfs.h +++ b/sys/vfs/tmpfs/tmpfs.h @@ -63,6 +63,10 @@ MALLOC_DECLARE(M_TMPFSMNT); */ struct tmpfs_dirent { TAILQ_ENTRY(tmpfs_dirent) td_entries; + TAILQ_ENTRY(tmpfs_dirent) td_hash; + int td_hashq; + int td_namelenq; + char *td_hashname; /* Length of the name stored in this directory entry. This avoids * the need to recalculate it every time the name is used. */ @@ -91,6 +95,17 @@ struct tmpfs_dirent { * importantly, to remove redundancy. */ TAILQ_HEAD(tmpfs_dir, tmpfs_dirent); +#define TH_HASH (64) + +struct tmpfs_hdir { + /* Head of a tail-queue that links the contents of + * the directory together. See above for a + * description of its contents. */ + struct tmpfs_dir th_dirhead; + + struct tmpfs_dir th_hash[TH_HASH]; +}; + /* Each entry in a directory has a cookie that identifies it. Cookies * supersede offsets within directories because, given how tmpfs stores * directories in memory, there is no such thing as an offset. (Emulating @@ -252,10 +267,7 @@ struct tmpfs_node { * this property identifies the root node. */ struct tmpfs_node * tn_parent; - /* Head of a tail-queue that links the contents of - * the directory together. See above for a - * description of its contents. */ - struct tmpfs_dir tn_dirhead; + struct tmpfs_hdir tn_hdirhead; /* Number and pointer of the first directory entry * returned by the readdir operation if it were diff --git a/sys/vfs/tmpfs/tmpfs_subr.c b/sys/vfs/tmpfs/tmpfs_subr.c index 14e70b9..9c77220 100644 --- a/sys/vfs/tmpfs/tmpfs_subr.c +++ b/sys/vfs/tmpfs/tmpfs_subr.c @@ -1,5 +1,7 @@ /* $NetBSD: tmpfs_subr.c,v 1.35 2007/07/09 21:10:50 ad Exp $ */ +#include +#include /*- * Copyright (c) 2005 The NetBSD Foundation, Inc. * All rights reserved. @@ -44,6 +46,7 @@ #include #include #include +#include #include #include @@ -55,6 +58,21 @@ #include static ino_t tmpfs_fetch_ino(struct tmpfs_mount *); +int tmpfs_dirhash(char *, uint16_t); +/* --------------------------------------------------------------------- */ +int +tmpfs_dirhash(char *name, uint16_t namelen) +{ + uint32_t hash; + char *c; + int len; + + c = index(name, '\0'); + len = min(namelen, c - name); + hash = fnv_32_buf(name, len, FNV1_32_INIT); + + return (hash & (TH_HASH - 1)); +} /* --------------------------------------------------------------------- */ @@ -88,6 +106,7 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type, struct tmpfs_node *nnode; struct timespec ts; udev_t rdev; + int i; /* If the root directory of the 'tmp' file system is not yet * allocated, this must be the request to do it. */ @@ -129,7 +148,10 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type, break; case VDIR: - TAILQ_INIT(&nnode->tn_dir.tn_dirhead); + TAILQ_INIT(&nnode->tn_dir.tn_hdirhead.th_dirhead); + for (i = 0; i < TH_HASH; i++) + TAILQ_INIT(&nnode->tn_dir.tn_hdirhead.th_hash[i]); + KKASSERT(parent != nnode); KKASSERT(IMPLIES(parent == NULL, tmp->tm_root == NULL)); nnode->tn_dir.tn_parent = parent; @@ -327,6 +349,8 @@ tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node, bcopy(name, nde->td_name, len); nde->td_name[len] = '\0'; + nde->td_namelenq = -1; + nde->td_hashq = -65; nde->td_node = node; TMPFS_NODE_LOCK(node); @@ -593,6 +617,7 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, * insert the new node into the directory, an operation that * cannot fail. */ tmpfs_dir_attach(dnode, de); + KKASSERT(de->td_hashq == tmpfs_dirhash(de->td_name, de->td_namelen)); TMPFS_NODE_UNLOCK(node); return error; @@ -608,8 +633,17 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, void tmpfs_dir_attach(struct tmpfs_node *dnode, struct tmpfs_dirent *de) { + int i; + TMPFS_NODE_LOCK(dnode); - TAILQ_INSERT_TAIL(&dnode->tn_dir.tn_dirhead, de, td_entries); + + i = tmpfs_dirhash(de->td_name, de->td_namelen); + + TAILQ_INSERT_TAIL(&dnode->tn_dir.tn_hdirhead.th_dirhead, de, td_entries); + TAILQ_INSERT_TAIL(&dnode->tn_dir.tn_hdirhead.th_hash[i], de, td_hash); + de->td_hashq = i; + de->td_hashname = strndup(de->td_name, de->td_namelen); + de->td_namelenq = de->td_namelen; TMPFS_ASSERT_ELOCKED(dnode); dnode->tn_size += sizeof(struct tmpfs_dirent); @@ -628,12 +662,20 @@ tmpfs_dir_attach(struct tmpfs_node *dnode, struct tmpfs_dirent *de) void tmpfs_dir_detach(struct tmpfs_node *dnode, struct tmpfs_dirent *de) { + int i; + TMPFS_NODE_LOCK(dnode); + + i = tmpfs_dirhash(de->td_name, de->td_namelen); + if (dnode->tn_dir.tn_readdir_lastp == de) { dnode->tn_dir.tn_readdir_lastn = 0; dnode->tn_dir.tn_readdir_lastp = NULL; } - TAILQ_REMOVE(&dnode->tn_dir.tn_dirhead, de, td_entries); + TAILQ_REMOVE(&dnode->tn_dir.tn_hdirhead.th_dirhead, de, td_entries); + TAILQ_REMOVE(&dnode->tn_dir.tn_hdirhead.th_hash[i], de, td_hash); + de->td_hashq = -i; + de->td_namelenq = -2; TMPFS_ASSERT_ELOCKED(dnode); dnode->tn_size -= sizeof(struct tmpfs_dirent); @@ -654,16 +696,19 @@ tmpfs_dir_detach(struct tmpfs_node *dnode, struct tmpfs_dirent *de) */ struct tmpfs_dirent * tmpfs_dir_lookup(struct tmpfs_node *node, struct tmpfs_node *f, - struct namecache *ncp) + struct namecache *ncp) { struct tmpfs_dirent *de; int len = ncp->nc_nlen; + int i; TMPFS_VALIDATE_DIR(node); - TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) { - if (f != NULL && de->td_node != f) - continue; + i = tmpfs_dirhash(ncp->nc_name, len); + + TAILQ_FOREACH(de, &node->tn_dir.tn_hdirhead.th_hash[i], td_hash) { + if (f != NULL && de->td_node != f) + continue; if (len == de->td_namelen) { if (!memcmp(ncp->nc_name, de->td_name, len)) break; @@ -760,7 +805,7 @@ tmpfs_dir_getdotdotdent(struct tmpfs_mount *tmp, struct tmpfs_node *node, if (error == 0) { struct tmpfs_dirent *de; - de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); + de = TAILQ_FIRST(&node->tn_dir.tn_hdirhead.th_dirhead); if (de == NULL) uio->uio_offset = TMPFS_DIRCOOKIE_EOF; else @@ -790,7 +835,7 @@ tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie) return node->tn_dir.tn_readdir_lastp; } - TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) { + TAILQ_FOREACH(de, &node->tn_dir.tn_hdirhead.th_dirhead, td_entries) { if (tmpfs_dircookie(de) == cookie) { break; } diff --git a/sys/vfs/tmpfs/tmpfs_vfsops.c b/sys/vfs/tmpfs/tmpfs_vfsops.c index 4aa5eb1..6090b56 100644 --- a/sys/vfs/tmpfs/tmpfs_vfsops.c +++ b/sys/vfs/tmpfs/tmpfs_vfsops.c @@ -351,8 +351,8 @@ tmpfs_unmount(struct mount *mp, int mntflags) if (node->tn_type == VDIR) { struct tmpfs_dirent *de; - while (!TAILQ_EMPTY(&node->tn_dir.tn_dirhead)) { - de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); + while (!TAILQ_EMPTY(&node->tn_dir.tn_hdirhead.th_dirhead)) { + de = TAILQ_FIRST(&node->tn_dir.tn_hdirhead.th_dirhead); tmpfs_dir_detach(node, de); tmpfs_free_dirent(tmp, de); node->tn_size -= sizeof(struct tmpfs_dirent); diff --git a/sys/vfs/tmpfs/tmpfs_vnops.c b/sys/vfs/tmpfs/tmpfs_vnops.c index 990a1ce..4bf5894 100644 --- a/sys/vfs/tmpfs/tmpfs_vnops.c +++ b/sys/vfs/tmpfs/tmpfs_vnops.c @@ -66,6 +66,7 @@ MALLOC_DECLARE(M_TMPFS); static void tmpfs_strategy_done(struct bio *bio); + extern int tmpfs_dirhash(char *, uint16_t); static __inline void tmpfs_knote(struct vnode *vp, int flags) @@ -895,6 +896,7 @@ tmpfs_nlink(struct vop_nlink_args *v) /* Insert the new directory entry into the appropriate directory. */ tmpfs_dir_attach(dnode, de); + KKASSERT(de->td_hashq == tmpfs_dirhash(de->td_name, de->td_namelen)); /* vp link count has changed, so update node times. */ @@ -1039,17 +1041,17 @@ tmpfs_nrename(struct vop_nrename_args *v) */ if (fdnode != tdnode) tmpfs_dir_detach(fdnode, de); + else { + int i = tmpfs_dirhash(de->td_name, de->td_namelen); + TAILQ_REMOVE(&fdnode->tn_dir.tn_hdirhead.th_hash[i], de, + td_hash); + } /* * Handle any name change. Swap with newname, we will * deallocate it at the end. */ if (newname != NULL) { -#if 0 - TMPFS_NODE_LOCK(fnode); - fnode->tn_status |= TMPFS_NODE_CHANGED; - TMPFS_NODE_UNLOCK(fnode); -#endif oldname = de->td_name; de->td_name = newname; de->td_namelen = (uint16_t)tncp->nc_nlen; @@ -1082,9 +1084,13 @@ tmpfs_nrename(struct vop_nrename_args *v) } tmpfs_dir_attach(tdnode, de); } else { + int i = tmpfs_dirhash(de->td_name, de->td_namelen); TMPFS_NODE_LOCK(tdnode); tdnode->tn_status |= TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(tdnode); + TAILQ_INSERT_TAIL(&tdnode->tn_dir.tn_hdirhead.th_hash[i], de, + td_hash); + } /* @@ -1213,6 +1219,27 @@ tmpfs_nrmdir(struct vop_nrmdir_args *v) /* Get the directory entry associated with node (vp). This was * filled by tmpfs_lookup while looking up the entry. */ de = tmpfs_dir_lookup(dnode, node, ncp); + +/* VS */ + if (de == NULL) { + int i = tmpfs_dirhash(ncp->nc_name, ncp->nc_nlen); + struct tmpfs_dirent *tde; + + tde = NULL; + TAILQ_FOREACH(tde, &dnode->tn_dir.tn_hdirhead.th_dirhead, td_entries) { + if (ncp->nc_nlen == tde->td_namelen) { + if (memcmp(ncp->nc_name, tde->td_name, ncp->nc_nlen) == 0) + break; + } + } + + kprintf("warning: lookup mismatch, unexpected! %.*s name in %p dir, %p expnode\n", + ncp->nc_nlen, ncp->nc_name, dnode, node); + kprintf("hash chain %d in dir %p\n", i, dnode); + kprintf("hand-find-de: %p %d hashq hname %s\n", tde, tde->td_hashq, tde->td_hashname); + } +/* !VS */ + KKASSERT(TMPFS_DIRENT_MATCHES(de, ncp->nc_name, ncp->nc_nlen)); @@ -1368,7 +1395,7 @@ outok: off = TMPFS_DIRCOOKIE_DOTDOT; } else { if (off == TMPFS_DIRCOOKIE_DOTDOT) { - de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); + de = TAILQ_FIRST(&node->tn_dir.tn_hdirhead.th_dirhead); } else if (de != NULL) { de = TAILQ_NEXT(de, td_entries); } else {