From 54e3e8084aad34ddcd74aea215d83eb3f641d248 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 14 May 2014 15:30:19 +0200 Subject: [PATCH 16/16] ext2fs: use librbtree for the nodehash Previously, a lookup of a node through nodehash took O(N / INOHSZ). With INOHSZ being a constant (512) this is linear in N. Use a red-black tree instead, which guarantees a lookup performance in O(log N) time. * ext2fs/inode.c (INOHSZ, INOHASH): Remove. (nodehash): Use a red-black tree. (node_cmp_lookup, node_cmp_insert, node_get_np): New functions. (inode_init): Remove. (diskfs_cached_lookup): Adjust accordingly. (ifind, diskfs_try_dropping_softrefs, diskfs_node_iterate): Likewise. * ext2fs/ext2fs.h (struct disknode): Remove list pointers, add red-black tree node. Because we store disknode objects in the tree, we also need a pointer to the node object, and the cache_id for efficient operations on the rb tree. * ext2fs/ext2fs.c (main): Drop inode_init. --- ext2fs/Makefile | 2 +- ext2fs/ext2fs.c | 2 - ext2fs/ext2fs.h | 7 ++-- ext2fs/inode.c | 124 +++++++++++++++++++++++++++++--------------------------- 4 files changed, 70 insertions(+), 65 deletions(-) diff --git a/ext2fs/Makefile b/ext2fs/Makefile index 8d2e68c..b075dc0 100644 --- a/ext2fs/Makefile +++ b/ext2fs/Makefile @@ -23,7 +23,7 @@ target = ext2fs SRCS = balloc.c dir.c ext2fs.c getblk.c hyper.c ialloc.c \ inode.c pager.c pokel.c truncate.c storeinfo.c msg.c xinl.c OBJS = $(SRCS:.c=.o) -HURDLIBS = diskfs pager iohelp fshelp store ports ihash shouldbeinlibc +HURDLIBS = diskfs pager iohelp fshelp store ports ihash rbtree shouldbeinlibc OTHERLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz) include ../Makeconf diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c index 128b6ed..0409dfb 100644 --- a/ext2fs/ext2fs.c +++ b/ext2fs/ext2fs.c @@ -185,8 +185,6 @@ main (int argc, char **argv) map_hypermetadata (); - inode_init (); - /* Set diskfs_root_node to the root inode. */ err = diskfs_cached_lookup (EXT2_ROOT_INO, &diskfs_root_node); if (err) diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h index 3422af2..3505bb7 100644 --- a/ext2fs/ext2fs.h +++ b/ext2fs/ext2fs.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -155,13 +156,13 @@ clear_bit (unsigned num, char *bitmap) /* ext2fs specific per-file data. */ struct disknode { + /* Red-black-tree node for the node cache. */ + struct rbtree_node tree_node; + /* For a directory, this array holds the number of directory entries in each DIRBLKSIZE piece of the directory. */ int *dirents; - /* Links on hash list. */ - struct node *hnext, **hprevp; - /* Lock to lock while fiddling with this inode's block allocation info. */ pthread_rwlock_t alloc_lock; diff --git a/ext2fs/inode.c b/ext2fs/inode.c index 7ec343f..0142bf6 100644 --- a/ext2fs/inode.c +++ b/ext2fs/inode.c @@ -20,6 +20,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ext2fs.h" +#include #include #include #include @@ -39,13 +40,6 @@ #define UF_IMMUTABLE 0 #endif -#define INOHSZ 512 -#if ((INOHSZ&(INOHSZ-1)) == 0) -#define INOHASH(ino) ((ino)&(INOHSZ-1)) -#else -#define INOHASH(ino) (((unsigned)(ino))%INOHSZ) -#endif - /* The nodehash is a cache of nodes. Access to nodehash and nodehash_nr_items is protected by @@ -55,23 +49,45 @@ asked to give up that light reference, we reacquire our lock momentarily to check whether someone else reacquired a reference through the nodehash. */ -static struct node *nodehash[INOHSZ]; +static struct rbtree nodehash = RBTREE_INITIALIZER; static size_t nodehash_nr_items; static pthread_mutex_t nodehash_lock = PTHREAD_MUTEX_INITIALIZER; -static error_t read_node (struct node *np); +static inline struct node * +node_get_np (const struct rbtree_node *node) +{ + struct disknode *dn; -pthread_spinlock_t generation_lock = PTHREAD_SPINLOCK_INITIALIZER; + dn = rbtree_entry (node, struct disknode, tree_node); -/* Initialize the inode hash table. */ -void -inode_init () + return diskfs_disknode_node (dn); +} + +static inline int +node_cmp_lookup (const ino_t inum, + const struct rbtree_node *node) { - int n; - for (n = 0; n < INOHSZ; n++) - nodehash[n] = 0; + struct node *np = node_get_np (node); + + if (inum == np->cache_id) + return 0; + else if (inum < np->cache_id) + return -1; + else + return 1; } +static inline int +node_cmp_insert (const struct rbtree_node *a, + const struct rbtree_node *b) +{ + return node_cmp_lookup (node_get_np (a)->cache_id, b); +} + +static error_t read_node (struct node *np); + +pthread_spinlock_t generation_lock = PTHREAD_SPINLOCK_INITIALIZER; + /* Fetch inode INUM, set *NPP to the node structure; gain one user reference and lock the node. */ error_t @@ -79,47 +95,42 @@ diskfs_cached_lookup (ino_t inum, struct node **npp) { error_t err; struct node *np; + struct rbtree_node *node; + unsigned long slot; struct disknode *dn; pthread_mutex_lock (&nodehash_lock); - for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext) - if (np->cache_id == inum) - { - diskfs_nref (np); - pthread_mutex_unlock (&nodehash_lock); - pthread_mutex_lock (&np->lock); - *npp = np; - return 0; - } - - /* Format specific data for the new node. */ - dn = malloc (sizeof (struct disknode)); - if (! dn) + node = rbtree_lookup_slot (&nodehash, inum, node_cmp_lookup, slot); + if (node != NULL) + { + np = node_get_np (node); + diskfs_nref (np); + pthread_mutex_unlock (&nodehash_lock); + pthread_mutex_lock (&np->lock); + *npp = np; + return 0; + } + + /* Create the new node. */ + np = diskfs_make_node_alloc (sizeof *dn); + if (np == NULL) { pthread_mutex_unlock (&nodehash_lock); return ENOMEM; } + np->cache_id = inum; + dn = diskfs_node_disknode (np); dn->dirents = 0; dn->dir_idx = 0; dn->pager = 0; pthread_rwlock_init (&dn->alloc_lock, NULL); pokel_init (&dn->indir_pokel, diskfs_disk_pager, disk_cache); - - /* Create the new node. */ - np = diskfs_make_node (dn); - np->cache_id = inum; - pthread_mutex_lock (&np->lock); /* Put NP in NODEHASH. */ - dn->hnext = nodehash[INOHASH(inum)]; - if (dn->hnext) - dn->hnext->dn->hprevp = &dn->hnext; - dn->hprevp = &nodehash[INOHASH(inum)]; - nodehash[INOHASH(inum)] = np; diskfs_nref_light (np); + rbtree_insert_slot (&nodehash, slot, &dn->tree_node); nodehash_nr_items += 1; - pthread_mutex_unlock (&nodehash_lock); /* Get the contents of NP off disk. */ @@ -149,16 +160,14 @@ diskfs_cached_lookup (ino_t inum, struct node **npp) struct node * ifind (ino_t inum) { - struct node *np; + struct rbtree_node *node; pthread_mutex_lock (&nodehash_lock); - for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext) + node = rbtree_lookup (&nodehash, inum, node_cmp_lookup); + if (node != NULL) { - if (np->cache_id != inum) - continue; - pthread_mutex_unlock (&nodehash_lock); - return np; + return node_get_np (node); } assert (0); } @@ -176,7 +185,6 @@ diskfs_node_norefs (struct node *np) pokel_inherit (&global_pokel, &np->dn->indir_pokel); pokel_finalize (&np->dn->indir_pokel); - free (np->dn); free (np); } @@ -186,7 +194,7 @@ void diskfs_try_dropping_softrefs (struct node *np) { pthread_mutex_lock (&nodehash_lock); - if (np->dn->hnext != NULL) + if (np->cache_id != 0) { /* Check if someone reacquired a reference through the nodehash. */ @@ -200,11 +208,9 @@ diskfs_try_dropping_softrefs (struct node *np) return; } - *np->dn->hprevp = np->dn->hnext; - if (np->dn->hnext) - np->dn->hnext->dn->hprevp = np->dn->hprevp; - np->dn->hnext = NULL; + rbtree_remove (&nodehash, &(diskfs_node_disknode (np))->tree_node); nodehash_nr_items -= 1; + np->cache_id = 0; diskfs_nrele_light (np); } pthread_mutex_unlock (&nodehash_lock); @@ -581,7 +587,7 @@ error_t diskfs_node_iterate (error_t (*fun)(struct node *)) { error_t err = 0; - int n; + struct rbtree_node *n; size_t num_nodes; struct node *node, **node_list, **p; @@ -605,12 +611,12 @@ diskfs_node_iterate (error_t (*fun)(struct node *)) } p = node_list; - for (n = 0; n < INOHSZ; n++) - for (node = nodehash[n]; node; node = node->dn->hnext) - { - *p++ = node; - diskfs_nref (node); - } + for (n = rbtree_first (&nodehash); n; n = rbtree_next (n)) + { + node = node_get_np (n); + *p++ = node; + diskfs_nref (node); + } pthread_mutex_unlock (&nodehash_lock); -- 2.0.0.rc2