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