|
| static hash_val_t | hash_vid_did (const void *key) |
| static int | hash_comp_vid_did (const void *key1, const void *key2) |
| static hash_val_t | hash_didname (const void *p) |
| static int | hash_comp_didname (const void *k1, const void *k2) |
| static int | should_validate_cache_entry (void) |
| | Determine if cache entry should be validated against filesystem.
|
| static int | cache_entry_externally_modified (struct dir *cdir, const struct stat *st) |
| | Smart validation for directory cache entries.
|
| static void | dircache_evict (void) |
| | Remove a fixed number of (oldest) entries from the cache and indexes.
|
| struct dir * | dircache_search_by_did (const struct vol *vol, cnid_t cnid) |
| | Search the dircache via a CNID for a directory.
|
| struct dir * | dircache_search_by_name (const struct vol *vol, const struct dir *dir, char *name, int len) |
| | Search the cache via did/name hashtable.
|
| int | dircache_add (const struct vol *vol, struct dir *dir) |
| | create struct dir from struct path
|
| void | dircache_remove (const struct vol *vol, struct dir *dir, int flags) |
| | Remove an entry from the dircache.
|
| void | dircache_remove_children (const struct vol *vol, struct dir *dir) |
| | Remove all child entries of a directory from the dircache.
|
| int | dircache_init (int reqsize) |
| | Initialize the dircache and indexes.
|
| void | log_dircache_stat (void) |
| | Log dircache statistics.
|
| void | dircache_dump (void) |
| | Dump dircache to /tmp/dircache.PID.
|
| int | dircache_set_validation_params (unsigned int freq, unsigned int meta_win, unsigned int meta_thresh) |
| | Set directory cache validation parameters.
|
| void | dircache_reset_validation_counter (void) |
| | Reset validation counter for consistent testing.
|
| void | dircache_report_invalid_entry (struct dir *dir) |
| | Report that a cache entry was invalid when actually used.
|
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 avoiding recursive walks up the path, querying the CNID database each time, when we have to calculate the location of e.g. 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 e.g. happens when a directory is enumerated, files are stored too in the dircache. In order to differentiate between files and dirs, we set the flag DIRF_ISFILE in struct dir.d_flags for files.
The most frequent codepatch that leads to caching is directory enumeration (cf enumerate.c):
- if a element is a directory:
- the cache is searched by dircache_search_by_name()
- 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:
- first getfilparams() is called, which calls
- getmetadata() where the cache is searched with dircache_search_by_name()
- if the element is not found
- get_id() queries the CNID from the database
- then a struct dir is initialized via dir_new() (note the fullpath arg is NULL)
- 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 id in hashing calculations.
In order to avoid cache poisoning, we store the cached entries st_ctime from stat in struct dir.ctime_dircache. Later when we search the cache we compare the stored value with the result of a fresh stat. If the times differ, we remove the cached entry and return "no entry found in cache". A elements ctime changes when
- the element is renamed (we lose the cached entry here, but it will expire when the cache fills)
- its a directory and an object has been created therein
- the element is deleted and recreated under the same name Using ctime leads to cache eviction in case 2) where it wouldn't be necessary, because the dir itself (name, CNID, ...) hasn't changed, but there's no other way.
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
Debugging
Sending SIGINT to a afpd child causes it to dump the dircache to a file "/tmp/dircache.PID".