diff options
Diffstat (limited to 'libdiskfs')
-rw-r--r-- | libdiskfs/name-cache.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/libdiskfs/name-cache.c b/libdiskfs/name-cache.c new file mode 100644 index 00000000..a07e0464 --- /dev/null +++ b/libdiskfs/name-cache.c @@ -0,0 +1,166 @@ +/* Directory name lookup caching + Copyright (C) 1996 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + + +#include "priv.h" +#include "name-cache.h" + +/* Maximum number of names to cache at once */ +#define MAXCACHE 256 + +/* Current number of names cached */ +static int cache_cur; + +/* Cache entry */ +struct lookup_cache +{ + struct lookup_cache *next, *prev; + + /* These do not hold references; special code exists to call + purge routines when the nodes lose refs. */ + struct node *dp; /* directory */ + struct node *np; /* node */ + + /* strlen of name member */ + size_t namelen; + char name[0]; +}; + +/* Cache itself */ +static struct lookup_cache *lookup_cache_head, *lookup_cache_tail; + +/* Node NP has just been found in DIR with NAME. If NP is null, that + means that this name has been confirmed as absent in the directory. */ +void +diskfs_enter_cache (struct node *dir, struct node *np, char *name) +{ + struct lookup_cache *lc, *tmp = 0; + size_t len = strlen (name) + 1; + + + lc = malloc (sizeof (struct lookup_cache) + len); + lc->dp = dir; + lc->np = np; + lc->namelen = len - 1; + bcopy (name, lc->name, len); + + spin_lock (&diskfs_node_refcnt_lock); + if (cache_cur == MAXCACHE) + { + /* Free the entry at the tail */ + tmp = lookup_cache_tail; + lookup_cache_tail = lookup_cache_tail->prev; + if (lookup_cache_tail) + lookup_cache_tail->next = 0; + else + lookup_cache_head = 0; + } + else + cache_cur++; + + /* Add the new entry at the front */ + lc->next = lookup_cache_head; + lc->prev = 0; + lc->next->prev = lc; + lookup_cache_head = lc; + if (!lc->next) + lookup_cache_tail = lc; + + spin_unlock (&diskfs_node_refcnt_lock); + + if (tmp) + free (tmp); +} + +/* Purge all references in the cache to NP as a node inside a directory */ +void +diskfs_purge_cache_node (struct node *np) +{ + struct lookup_cache *lc, *nxt; + + spin_lock (&diskfs_node_refcnt_lock); + for (lc = lookup_cache_head; lc; lc = nxt) + if (lc->np == np) + { + if (lc->prev) + lc->prev->next = lc->next; + if (lc->next) + lc->next->prev = lc->prev; + nxt = lc->next; + free (lc); + } + else + nxt = lc->next; + spin_unlock (&diskfs_node_refcnt_lock); +} + +/* Purge all references in the cache to NP, either as a node or as a + directory. diskfs_node_refcnt_lock must be held around this call. */ +void +_diskfs_purge_cache (struct node *np) +{ + struct lookup_cache *lc, *nxt; + + for (lc = lookup_cache_head; lc; lc = nxt) + if (lc->np == np || lc->dp == np) + { + if (lc->prev) + lc->prev->next = lc->next; + if (lc->next) + lc->next->prev = lc->prev; + nxt = lc->next; + free (lc); + } + else + nxt = lc->next; +} + +/* Scan the cache looking for NAME inside DIR. If we don't know + anything entry at all, then return 0. If the entry is confirmed to + not exist, then return -1. Otherwise, return NP for the entry, with + a newly allocated reference. */ +struct node * +diskfs_check_cache (struct node *dir, char *name) +{ + size_t len = strlen (name); + + spin_lock (&diskfs_node_refcnt_lock); + for (lc = lookup_cache_head; lc; lc = lc->next) + if (lc->dp == dir + && lc->namelen == len + && lc->name[0] == name[0] + && !strcmp (lc->name, name)) + { + if (lc->np) + { + rp->refereces++; + spin_unlock (&diskfs_node_refcnt_lock); + return np; + } + else + { + spin_unlock (&diskfs_node_refcnt_lock); + return (struct node *) -1; + } + } + spin_unlock (&diskfs_node_refcnt_lock); + return 0; +} + |