xref: /haiku/src/system/kernel/fs/EntryCache.cpp (revision b8a2c9099141239b25b8f8b69dfe5318388ccc48)
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