From 2bb8bfa49d35ab2f7b33587e59c4a9c635642ea0 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 15 Apr 2015 13:17:06 +0200 Subject: [PATCH hurd 10/16] libdiskfs: implement a node cache xxx Move the node cache from ext2fs into libdiskfs. * ext2fs/dir.c * ext2fs/ext2fs.c * ext2fs/ext2fs.h * ext2fs/inode.c * libdiskfs/Makefile * libdiskfs/diskfs.h * libdiskfs/node-cache.c --- ext2fs/dir.c | 4 +- ext2fs/ext2fs.c | 2 - ext2fs/ext2fs.h | 9 -- ext2fs/inode.c | 228 ++++---------------------------------------- libdiskfs/Makefile | 2 +- libdiskfs/diskfs.h | 17 ++++ libdiskfs/node-cache.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 289 insertions(+), 222 deletions(-) create mode 100644 libdiskfs/node-cache.c diff --git a/ext2fs/dir.c b/ext2fs/dir.c index 2dfe1d7..6cdfba2 100644 --- a/ext2fs/dir.c +++ b/ext2fs/dir.c @@ -306,7 +306,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type, /* Here below are the spec dotdot cases. */ else if (type == RENAME || type == REMOVE) - np = ifind (inum); + np = diskfs_cached_ifind (inum); else if (type == LOOKUP) { @@ -359,7 +359,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type, diskfs_nput (np); } else if (type == RENAME || type == REMOVE) - /* We just did ifind to get np; that allocates + /* We just did diskfs_cached_ifind to get np; that allocates no new references, so we don't have anything to do */ ; else if (type == LOOKUP) diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c index beb7cad..d0fdfe7 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 9667b6f..96d8e9d 100644 --- a/ext2fs/ext2fs.h +++ b/ext2fs/ext2fs.h @@ -159,9 +159,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; @@ -419,12 +416,6 @@ dino_deref (struct ext2_inode *inode) /* Write all active disknodes into the inode pager. */ void write_all_disknodes (); - -/* Lookup node INUM (which must have a reference already) and return it - without allocating any new references. */ -struct node *ifind (ino_t inum); - -void inode_init (void); /* ---------------------------------------------------------------- */ diff --git a/ext2fs/inode.c b/ext2fs/inode.c index 7af617c..22a0c8e 100644 --- a/ext2fs/inode.c +++ b/ext2fs/inode.c @@ -39,144 +39,34 @@ #define UF_IMMUTABLE 0 #endif -#define INOHSZ 8192 -#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 - nodecache_lock. - - Every node in the nodehash carries a light reference. When we are - 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 size_t nodehash_nr_items; -static pthread_rwlock_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; -} - -/* Lookup node with inode number INUM. Returns NULL if the node is - not found in the node cache. */ -static struct node * -lookup (ino_t inum) -{ - struct node *np; - for (np = nodehash[INOHASH(inum)]; np; np = diskfs_node_disknode (np)->hnext) - if (np->cache_id == inum) - return np; - return NULL; -} - -/* Fetch inode INUM, set *NPP to the node structure; - gain one user reference and lock the node. */ +/* XXX */ error_t -diskfs_cached_lookup (ino_t inum, struct node **npp) +diskfs_user_make_node (struct node **npp) { - error_t err; - struct node *np, *tmp; + struct node *np; struct disknode *dn; - pthread_rwlock_rdlock (&nodecache_lock); - np = lookup (inum); - if (np) - goto gotit; - pthread_rwlock_unlock (&nodecache_lock); - /* Create the new node. */ np = diskfs_make_node_alloc (sizeof *dn); - dn = diskfs_node_disknode (np); - np->cache_id = inum; + if (np == NULL) + return ENOMEM; /* Format specific data for the new node. */ + 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); - pthread_mutex_lock (&np->lock); - - /* Put NP in NODEHASH. */ - pthread_rwlock_wrlock (&nodecache_lock); - tmp = lookup (inum); - if (tmp) - { - /* We lost a race. */ - diskfs_nput (np); - np = tmp; - goto gotit; - } - - dn->hnext = nodehash[INOHASH(inum)]; - if (dn->hnext) - diskfs_node_disknode (dn->hnext)->hprevp = &dn->hnext; - dn->hprevp = &nodehash[INOHASH(inum)]; - nodehash[INOHASH(inum)] = np; - diskfs_nref_light (np); - nodehash_nr_items += 1; - pthread_rwlock_unlock (&nodecache_lock); - - /* Get the contents of NP off disk. */ - err = read_node (np); - - if (!diskfs_check_readonly () && !np->dn_stat.st_gen) - { - pthread_spin_lock (&generation_lock); - if (++next_generation < diskfs_mtime->seconds) - next_generation = diskfs_mtime->seconds; - np->dn_stat.st_gen = next_generation; - pthread_spin_unlock (&generation_lock); - np->dn_set_ctime = 1; - } - - if (err) - return err; - else - { - *npp = np; - return 0; - } - - gotit: - diskfs_nref (np); - pthread_rwlock_unlock (&nodecache_lock); - pthread_mutex_lock (&np->lock); *npp = np; return 0; } -/* Lookup node INUM (which must have a reference already) and return it - without allocating any new references. */ -struct node * -ifind (ino_t inum) -{ - struct node *np; - - pthread_rwlock_rdlock (&nodecache_lock); - np = lookup (inum); - pthread_rwlock_unlock (&nodecache_lock); - - assert (np); - return np; -} - /* The last reference to a node has gone away; drop it from the hash table and clean all state in the dn structure. */ void @@ -196,35 +86,8 @@ diskfs_node_norefs (struct node *np) /* The last hard reference to a node has gone away; arrange to have all the weak references dropped that can be. */ void -diskfs_try_dropping_softrefs (struct node *np) +diskfs_user_try_dropping_softrefs (struct node *np) { - pthread_rwlock_wrlock (&nodecache_lock); - if (diskfs_node_disknode (np)->hprevp != NULL) - { - /* Check if someone reacquired a reference through the - nodehash. */ - struct references result; - refcounts_references (&np->refcounts, &result); - - if (result.hard > 0) - { - /* A reference was reacquired through a hash table lookup. - It's fine, we didn't touch anything yet. */ - pthread_rwlock_unlock (&nodecache_lock); - return; - } - - *diskfs_node_disknode (np)->hprevp = diskfs_node_disknode (np)->hnext; - if (diskfs_node_disknode (np)->hnext) - diskfs_node_disknode (diskfs_node_disknode (np)->hnext)->hprevp = - diskfs_node_disknode (np)->hprevp; - diskfs_node_disknode (np)->hnext = NULL; - diskfs_node_disknode (np)->hprevp = NULL; - nodehash_nr_items -= 1; - diskfs_nrele_light (np); - } - pthread_rwlock_unlock (&nodecache_lock); - drop_pager_softrefs (np); } @@ -243,8 +106,8 @@ diskfs_new_hardrefs (struct node *np) } /* Read stat information out of the ext2_inode. */ -static error_t -read_node (struct node *np) +error_t +diskfs_user_read_node (struct node *np) { error_t err; struct stat *st = &np->dn_stat; @@ -384,6 +247,16 @@ read_node (struct node *np) linux, some devices). */ np->allocsize = 0; + if (!diskfs_check_readonly () && !np->dn_stat.st_gen) + { + pthread_spin_lock (&generation_lock); + if (++next_generation < diskfs_mtime->seconds) + next_generation = diskfs_mtime->seconds; + np->dn_stat.st_gen = next_generation; + pthread_spin_unlock (&generation_lock); + np->dn_set_ctime = 1; + } + return 0; } @@ -585,72 +458,11 @@ diskfs_node_reload (struct node *node) } pokel_flush (&dn->indir_pokel); flush_node_pager (node); - read_node (node); + diskfs_user_read_node (node); return 0; } -/* For each active node, call FUN. The node is to be locked around the call - to FUN. If FUN returns non-zero for any node, then immediately stop, and - return that value. */ -error_t -diskfs_node_iterate (error_t (*fun)(struct node *)) -{ - error_t err = 0; - int n; - size_t num_nodes; - struct node *node, **node_list, **p; - - 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 - nodecache_lock, but we can't hold this while locking the - individual node locks). */ - num_nodes = nodehash_nr_items; - - /* TODO This method doesn't scale beyond a few dozen nodes and should be - replaced. */ - node_list = malloc (num_nodes * sizeof (struct node *)); - if (node_list == NULL) - { - 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 = diskfs_node_disknode (node)->hnext) - { - *p++ = node; - - /* We acquire a hard reference for node, but without using - diskfs_nref. We do this so that diskfs_new_hardrefs will not - get called. */ - refcounts_ref (&node->refcounts, NULL); - } - - pthread_rwlock_unlock (&nodecache_lock); - - p = node_list; - while (num_nodes-- > 0) - { - node = *p++; - if (!err) - { - pthread_mutex_lock (&node->lock); - err = (*fun)(node); - pthread_mutex_unlock (&node->lock); - } - diskfs_nrele (node); - } - - free (node_list); - return err; -} - /* Write all active disknodes into the ext2_inode pager. */ void write_all_disknodes () diff --git a/libdiskfs/Makefile b/libdiskfs/Makefile index 996e86a..47b9339 100644 --- a/libdiskfs/Makefile +++ b/libdiskfs/Makefile @@ -41,7 +41,7 @@ OTHERSRCS = conch-fetch.c conch-set.c dir-clear.c dir-init.c dir-renamed.c \ extern-inline.c \ node-create.c node-drop.c node-make.c node-rdwr.c node-update.c \ node-nref.c node-nput.c node-nrele.c node-nrefl.c node-nputl.c \ - node-nrelel.c \ + node-nrelel.c node-cache.c \ peropen-make.c peropen-rele.c protid-make.c protid-rele.c \ init-init.c init-startup.c init-first.c init-main.c \ rdwr-internal.c boot-start.c demuxer.c node-times.c shutdown.c \ diff --git a/libdiskfs/diskfs.h b/libdiskfs/diskfs.h index 7a21dff..22c4aa6 100644 --- a/libdiskfs/diskfs.h +++ b/libdiskfs/diskfs.h @@ -80,6 +80,9 @@ struct peropen filesystem. */ struct node { + /* Links on hash list. */ + struct node *hnext, **hprevp; + struct disknode *dn; io_statbuf_t dn_stat; @@ -1102,4 +1105,18 @@ struct store *diskfs_init_main (struct argp *startup_argp, /* Make errors go somewhere reasonable. */ void diskfs_console_stdio (); +//XXX +/* XXX */ +error_t diskfs_user_make_node (struct node **npp); +/* Read stat information out of the ext2_inode. */ +error_t diskfs_user_read_node (struct node *np); +/* The last hard reference to a node has gone away; arrange to have + all the weak references dropped that can be. */ +void diskfs_user_try_dropping_softrefs (struct node *np); + +/* Lookup node INUM (which must have a reference already) and return it + without allocating any new references. */ +struct node *diskfs_cached_ifind (ino_t inum); + + #endif /* hurd/diskfs.h */ diff --git a/libdiskfs/node-cache.c b/libdiskfs/node-cache.c new file mode 100644 index 0000000..58249bf --- /dev/null +++ b/libdiskfs/node-cache.c @@ -0,0 +1,249 @@ +/* Inode cache. + + Copyright (C) 1994-2015 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see . */ + +#include "priv.h" + +#define INOHSZ 8192 +#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 + nodecache_lock. + + Every node in the nodehash carries a light reference. When we are + 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 size_t nodehash_nr_items; +static pthread_rwlock_t nodecache_lock = PTHREAD_RWLOCK_INITIALIZER; + +/* Initialize the inode hash table. */ +static void __attribute__ ((constructor)) +nodecache_init () +{ +} + +/* Lookup node with inode number INUM. Returns NULL if the node is + not found in the node cache. */ +static struct node * +lookup (ino_t inum) +{ + struct node *np; + for (np = nodehash[INOHASH(inum)]; np; np = np->hnext) + if (np->cache_id == inum) + return np; + return NULL; +} + +/* Fetch inode INUM, set *NPP to the node structure; + gain one user reference and lock the node. */ +error_t __attribute__ ((weak)) +diskfs_cached_lookup (ino_t inum, struct node **npp) +{ + error_t err; + struct node *np, *tmp; + + pthread_rwlock_rdlock (&nodecache_lock); + np = lookup (inum); + if (np) + goto gotit; + pthread_rwlock_unlock (&nodecache_lock); + + err = diskfs_user_make_node (&np); + if (err) + return err; + + np->cache_id = inum; + pthread_mutex_lock (&np->lock); + + /* Put NP in NODEHASH. */ + pthread_rwlock_wrlock (&nodecache_lock); + tmp = lookup (inum); + if (tmp) + { + /* We lost a race. */ + diskfs_nput (np); + np = tmp; + goto gotit; + } + + np->hnext = nodehash[INOHASH(inum)]; + if (np->hnext) + np->hnext->hprevp = &np->hnext; + np->hprevp = &nodehash[INOHASH(inum)]; + nodehash[INOHASH(inum)] = np; + diskfs_nref_light (np); + nodehash_nr_items += 1; + pthread_rwlock_unlock (&nodecache_lock); + + /* Get the contents of NP off disk. */ + err = diskfs_user_read_node (np); + if (err) + return err; + else + { + *npp = np; + return 0; + } + + gotit: + diskfs_nref (np); + pthread_rwlock_unlock (&nodecache_lock); + pthread_mutex_lock (&np->lock); + *npp = np; + return 0; +} + +/* Lookup node INUM (which must have a reference already) and return it + without allocating any new references. */ +struct node * +diskfs_cached_ifind (ino_t inum) +{ + struct node *np; + + pthread_rwlock_rdlock (&nodecache_lock); + np = lookup (inum); + pthread_rwlock_unlock (&nodecache_lock); + + assert (np); + return np; +} + +void __attribute__ ((weak)) +diskfs_try_dropping_softrefs (struct node *np) +{ + pthread_rwlock_wrlock (&nodecache_lock); + if (np->hprevp != NULL) + { + /* Check if someone reacquired a reference through the + nodehash. */ + struct references result; + refcounts_references (&np->refcounts, &result); + + if (result.hard > 0) + { + /* A reference was reacquired through a hash table lookup. + It's fine, we didn't touch anything yet. */ + pthread_rwlock_unlock (&nodecache_lock); + return; + } + + *np->hprevp = np->hnext; + if (np->hnext) + np->hnext->hprevp = np->hprevp; + np->hnext = NULL; + np->hprevp = NULL; + nodehash_nr_items -= 1; + diskfs_nrele_light (np); + } + pthread_rwlock_unlock (&nodecache_lock); + + diskfs_user_try_dropping_softrefs (np); +} + +/* For each active node, call FUN. The node is to be locked around the call + to FUN. If FUN returns non-zero for any node, then immediately stop, and + return that value. */ +error_t __attribute__ ((weak)) +diskfs_node_iterate (error_t (*fun)(struct node *)) +{ + error_t err = 0; + int n; + size_t num_nodes; + struct node *node, **node_list, **p; + + 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 + nodecache_lock, but we can't hold this while locking the + individual node locks). */ + /* XXX: Can we? */ + num_nodes = nodehash_nr_items; + + /* TODO This method doesn't scale beyond a few dozen nodes and should be + replaced. */ + node_list = malloc (num_nodes * sizeof (struct node *)); + if (node_list == NULL) + { + pthread_rwlock_unlock (&nodecache_lock); + error (0, 0, "unable to allocate temporary node table"); + return ENOMEM; + } + + p = node_list; + for (n = 0; n < INOHSZ; n++) + for (node = nodehash[n]; node; node = node->hnext) + { + *p++ = node; + + /* We acquire a hard reference for node, but without using + diskfs_nref. We do this so that diskfs_new_hardrefs will not + get called. */ + refcounts_ref (&node->refcounts, NULL); + } + + pthread_rwlock_unlock (&nodecache_lock); + + p = node_list; + while (num_nodes-- > 0) + { + node = *p++; + if (!err) + { + pthread_mutex_lock (&node->lock); + err = (*fun)(node); + pthread_mutex_unlock (&node->lock); + } + diskfs_nrele (node); + } + + free (node_list); + return err; +} + +/* XXX */ +error_t __attribute__ ((weak)) +diskfs_user_make_node (struct node **npp) +{ + assert (! "diskfs_user_make_node not implemented"); +} + +/* Read stat information out of the ext2_inode. */ +error_t __attribute__ ((weak)) +diskfs_user_read_node (struct node *np) +{ + assert (! "diskfs_user_read_node not implemented"); +} + +/* The last hard reference to a node has gone away; arrange to have + all the weak references dropped that can be. */ +void __attribute__ ((weak)) +diskfs_user_try_dropping_softrefs (struct node *np) +{ + assert (! "diskfs_user_try_dropping_softrefs not implemented"); +} + -- 2.1.4