]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/dircache.c
dircache documentatation
[netatalk.git] / etc / afpd / dircache.c
index 5906036e0a4b84d479d0046198a3d2917f641334..145b962ddf354abc0e9f780e09bdb6f39bf3a006 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $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".
  */
 
 /********************************************************
@@ -59,7 +97,7 @@
  ********************************************************/
 
 /*****************************
- *       THE dircache        */
+ *       the dircache        */
 
 static hash_t       *dircache;        /* The actual cache */
 static unsigned int dircache_maxsize; /* cache maximum size */
@@ -173,7 +211,6 @@ static int hash_comp_didname(const void *k1, const void *k2)
 
 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
@@ -187,7 +224,7 @@ static const int dircache_free_quantum = 256; /* number of entries to free */
  */
 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}");
@@ -195,7 +232,7 @@ static void dircache_evict(void)
     while (i--) {
         if ((dir = (struct dir *)dequeue(index_queue)) == NULL) { /* 1 */
             dircache_dump();
-            exit(EXITERR_SYS);
+            AFP_PANIC("dircache_evict");
         }
         queue_count--;
 
@@ -204,7 +241,7 @@ static void dircache_evict(void)
             || (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;
@@ -247,7 +284,8 @@ struct dir *dircache_search_by_did(const struct vol *vol, cnid_t did)
         cdir = hnode_get(hn);
 
     if (cdir)
-        LOG(log_debug, logtype_afpd, "dircache(did:%u): {cached: path:'%s'}", ntohl(did), cfrombstring(cdir->d_fullpath));
+        LOG(log_debug, logtype_afpd, "dircache(did:%u): {cached: path:'%s'}",
+            ntohl(did), cfrombstring(cdir->d_u_name));
     else
         LOG(log_debug, logtype_afpd, "dircache(did:%u): {not in cache}", ntohl(did));
 
@@ -271,11 +309,14 @@ struct dir *dircache_search_by_name(const struct vol *vol, const struct dir *dir
     hnode_t *hn;
     static_bstring uname = {-1, len, (unsigned char *)name};
 
-   AFP_ASSERT(vol);
-   AFP_ASSERT(dir);
-   AFP_ASSERT(name);
-   AFP_ASSERT(len == strlen(name));
-   AFP_ASSERT(len < 256);
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
+    AFP_ASSERT(name);
+    AFP_ASSERT(len == strlen(name));
+    AFP_ASSERT(len < 256);
+
+    LOG(log_debug, logtype_afpd, "dircache_search_by_name(did:%u, \"%s\")",
+        ntohl(dir->d_did), name);
 
     if (dir->d_did != DIRDID_ROOT_PARENT) {
         key.d_vid = vol->v_vid;
@@ -287,11 +328,11 @@ struct dir *dircache_search_by_name(const struct vol *vol, const struct dir *dir
     }
 
     if (cdir)
-        LOG(log_debug, logtype_afpd, "dircache(pdid:%u, did:%u, '%s'): {found in cache}",
-            ntohl(dir->d_did), ntohl(cdir->d_did), cfrombstring(cdir->d_fullpath));
+        LOG(log_debug, logtype_afpd, "dircache(did:%u, '%s'): {found in cache}",
+            ntohl(dir->d_did), name);
     else
-        LOG(log_debug, logtype_afpd, "dircache(pdid:%u,'%s/%s'): {not in cache}",
-            ntohl(dir->d_did), cfrombstring(dir->d_fullpath), name);
+        LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {not in cache}",
+            ntohl(dir->d_did), name);
 
     return cdir;
 }
@@ -310,7 +351,6 @@ int dircache_add(struct dir *dir)
    AFP_ASSERT(dir);
    AFP_ASSERT(ntohl(dir->d_pdid) >= 2);
    AFP_ASSERT(ntohl(dir->d_did) >= CNID_START);
-   AFP_ASSERT(dir->d_fullpath);
    AFP_ASSERT(dir->d_u_name);
    AFP_ASSERT(dir->d_vid);
    AFP_ASSERT(dircache->hash_nodecount <= dircache_maxsize);
@@ -339,7 +379,7 @@ int dircache_add(struct dir *dir)
         queue_count++;
     }
 
-    LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {added}", ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
+    LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {added}", ntohl(dir->d_did), cfrombstring(dir->d_u_name));
 
    AFP_ASSERT(queue_count == index_didname->hash_nodecount 
            && queue_count == dircache->hash_nodecount);
@@ -372,24 +412,24 @@ void dircache_remove(const struct vol *vol _U_, struct dir *dir, int flags)
     if (flags & DIDNAME_INDEX) {
         if ((hn = hash_lookup(index_didname, dir)) == NULL) {
             LOG(log_error, logtype_default, "dircache_remove(%u,%s): not in didname index", 
-                ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
+                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) {
         if ((hn = hash_lookup(dircache, dir)) == NULL) {
             LOG(log_error, logtype_default, "dircache_remove(%u,%s): not in dircache", 
-                ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
+                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), cfrombstring(dir->d_fullpath));
+    LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {removed}", ntohl(dir->d_did), dir->d_u_name);
 
    AFP_ASSERT(queue_count == index_didname->hash_nodecount 
            && queue_count == dircache->hash_nodecount);
@@ -470,7 +510,8 @@ void dircache_dump(void)
             break;
         dir = (struct dir *)n->data;
         fprintf(dump, "%05u: vid:%u, pdid:%6u, did:%6u, path:%s, locked:%3s, oforks:%s\n",
-                i, ntohs(dir->d_vid), ntohl(dir->d_pdid), ntohl(dir->d_did), cfrombstring(dir->d_fullpath),
+                i, ntohs(dir->d_vid), ntohl(dir->d_pdid), ntohl(dir->d_did),
+                cfrombstring(dir->d_u_name),
                 (dir->d_flags & DIRF_CACHELOCK) ? "yes" : "no",
                 dir->d_ofork ? "yes" : "no");
         n = n->next;