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 211b1b94b8SIngo Weinhold EntryCacheGeneration::EntryCacheGeneration() 221b1b94b8SIngo Weinhold : 231b1b94b8SIngo Weinhold next_index(0), 241b1b94b8SIngo Weinhold entries(NULL) 251b1b94b8SIngo Weinhold { 261b1b94b8SIngo Weinhold } 271b1b94b8SIngo Weinhold 281b1b94b8SIngo Weinhold 291b1b94b8SIngo Weinhold EntryCacheGeneration::~EntryCacheGeneration() 301b1b94b8SIngo Weinhold { 311b1b94b8SIngo Weinhold delete[] entries; 321b1b94b8SIngo Weinhold } 331b1b94b8SIngo Weinhold 341b1b94b8SIngo Weinhold 351b1b94b8SIngo Weinhold status_t 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 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 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 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)) { 91e83979afSAugustin Cavalier entriesSize = 8096; 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 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 130*b8a2c909SAugustin Cavalier const size_t nameLen = strlen(name); 131*b8a2c909SAugustin 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; 139efb0a3a8SMichael Lotz entry->missing = missing; 1401b1b94b8SIngo Weinhold entry->generation = fCurrentGeneration; 1411b1b94b8SIngo Weinhold entry->index = kEntryNotInArray; 142*b8a2c909SAugustin Cavalier memcpy(entry->name, name, nameLen + 1); 1431b1b94b8SIngo Weinhold 1441b1b94b8SIngo Weinhold fEntries.Insert(entry); 1451b1b94b8SIngo Weinhold 1461b1b94b8SIngo Weinhold _AddEntryToCurrentGeneration(entry); 1471b1b94b8SIngo Weinhold 1481b1b94b8SIngo Weinhold return B_OK; 1491b1b94b8SIngo Weinhold } 1501b1b94b8SIngo Weinhold 1511b1b94b8SIngo Weinhold 1521b1b94b8SIngo Weinhold status_t 1531b1b94b8SIngo Weinhold EntryCache::Remove(ino_t dirID, const char* name) 1541b1b94b8SIngo Weinhold { 1551b1b94b8SIngo Weinhold EntryCacheKey key(dirID, name); 1561b1b94b8SIngo Weinhold 1571b1b94b8SIngo Weinhold WriteLocker writeLocker(fLock); 1581b1b94b8SIngo Weinhold 1591b1b94b8SIngo Weinhold EntryCacheEntry* entry = fEntries.Lookup(key); 1601b1b94b8SIngo Weinhold if (entry == NULL) 1611b1b94b8SIngo Weinhold return B_ENTRY_NOT_FOUND; 1621b1b94b8SIngo Weinhold 1631b1b94b8SIngo Weinhold fEntries.Remove(entry); 1641b1b94b8SIngo Weinhold 1651b1b94b8SIngo Weinhold if (entry->index >= 0) { 1661b1b94b8SIngo Weinhold // remove the entry from its generation and delete it 1671b1b94b8SIngo Weinhold fGenerations[entry->generation].entries[entry->index] = NULL; 1681b1b94b8SIngo Weinhold free(entry); 1691b1b94b8SIngo Weinhold } else { 1701b1b94b8SIngo Weinhold // We can't free it, since another thread is about to try to move it 1711b1b94b8SIngo Weinhold // to another generation. We mark it removed and the other thread will 1721b1b94b8SIngo Weinhold // take care of deleting it. 1731b1b94b8SIngo Weinhold entry->index = kEntryRemoved; 1741b1b94b8SIngo Weinhold } 1751b1b94b8SIngo Weinhold 1761b1b94b8SIngo Weinhold return B_OK; 1771b1b94b8SIngo Weinhold } 1781b1b94b8SIngo Weinhold 1791b1b94b8SIngo Weinhold 1801b1b94b8SIngo Weinhold bool 181efb0a3a8SMichael Lotz EntryCache::Lookup(ino_t dirID, const char* name, ino_t& _nodeID, 182efb0a3a8SMichael Lotz bool& _missing) 1831b1b94b8SIngo Weinhold { 1841b1b94b8SIngo Weinhold EntryCacheKey key(dirID, name); 1851b1b94b8SIngo Weinhold 1861b1b94b8SIngo Weinhold ReadLocker readLocker(fLock); 1871b1b94b8SIngo Weinhold 1881b1b94b8SIngo Weinhold EntryCacheEntry* entry = fEntries.Lookup(key); 1891b1b94b8SIngo Weinhold if (entry == NULL) 1901b1b94b8SIngo Weinhold return false; 1911b1b94b8SIngo Weinhold 19225866ebeSAugustin Cavalier const int32 oldGeneration = atomic_get_and_set(&entry->generation, 193077c84ebSPawel Dziepak fCurrentGeneration); 1941b1b94b8SIngo Weinhold if (oldGeneration == fCurrentGeneration || entry->index < 0) { 1951b1b94b8SIngo Weinhold // The entry is already in the current generation or is being moved to 1961b1b94b8SIngo Weinhold // it by another thread. 1971b1b94b8SIngo Weinhold _nodeID = entry->node_id; 198efb0a3a8SMichael Lotz _missing = entry->missing; 1991b1b94b8SIngo Weinhold return true; 2001b1b94b8SIngo Weinhold } 2011b1b94b8SIngo Weinhold 2021b1b94b8SIngo Weinhold // remove from old generation array 2031b1b94b8SIngo Weinhold fGenerations[oldGeneration].entries[entry->index] = NULL; 2041b1b94b8SIngo Weinhold entry->index = kEntryNotInArray; 2051b1b94b8SIngo Weinhold 2061b1b94b8SIngo Weinhold // add to the current generation 2073a19a89fSAugustin Cavalier const int32 index = atomic_add(&fGenerations[fCurrentGeneration].next_index, 1); 20825866ebeSAugustin Cavalier if (index < fGenerations[fCurrentGeneration].entries_size) { 2091b1b94b8SIngo Weinhold fGenerations[fCurrentGeneration].entries[index] = entry; 2101b1b94b8SIngo Weinhold entry->index = index; 2111b1b94b8SIngo Weinhold _nodeID = entry->node_id; 212efb0a3a8SMichael Lotz _missing = entry->missing; 2131b1b94b8SIngo Weinhold return true; 2141b1b94b8SIngo Weinhold } 2151b1b94b8SIngo Weinhold 2161b1b94b8SIngo Weinhold // The current generation is full, so we probably need to clear the oldest 2171b1b94b8SIngo Weinhold // one to make room. We need the write lock for that. 2181b1b94b8SIngo Weinhold readLocker.Unlock(); 2191b1b94b8SIngo Weinhold WriteLocker writeLocker(fLock); 2201b1b94b8SIngo Weinhold 2211b1b94b8SIngo Weinhold if (entry->index == kEntryRemoved) { 2221b1b94b8SIngo Weinhold // the entry has been removed in the meantime 2231b1b94b8SIngo Weinhold free(entry); 2241b1b94b8SIngo Weinhold return false; 2251b1b94b8SIngo Weinhold } 2261b1b94b8SIngo Weinhold 2271b1b94b8SIngo Weinhold _AddEntryToCurrentGeneration(entry); 2281b1b94b8SIngo Weinhold 2291b1b94b8SIngo Weinhold _nodeID = entry->node_id; 230efb0a3a8SMichael Lotz _missing = entry->missing; 2311b1b94b8SIngo Weinhold return true; 2321b1b94b8SIngo Weinhold } 2331b1b94b8SIngo Weinhold 2341b1b94b8SIngo Weinhold 23583291a2aSIngo Weinhold const char* 23683291a2aSIngo Weinhold EntryCache::DebugReverseLookup(ino_t nodeID, ino_t& _dirID) 23783291a2aSIngo Weinhold { 23883291a2aSIngo Weinhold for (EntryTable::Iterator it = fEntries.GetIterator(); 23983291a2aSIngo Weinhold EntryCacheEntry* entry = it.Next();) { 24083291a2aSIngo Weinhold if (nodeID == entry->node_id && strcmp(entry->name, ".") != 0 24183291a2aSIngo Weinhold && strcmp(entry->name, "..") != 0) { 24283291a2aSIngo Weinhold _dirID = entry->dir_id; 24383291a2aSIngo Weinhold return entry->name; 24483291a2aSIngo Weinhold } 24583291a2aSIngo Weinhold } 24683291a2aSIngo Weinhold 24783291a2aSIngo Weinhold return NULL; 24883291a2aSIngo Weinhold } 24983291a2aSIngo Weinhold 25083291a2aSIngo Weinhold 2511b1b94b8SIngo Weinhold void 2521b1b94b8SIngo Weinhold EntryCache::_AddEntryToCurrentGeneration(EntryCacheEntry* entry) 2531b1b94b8SIngo Weinhold { 25425866ebeSAugustin Cavalier ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); 25525866ebeSAugustin Cavalier 2561b1b94b8SIngo Weinhold // the generation might not be full yet 2571b1b94b8SIngo Weinhold int32 index = fGenerations[fCurrentGeneration].next_index++; 25825866ebeSAugustin Cavalier if (index < fGenerations[fCurrentGeneration].entries_size) { 2591b1b94b8SIngo Weinhold fGenerations[fCurrentGeneration].entries[index] = entry; 2601b1b94b8SIngo Weinhold entry->generation = fCurrentGeneration; 2611b1b94b8SIngo Weinhold entry->index = index; 2621b1b94b8SIngo Weinhold return; 2631b1b94b8SIngo Weinhold } 2641b1b94b8SIngo Weinhold 2651b1b94b8SIngo Weinhold // we have to clear the oldest generation 26625866ebeSAugustin Cavalier const int32 newGeneration = (fCurrentGeneration + 1) % fGenerationCount; 26725866ebeSAugustin Cavalier for (int32 i = 0; i < fGenerations[newGeneration].entries_size; i++) { 2681b1b94b8SIngo Weinhold EntryCacheEntry* otherEntry = fGenerations[newGeneration].entries[i]; 2691b1b94b8SIngo Weinhold if (otherEntry == NULL) 2701b1b94b8SIngo Weinhold continue; 2711b1b94b8SIngo Weinhold 2721b1b94b8SIngo Weinhold fGenerations[newGeneration].entries[i] = NULL; 2731b1b94b8SIngo Weinhold fEntries.Remove(otherEntry); 2741b1b94b8SIngo Weinhold free(otherEntry); 2751b1b94b8SIngo Weinhold } 2761b1b94b8SIngo Weinhold 2771b1b94b8SIngo Weinhold // set the new generation and add the entry 2781b1b94b8SIngo Weinhold fCurrentGeneration = newGeneration; 2791b1b94b8SIngo Weinhold fGenerations[newGeneration].entries[0] = entry; 28025866ebeSAugustin Cavalier fGenerations[newGeneration].next_index = 1; 2811b1b94b8SIngo Weinhold entry->generation = newGeneration; 2821b1b94b8SIngo Weinhold entry->index = 0; 2831b1b94b8SIngo Weinhold } 284