/*
- $Id: dircache.c,v 1.1.2.7 2010-02-11 13:06:54 franklahm Exp $
Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include "globals.h"
/*
- * Dircache and indexes
- * ====================
+ * Directory Cache
+ * ===============
+ *
+ * Cache files and directories in a LRU cache.
+ *
+ * The directory cache caches directories and files(!). The main reason for having the cache
+ * is to avoid recursively walking up the CNID patch, querying the CNID database each time, when
+ * we have to calculate the location of eg directory with CNID 30, which is located in a dir with
+ * CNID 25, next CNID 20 and then CNID 2 (the volume root as per AFP spec).
+ * If all these dirs where in the cache, each database look up can be avoided. Additionally there's
+ * the element "fullpath" in struct dir, which is used to avoid the recursion in any case. Wheneveer
+ * a struct dir is initialized, the fullpath to the directory is stored there.
+ *
+ * In order to speed up the CNID query for files too, which eg happens when a directory is enumerated,
+ * files are stored too in the dircache. In order to differentiate between files and dirs, we re-use
+ * the element fullpath, which for files is always NULL.
+ *
+ * The most frequent codepatch that leads to caching is directory enumeration (cf enumerate.c):
+ * - if a element is a directory:
+ * (1) the cache is searched by dircache_search_by_name()
+ * (2) if it wasn't found a new struct dir is created and cached both from within dir_add()
+ * - for files the caching happens a little bit down the call chain:
+ * (3) first getfilparams() is called, which calls
+ * (4) getmetadata() where the cache is searched with dircache_search_by_name()
+ * (5) if the element is not found
+ * (6) get_id() queries the CNID from the database
+ * (7) then a struct dir is initialized via dir_new() (note the fullpath arg is NULL)
+ * (8) finally added to the cache with dircache_add()
+ * (2) of course does contain the steps 6,7 and 8.
+ *
+ * The dircache is a LRU cache, whenever it fills up we call dircache_evict internally which removes
+ * DIRCACHE_FREE_QUANTUM elements from the cache.
+ *
+ * There is only one cache for all volumes, so of course we use the volume is in hashing calculations.
+ *
+ * Indexes
+ * =======
+ *
* The maximum dircache size is:
* max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
* It is a hashtable which we use to store "struct dir"s in. If the cache get full, oldest
* entries are evicted in chunks of DIRCACHE_FREE.
+ *
* We have/need two indexes:
* - a DID/name index on the main dircache, another hashtable
* - a queue index on the dircache, for evicting the oldest entries
* The cache supports locking of struct dir elements through the DIRF_CACHELOCK flag. A dir
* locked this way wont ever be removed from the cache, so be careful.
*
- * Sending SIGHUP to a afpd child causes it to dump the dircache to a file
- * "/tmp/dircache.PID".
+ * Debugging
+ * =========
+ *
+ * Sending SIGHUP to a afpd child causes it to dump the dircache to a file "/tmp/dircache.PID".
*/
/********************************************************
********************************************************/
/*****************************
- * THE dircache */
+ * the dircache */
static hash_t *dircache; /* The actual cache */
static unsigned int dircache_maxsize; /* cache maximum size */
static q_t *index_queue; /* the index itself */
static unsigned int queue_count;
-static const int dircache_free_quantum = 256; /* number of entries to free */
/*!
* @brief Remove a fixed number of (oldest) entries from the cache and indexes
*/
static void dircache_evict(void)
{
- int i = dircache_free_quantum;
+ int i = DIRCACHE_FREE_QUANTUM;
struct dir *dir;
LOG(log_debug, logtype_afpd, "dircache: {starting cache eviction}");
while (i--) {
if ((dir = (struct dir *)dequeue(index_queue)) == NULL) { /* 1 */
dircache_dump();
- exit(EXITERR_SYS);
+ AFP_PANIC("dircache_evict");
}
queue_count--;
|| (dir->d_flags & DIRF_CACHELOCK)) { /* 2 */
if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
dircache_dump();
- exit(EXITERR_SYS);
+ AFP_PANIC("dircache_evict");
}
queue_count++;
continue;
LOG(log_error, logtype_default, "dircache_remove(%u,%s): not in didname index",
ntohl(dir->d_did), dir->d_u_name);
dircache_dump();
- exit(EXITERR_SYS);
+ AFP_PANIC("dircache_remove");
}
- hash_delete(index_didname, hn);
+ hash_delete_free(index_didname, hn);
}
if (flags & DIRCACHE) {
LOG(log_error, logtype_default, "dircache_remove(%u,%s): not in dircache",
ntohl(dir->d_did), dir->d_u_name);
dircache_dump();
- exit(EXITERR_SYS);
+ AFP_PANIC("dircache_remove");
}
- hash_delete(dircache, hn);
+ hash_delete_free(dircache, hn);
}
LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {removed}", ntohl(dir->d_did), dir->d_u_name);