11b1b94b8SIngo Weinhold /*
21b1b94b8SIngo Weinhold * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
31b1b94b8SIngo Weinhold * Distributed under the terms of the MIT License.
41b1b94b8SIngo Weinhold */
51b1b94b8SIngo Weinhold
61b1b94b8SIngo Weinhold
71b1b94b8SIngo Weinhold #include "EntryCache.h"
81b1b94b8SIngo Weinhold
91b1b94b8SIngo Weinhold #include <new>
10e83979afSAugustin Cavalier #include <vm/vm.h>
11abc06f6bSJarosław Pelczar #include <slab/Slab.h>
121b1b94b8SIngo Weinhold
131b1b94b8SIngo Weinhold
141b1b94b8SIngo Weinhold static const int32 kEntryNotInArray = -1;
151b1b94b8SIngo Weinhold static const int32 kEntryRemoved = -2;
161b1b94b8SIngo Weinhold
171b1b94b8SIngo Weinhold
181b1b94b8SIngo Weinhold // #pragma mark - EntryCacheGeneration
191b1b94b8SIngo Weinhold
201b1b94b8SIngo Weinhold
EntryCacheGeneration()211b1b94b8SIngo Weinhold EntryCacheGeneration::EntryCacheGeneration()
221b1b94b8SIngo Weinhold :
231b1b94b8SIngo Weinhold next_index(0),
241b1b94b8SIngo Weinhold entries(NULL)
251b1b94b8SIngo Weinhold {
261b1b94b8SIngo Weinhold }
271b1b94b8SIngo Weinhold
281b1b94b8SIngo Weinhold
~EntryCacheGeneration()291b1b94b8SIngo Weinhold EntryCacheGeneration::~EntryCacheGeneration()
301b1b94b8SIngo Weinhold {
311b1b94b8SIngo Weinhold delete[] entries;
321b1b94b8SIngo Weinhold }
331b1b94b8SIngo Weinhold
341b1b94b8SIngo Weinhold
351b1b94b8SIngo Weinhold status_t
Init(int32 entriesSize)3625866ebeSAugustin Cavalier EntryCacheGeneration::Init(int32 entriesSize)
371b1b94b8SIngo Weinhold {
3825866ebeSAugustin Cavalier entries_size = entriesSize;
3925866ebeSAugustin Cavalier entries = new(std::nothrow) EntryCacheEntry*[entries_size];
401b1b94b8SIngo Weinhold if (entries == NULL)
411b1b94b8SIngo Weinhold return B_NO_MEMORY;
421b1b94b8SIngo Weinhold
4325866ebeSAugustin Cavalier memset(entries, 0, sizeof(EntryCacheEntry*) * entries_size);
441b1b94b8SIngo Weinhold return B_OK;
451b1b94b8SIngo Weinhold }
461b1b94b8SIngo Weinhold
471b1b94b8SIngo Weinhold
481b1b94b8SIngo Weinhold // #pragma mark - EntryCache
491b1b94b8SIngo Weinhold
501b1b94b8SIngo Weinhold
EntryCache()511b1b94b8SIngo Weinhold EntryCache::EntryCache()
521b1b94b8SIngo Weinhold :
538016c5b7SAugustin Cavalier fGenerationCount(0),
548016c5b7SAugustin Cavalier fGenerations(NULL),
551b1b94b8SIngo Weinhold fCurrentGeneration(0)
561b1b94b8SIngo Weinhold {
571b1b94b8SIngo Weinhold rw_lock_init(&fLock, "entry cache");
581b1b94b8SIngo Weinhold
591b1b94b8SIngo Weinhold new(&fEntries) EntryTable;
601b1b94b8SIngo Weinhold }
611b1b94b8SIngo Weinhold
621b1b94b8SIngo Weinhold
~EntryCache()631b1b94b8SIngo Weinhold EntryCache::~EntryCache()
641b1b94b8SIngo Weinhold {
651b1b94b8SIngo Weinhold // delete entries
661b1b94b8SIngo Weinhold EntryCacheEntry* entry = fEntries.Clear(true);
671b1b94b8SIngo Weinhold while (entry != NULL) {
681b1b94b8SIngo Weinhold EntryCacheEntry* next = entry->hash_link;
691b1b94b8SIngo Weinhold free(entry);
701b1b94b8SIngo Weinhold entry = next;
711b1b94b8SIngo Weinhold }
7225866ebeSAugustin Cavalier delete[] fGenerations;
731b1b94b8SIngo Weinhold
741b1b94b8SIngo Weinhold rw_lock_destroy(&fLock);
751b1b94b8SIngo Weinhold }
761b1b94b8SIngo Weinhold
771b1b94b8SIngo Weinhold
781b1b94b8SIngo Weinhold status_t
Init()791b1b94b8SIngo Weinhold EntryCache::Init()
801b1b94b8SIngo Weinhold {
811b1b94b8SIngo Weinhold status_t error = fEntries.Init();
821b1b94b8SIngo Weinhold if (error != B_OK)
831b1b94b8SIngo Weinhold return error;
841b1b94b8SIngo Weinhold
8525866ebeSAugustin Cavalier int32 entriesSize = 1024;
86e83979afSAugustin Cavalier fGenerationCount = 8;
87e83979afSAugustin Cavalier
88e83979afSAugustin Cavalier // TODO: Choose generation size/count more scientifically?
89e83979afSAugustin Cavalier // TODO: Add low_resource handler hook?
90e83979afSAugustin Cavalier if (vm_available_memory() >= (1024*1024*1024)) {
91*ab4fc434SAugustin Cavalier entriesSize = 8192;
92e83979afSAugustin Cavalier fGenerationCount = 16;
93e83979afSAugustin Cavalier }
9425866ebeSAugustin Cavalier
9525866ebeSAugustin Cavalier fGenerations = new(std::nothrow) EntryCacheGeneration[fGenerationCount];
9625866ebeSAugustin Cavalier for (int32 i = 0; i < fGenerationCount; i++) {
9725866ebeSAugustin Cavalier error = fGenerations[i].Init(entriesSize);
981b1b94b8SIngo Weinhold if (error != B_OK)
991b1b94b8SIngo Weinhold return error;
1001b1b94b8SIngo Weinhold }
1011b1b94b8SIngo Weinhold
1021b1b94b8SIngo Weinhold return B_OK;
1031b1b94b8SIngo Weinhold }
1041b1b94b8SIngo Weinhold
1051b1b94b8SIngo Weinhold
1061b1b94b8SIngo Weinhold status_t
Add(ino_t dirID,const char * name,ino_t nodeID,bool missing)107efb0a3a8SMichael Lotz EntryCache::Add(ino_t dirID, const char* name, ino_t nodeID, bool missing)
1081b1b94b8SIngo Weinhold {
1091b1b94b8SIngo Weinhold EntryCacheKey key(dirID, name);
1101b1b94b8SIngo Weinhold
1111b1b94b8SIngo Weinhold WriteLocker _(fLock);
1121b1b94b8SIngo Weinhold
11325866ebeSAugustin Cavalier if (fGenerationCount == 0)
11425866ebeSAugustin Cavalier return B_NO_MEMORY;
11525866ebeSAugustin Cavalier
1161b1b94b8SIngo Weinhold EntryCacheEntry* entry = fEntries.Lookup(key);
1171b1b94b8SIngo Weinhold if (entry != NULL) {
1181b1b94b8SIngo Weinhold entry->node_id = nodeID;
119efb0a3a8SMichael Lotz entry->missing = missing;
1201b1b94b8SIngo Weinhold if (entry->generation != fCurrentGeneration) {
1211b1b94b8SIngo Weinhold if (entry->index >= 0) {
1221b1b94b8SIngo Weinhold fGenerations[entry->generation].entries[entry->index] = NULL;
1231b1b94b8SIngo Weinhold _AddEntryToCurrentGeneration(entry);
1241b1b94b8SIngo Weinhold }
1251b1b94b8SIngo Weinhold }
1261b1b94b8SIngo Weinhold return B_OK;
1271b1b94b8SIngo Weinhold }
1281b1b94b8SIngo Weinhold
129abc06f6bSJarosław Pelczar // Avoid deadlock if system had to wait for free memory
130b8a2c909SAugustin Cavalier const size_t nameLen = strlen(name);
131b8a2c909SAugustin Cavalier entry = (EntryCacheEntry*)malloc_etc(sizeof(EntryCacheEntry) + nameLen,
132abc06f6bSJarosław Pelczar CACHE_DONT_WAIT_FOR_MEMORY);
133abc06f6bSJarosław Pelczar
1341b1b94b8SIngo Weinhold if (entry == NULL)
1351b1b94b8SIngo Weinhold return B_NO_MEMORY;
1361b1b94b8SIngo Weinhold
1371b1b94b8SIngo Weinhold entry->node_id = nodeID;
1381b1b94b8SIngo Weinhold entry->dir_id = dirID;
139*ab4fc434SAugustin Cavalier entry->hash = EntryCacheKey::Hash(dirID, name);
140efb0a3a8SMichael Lotz entry->missing = missing;
1411b1b94b8SIngo Weinhold entry->generation = fCurrentGeneration;
1421b1b94b8SIngo Weinhold entry->index = kEntryNotInArray;
143b8a2c909SAugustin Cavalier memcpy(entry->name, name, nameLen + 1);
1441b1b94b8SIngo Weinhold
1451b1b94b8SIngo Weinhold fEntries.Insert(entry);
1461b1b94b8SIngo Weinhold
1471b1b94b8SIngo Weinhold _AddEntryToCurrentGeneration(entry);
1481b1b94b8SIngo Weinhold
1491b1b94b8SIngo Weinhold return B_OK;
1501b1b94b8SIngo Weinhold }
1511b1b94b8SIngo Weinhold
1521b1b94b8SIngo Weinhold
1531b1b94b8SIngo Weinhold status_t
Remove(ino_t dirID,const char * name)1541b1b94b8SIngo Weinhold EntryCache::Remove(ino_t dirID, const char* name)
1551b1b94b8SIngo Weinhold {
1561b1b94b8SIngo Weinhold EntryCacheKey key(dirID, name);
1571b1b94b8SIngo Weinhold
1581b1b94b8SIngo Weinhold WriteLocker writeLocker(fLock);
1591b1b94b8SIngo Weinhold
1601b1b94b8SIngo Weinhold EntryCacheEntry* entry = fEntries.Lookup(key);
1611b1b94b8SIngo Weinhold if (entry == NULL)
1621b1b94b8SIngo Weinhold return B_ENTRY_NOT_FOUND;
1631b1b94b8SIngo Weinhold
1641b1b94b8SIngo Weinhold fEntries.Remove(entry);
1651b1b94b8SIngo Weinhold
1661b1b94b8SIngo Weinhold if (entry->index >= 0) {
1671b1b94b8SIngo Weinhold // remove the entry from its generation and delete it
1681b1b94b8SIngo Weinhold fGenerations[entry->generation].entries[entry->index] = NULL;
1691b1b94b8SIngo Weinhold free(entry);
1701b1b94b8SIngo Weinhold } else {
1711b1b94b8SIngo Weinhold // We can't free it, since another thread is about to try to move it
1721b1b94b8SIngo Weinhold // to another generation. We mark it removed and the other thread will
1731b1b94b8SIngo Weinhold // take care of deleting it.
1741b1b94b8SIngo Weinhold entry->index = kEntryRemoved;
1751b1b94b8SIngo Weinhold }
1761b1b94b8SIngo Weinhold
1771b1b94b8SIngo Weinhold return B_OK;
1781b1b94b8SIngo Weinhold }
1791b1b94b8SIngo Weinhold
1801b1b94b8SIngo Weinhold
1811b1b94b8SIngo Weinhold bool
Lookup(ino_t dirID,const char * name,ino_t & _nodeID,bool & _missing)182efb0a3a8SMichael Lotz EntryCache::Lookup(ino_t dirID, const char* name, ino_t& _nodeID,
183efb0a3a8SMichael Lotz bool& _missing)
1841b1b94b8SIngo Weinhold {
1851b1b94b8SIngo Weinhold EntryCacheKey key(dirID, name);
1861b1b94b8SIngo Weinhold
1871b1b94b8SIngo Weinhold ReadLocker readLocker(fLock);
1881b1b94b8SIngo Weinhold
1891b1b94b8SIngo Weinhold EntryCacheEntry* entry = fEntries.Lookup(key);
1901b1b94b8SIngo Weinhold if (entry == NULL)
1911b1b94b8SIngo Weinhold return false;
1921b1b94b8SIngo Weinhold
19325866ebeSAugustin Cavalier const int32 oldGeneration = atomic_get_and_set(&entry->generation,
194077c84ebSPawel Dziepak fCurrentGeneration);
1951b1b94b8SIngo Weinhold if (oldGeneration == fCurrentGeneration || entry->index < 0) {
1961b1b94b8SIngo Weinhold // The entry is already in the current generation or is being moved to
1971b1b94b8SIngo Weinhold // it by another thread.
1981b1b94b8SIngo Weinhold _nodeID = entry->node_id;
199efb0a3a8SMichael Lotz _missing = entry->missing;
2001b1b94b8SIngo Weinhold return true;
2011b1b94b8SIngo Weinhold }
2021b1b94b8SIngo Weinhold
2031b1b94b8SIngo Weinhold // remove from old generation array
2041b1b94b8SIngo Weinhold fGenerations[oldGeneration].entries[entry->index] = NULL;
2051b1b94b8SIngo Weinhold entry->index = kEntryNotInArray;
2061b1b94b8SIngo Weinhold
2071b1b94b8SIngo Weinhold // add to the current generation
2083a19a89fSAugustin Cavalier const int32 index = atomic_add(&fGenerations[fCurrentGeneration].next_index, 1);
20925866ebeSAugustin Cavalier if (index < fGenerations[fCurrentGeneration].entries_size) {
2101b1b94b8SIngo Weinhold fGenerations[fCurrentGeneration].entries[index] = entry;
2111b1b94b8SIngo Weinhold entry->index = index;
2121b1b94b8SIngo Weinhold _nodeID = entry->node_id;
213efb0a3a8SMichael Lotz _missing = entry->missing;
2141b1b94b8SIngo Weinhold return true;
2151b1b94b8SIngo Weinhold }
2161b1b94b8SIngo Weinhold
2171b1b94b8SIngo Weinhold // The current generation is full, so we probably need to clear the oldest
2181b1b94b8SIngo Weinhold // one to make room. We need the write lock for that.
2191b1b94b8SIngo Weinhold readLocker.Unlock();
2201b1b94b8SIngo Weinhold WriteLocker writeLocker(fLock);
2211b1b94b8SIngo Weinhold
2221b1b94b8SIngo Weinhold if (entry->index == kEntryRemoved) {
2231b1b94b8SIngo Weinhold // the entry has been removed in the meantime
2241b1b94b8SIngo Weinhold free(entry);
2251b1b94b8SIngo Weinhold return false;
2261b1b94b8SIngo Weinhold }
2271b1b94b8SIngo Weinhold
2281b1b94b8SIngo Weinhold _AddEntryToCurrentGeneration(entry);
2291b1b94b8SIngo Weinhold
2301b1b94b8SIngo Weinhold _nodeID = entry->node_id;
231efb0a3a8SMichael Lotz _missing = entry->missing;
2321b1b94b8SIngo Weinhold return true;
2331b1b94b8SIngo Weinhold }
2341b1b94b8SIngo Weinhold
2351b1b94b8SIngo Weinhold
23683291a2aSIngo Weinhold const char*
DebugReverseLookup(ino_t nodeID,ino_t & _dirID)23783291a2aSIngo Weinhold EntryCache::DebugReverseLookup(ino_t nodeID, ino_t& _dirID)
23883291a2aSIngo Weinhold {
23983291a2aSIngo Weinhold for (EntryTable::Iterator it = fEntries.GetIterator();
24083291a2aSIngo Weinhold EntryCacheEntry* entry = it.Next();) {
24183291a2aSIngo Weinhold if (nodeID == entry->node_id && strcmp(entry->name, ".") != 0
24283291a2aSIngo Weinhold && strcmp(entry->name, "..") != 0) {
24383291a2aSIngo Weinhold _dirID = entry->dir_id;
24483291a2aSIngo Weinhold return entry->name;
24583291a2aSIngo Weinhold }
24683291a2aSIngo Weinhold }
24783291a2aSIngo Weinhold
24883291a2aSIngo Weinhold return NULL;
24983291a2aSIngo Weinhold }
25083291a2aSIngo Weinhold
25183291a2aSIngo Weinhold
2521b1b94b8SIngo Weinhold void
_AddEntryToCurrentGeneration(EntryCacheEntry * entry)2531b1b94b8SIngo Weinhold EntryCache::_AddEntryToCurrentGeneration(EntryCacheEntry* entry)
2541b1b94b8SIngo Weinhold {
25525866ebeSAugustin Cavalier ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
25625866ebeSAugustin Cavalier
2571b1b94b8SIngo Weinhold // the generation might not be full yet
2581b1b94b8SIngo Weinhold int32 index = fGenerations[fCurrentGeneration].next_index++;
25925866ebeSAugustin Cavalier if (index < fGenerations[fCurrentGeneration].entries_size) {
2601b1b94b8SIngo Weinhold fGenerations[fCurrentGeneration].entries[index] = entry;
2611b1b94b8SIngo Weinhold entry->generation = fCurrentGeneration;
2621b1b94b8SIngo Weinhold entry->index = index;
2631b1b94b8SIngo Weinhold return;
2641b1b94b8SIngo Weinhold }
2651b1b94b8SIngo Weinhold
2661b1b94b8SIngo Weinhold // we have to clear the oldest generation
26725866ebeSAugustin Cavalier const int32 newGeneration = (fCurrentGeneration + 1) % fGenerationCount;
26825866ebeSAugustin Cavalier for (int32 i = 0; i < fGenerations[newGeneration].entries_size; i++) {
2691b1b94b8SIngo Weinhold EntryCacheEntry* otherEntry = fGenerations[newGeneration].entries[i];
2701b1b94b8SIngo Weinhold if (otherEntry == NULL)
2711b1b94b8SIngo Weinhold continue;
2721b1b94b8SIngo Weinhold
2731b1b94b8SIngo Weinhold fGenerations[newGeneration].entries[i] = NULL;
2741b1b94b8SIngo Weinhold fEntries.Remove(otherEntry);
2751b1b94b8SIngo Weinhold free(otherEntry);
2761b1b94b8SIngo Weinhold }
2771b1b94b8SIngo Weinhold
2781b1b94b8SIngo Weinhold // set the new generation and add the entry
2791b1b94b8SIngo Weinhold fCurrentGeneration = newGeneration;
2801b1b94b8SIngo Weinhold fGenerations[newGeneration].entries[0] = entry;
28125866ebeSAugustin Cavalier fGenerations[newGeneration].next_index = 1;
2821b1b94b8SIngo Weinhold entry->generation = newGeneration;
2831b1b94b8SIngo Weinhold entry->index = 0;
2841b1b94b8SIngo Weinhold }
285