From 9090a73b917b9fcefae889a9d6d88283555100b4 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 20/20] ext2fs: use librdxtree 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 radix tree instead, which gives us a lookup in time linear with the size of keys. * ext2fs/inode.c (INOHSZ, INOHASH): Remove. (nodehash): Use a radix tree. (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. * ext2fs/ext2fs.c (main): Drop inode_init. --- ext2fs/Makefile | 2 +- ext2fs/ext2fs.c | 2 - ext2fs/ext2fs.h | 4 +- ext2fs/inode.c | 121 +++++++++++++++++++++----------------------------------- 4 files changed, 48 insertions(+), 81 deletions(-) diff --git a/ext2fs/Makefile b/ext2fs/Makefile index 8d2e68c..f93d696 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 rdxtree 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..8eaa846 100644 --- a/ext2fs/ext2fs.h +++ b/ext2fs/ext2fs.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -159,9 +160,6 @@ struct disknode 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 62bbebe..2e829ac 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,14 @@ 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 rdxtree nodecache = RDXTREE_INITIALIZER; static size_t nodehash_nr_items; -static pthread_mutex_t nodehash_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t nodecache_lock = PTHREAD_RWLOCK_INITIALIZER; static error_t read_node (struct node *np); pthread_spinlock_t generation_lock = PTHREAD_SPINLOCK_INITIALIZER; -/* Initialize the inode hash table. */ -void -inode_init () -{ - int n; - for (n = 0; n < INOHSZ; n++) - nodehash[n] = 0; -} - /* Fetch inode INUM, set *NPP to the node structure; gain one user reference and lock the node. */ error_t @@ -81,46 +66,40 @@ diskfs_cached_lookup (ino_t inum, struct node **npp) struct node *np; 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) + pthread_rwlock_rdlock (&nodecache_lock); + np = rdxtree_lookup (&nodecache, inum); + if (np != NULL) + { + diskfs_nref (np); + pthread_rwlock_unlock (&nodecache_lock); + pthread_mutex_lock (&np->lock); + *npp = np; + return 0; + } + pthread_rwlock_unlock (&nodecache_lock); + + /* Create the new node. */ + np = diskfs_make_node_alloc (sizeof *dn); + if (np == NULL) { - pthread_mutex_unlock (&nodehash_lock); + pthread_mutex_unlock (&nodecache_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); + pthread_rwlock_wrlock (&nodecache_lock); + rdxtree_insert (&nodecache, inum, np); nodehash_nr_items += 1; - - pthread_mutex_unlock (&nodehash_lock); + pthread_rwlock_unlock (&nodecache_lock); /* Get the contents of NP off disk. */ err = read_node (np); @@ -151,16 +130,12 @@ ifind (ino_t inum) { struct node *np; - pthread_mutex_lock (&nodehash_lock); - for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext) - { - if (np->cache_id != inum) - continue; + pthread_rwlock_rdlock (&nodecache_lock); + np = rdxtree_lookup (&nodecache, inum); + pthread_rwlock_unlock (&nodecache_lock); - pthread_mutex_unlock (&nodehash_lock); - return np; - } - assert (0); + assert (np); + return np; } /* The last reference to a node has gone away; drop @@ -176,7 +151,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); } @@ -185,8 +159,8 @@ diskfs_node_norefs (struct node *np) void diskfs_try_dropping_softrefs (struct node *np) { - pthread_mutex_lock (&nodehash_lock); - if (np->dn->hnext != NULL) + pthread_rwlock_wrlock (&nodecache_lock); + if (np->cache_id != 0) { /* Check if someone reacquired a reference through the nodehash. */ @@ -196,18 +170,16 @@ diskfs_try_dropping_softrefs (struct node *np) { /* A reference was reacquired through a hash table lookup. It's fine, we didn't touch anything yet. */ - pthread_mutex_unlock (&nodehash_lock); + pthread_rwlock_unlock (&nodecache_lock); return; } - *np->dn->hprevp = np->dn->hnext; - if (np->dn->hnext) - np->dn->hnext->dn->hprevp = np->dn->hprevp; - np->dn->hnext = NULL; + rdxtree_remove (&nodecache, np->cache_id); nodehash_nr_items -= 1; + np->cache_id = 0; diskfs_nrele_light (np); } - pthread_mutex_unlock (&nodehash_lock); + pthread_rwlock_unlock (&nodecache_lock); drop_pager_softrefs (np); } @@ -581,16 +553,16 @@ error_t diskfs_node_iterate (error_t (*fun)(struct node *)) { error_t err = 0; - int n; + struct rdxtree_iter iter; size_t num_nodes; struct node *node, **node_list, **p; - pthread_mutex_lock (&nodehash_lock); + pthread_rwlock_rdlock (&nodecache_lock); /* We must copy everything from the hash table into another data structure to avoid running into any problems with the hash-table being modified during processing (normally we delegate access to hash-table with - nodehash_lock, but we can't hold this while locking the + nodecache_lock, but we can't hold this while locking the individual node locks). */ num_nodes = nodehash_nr_items; @@ -599,20 +571,19 @@ diskfs_node_iterate (error_t (*fun)(struct node *)) node_list = malloc (num_nodes * sizeof (struct node *)); if (node_list == NULL) { - pthread_mutex_unlock (&nodehash_lock); + pthread_rwlock_unlock (&nodecache_lock); ext2_debug ("unable to allocate temporary node table"); return ENOMEM; } p = node_list; - for (n = 0; n < INOHSZ; n++) - for (node = nodehash[n]; node; node = node->dn->hnext) - { - *p++ = node; - diskfs_nref_light (node); - } - - pthread_mutex_unlock (&nodehash_lock); + rdxtree_for_each (&nodecache, &iter, node) + { + *p++ = node; + diskfs_nref_light (node); + } + + pthread_rwlock_unlock (&nodecache_lock); p = node_list; while (num_nodes-- > 0) -- 2.0.0.rc2