xref: /haiku/src/system/kernel/slab/ObjectDepot.cpp (revision 86c794e5c10f1b2d99d672d424a8637639c703dd)
1a8806e5eSIngo Weinhold /*
2453a2bddSIngo Weinhold  * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3a8806e5eSIngo Weinhold  * Copyright 2008, Axel Dörfler. All Rights Reserved.
4a8806e5eSIngo Weinhold  * Copyright 2007, Hugo Santos. All Rights Reserved.
5a8806e5eSIngo Weinhold  *
6a8806e5eSIngo Weinhold  * Distributed under the terms of the MIT License.
7a8806e5eSIngo Weinhold  */
8a8806e5eSIngo Weinhold 
9a8806e5eSIngo Weinhold 
10a8806e5eSIngo Weinhold #include <slab/ObjectDepot.h>
11a8806e5eSIngo Weinhold 
12a8806e5eSIngo Weinhold #include <algorithm>
13a8806e5eSIngo Weinhold 
14453a2bddSIngo Weinhold #include <int.h>
15bb439b87SIngo Weinhold #include <slab/Slab.h>
16453a2bddSIngo Weinhold #include <smp.h>
17a8806e5eSIngo Weinhold #include <util/AutoLock.h>
18a8806e5eSIngo Weinhold 
19a8806e5eSIngo Weinhold #include "slab_private.h"
20a8806e5eSIngo Weinhold 
21a8806e5eSIngo Weinhold 
22a8806e5eSIngo Weinhold static const int kMagazineCapacity = 32;
23a8806e5eSIngo Weinhold 	// TODO: Should be dynamically tuned per cache.
24a8806e5eSIngo Weinhold 
25a8806e5eSIngo Weinhold 
26825566f8SIngo Weinhold struct DepotMagazine {
27825566f8SIngo Weinhold 			DepotMagazine*		next;
28825566f8SIngo Weinhold 			uint16				current_round;
29825566f8SIngo Weinhold 			uint16				round_count;
30a8806e5eSIngo Weinhold 			void*				rounds[0];
31825566f8SIngo Weinhold 
32825566f8SIngo Weinhold public:
33825566f8SIngo Weinhold 	inline	bool				IsEmpty() const;
34825566f8SIngo Weinhold 	inline	bool				IsFull() const;
35825566f8SIngo Weinhold 
36825566f8SIngo Weinhold 	inline	void*				Pop();
37825566f8SIngo Weinhold 	inline	bool				Push(void* object);
38a8806e5eSIngo Weinhold };
39a8806e5eSIngo Weinhold 
40a8806e5eSIngo Weinhold 
41a8806e5eSIngo Weinhold struct depot_cpu_store {
42825566f8SIngo Weinhold 	DepotMagazine*	loaded;
43825566f8SIngo Weinhold 	DepotMagazine*	previous;
44a8806e5eSIngo Weinhold };
45a8806e5eSIngo Weinhold 
46a8806e5eSIngo Weinhold 
47825566f8SIngo Weinhold bool
48825566f8SIngo Weinhold DepotMagazine::IsEmpty() const
49a8806e5eSIngo Weinhold {
50825566f8SIngo Weinhold 	return current_round == 0;
51a8806e5eSIngo Weinhold }
52a8806e5eSIngo Weinhold 
53a8806e5eSIngo Weinhold 
54825566f8SIngo Weinhold bool
55825566f8SIngo Weinhold DepotMagazine::IsFull() const
56a8806e5eSIngo Weinhold {
57825566f8SIngo Weinhold 	return current_round == round_count;
58a8806e5eSIngo Weinhold }
59a8806e5eSIngo Weinhold 
60a8806e5eSIngo Weinhold 
61825566f8SIngo Weinhold void*
62825566f8SIngo Weinhold DepotMagazine::Pop()
63a8806e5eSIngo Weinhold {
64825566f8SIngo Weinhold 	return rounds[--current_round];
65a8806e5eSIngo Weinhold }
66a8806e5eSIngo Weinhold 
67a8806e5eSIngo Weinhold 
68825566f8SIngo Weinhold bool
69825566f8SIngo Weinhold DepotMagazine::Push(void* object)
70a8806e5eSIngo Weinhold {
71825566f8SIngo Weinhold 	if (IsFull())
72a8806e5eSIngo Weinhold 		return false;
73825566f8SIngo Weinhold 
74825566f8SIngo Weinhold 	rounds[current_round++] = object;
75a8806e5eSIngo Weinhold 	return true;
76a8806e5eSIngo Weinhold }
77a8806e5eSIngo Weinhold 
78a8806e5eSIngo Weinhold 
79825566f8SIngo Weinhold static DepotMagazine*
80a8806e5eSIngo Weinhold alloc_magazine()
81a8806e5eSIngo Weinhold {
82825566f8SIngo Weinhold 	DepotMagazine* magazine = (DepotMagazine*)slab_internal_alloc(
83bb439b87SIngo Weinhold 		sizeof(DepotMagazine) + kMagazineCapacity * sizeof(void*),
84bb439b87SIngo Weinhold 		CACHE_DONT_SLEEP);
85a8806e5eSIngo Weinhold 	if (magazine) {
86a8806e5eSIngo Weinhold 		magazine->next = NULL;
87a8806e5eSIngo Weinhold 		magazine->current_round = 0;
88a8806e5eSIngo Weinhold 		magazine->round_count = kMagazineCapacity;
89a8806e5eSIngo Weinhold 	}
90a8806e5eSIngo Weinhold 
91a8806e5eSIngo Weinhold 	return magazine;
92a8806e5eSIngo Weinhold }
93a8806e5eSIngo Weinhold 
94a8806e5eSIngo Weinhold 
95a8806e5eSIngo Weinhold static void
96*86c794e5SIngo Weinhold free_magazine(DepotMagazine* magazine, uint32 flags)
97a8806e5eSIngo Weinhold {
98*86c794e5SIngo Weinhold 	slab_internal_free(magazine, flags);
99a8806e5eSIngo Weinhold }
100a8806e5eSIngo Weinhold 
101a8806e5eSIngo Weinhold 
102a8806e5eSIngo Weinhold static void
103*86c794e5SIngo Weinhold empty_magazine(object_depot* depot, DepotMagazine* magazine, uint32 flags)
104a8806e5eSIngo Weinhold {
105a8806e5eSIngo Weinhold 	for (uint16 i = 0; i < magazine->current_round; i++)
106*86c794e5SIngo Weinhold 		depot->return_object(depot, depot->cookie, magazine->rounds[i], flags);
107*86c794e5SIngo Weinhold 	free_magazine(magazine, flags);
108a8806e5eSIngo Weinhold }
109a8806e5eSIngo Weinhold 
110a8806e5eSIngo Weinhold 
111a8806e5eSIngo Weinhold static bool
112825566f8SIngo Weinhold exchange_with_full(object_depot* depot, DepotMagazine*& magazine)
113a8806e5eSIngo Weinhold {
114453a2bddSIngo Weinhold 	SpinLocker _(depot->inner_lock);
115a8806e5eSIngo Weinhold 
116a8806e5eSIngo Weinhold 	if (depot->full == NULL)
117a8806e5eSIngo Weinhold 		return false;
118a8806e5eSIngo Weinhold 
119a8806e5eSIngo Weinhold 	depot->full_count--;
120a8806e5eSIngo Weinhold 	depot->empty_count++;
121a8806e5eSIngo Weinhold 
122a8806e5eSIngo Weinhold 	_push(depot->empty, magazine);
123a8806e5eSIngo Weinhold 	magazine = _pop(depot->full);
124a8806e5eSIngo Weinhold 	return true;
125a8806e5eSIngo Weinhold }
126a8806e5eSIngo Weinhold 
127a8806e5eSIngo Weinhold 
128a8806e5eSIngo Weinhold static bool
129825566f8SIngo Weinhold exchange_with_empty(object_depot* depot, DepotMagazine*& magazine)
130a8806e5eSIngo Weinhold {
131453a2bddSIngo Weinhold 	SpinLocker _(depot->inner_lock);
132a8806e5eSIngo Weinhold 
133a8806e5eSIngo Weinhold 	if (depot->empty == NULL)
134a8806e5eSIngo Weinhold 		return false;
135453a2bddSIngo Weinhold 
136a8806e5eSIngo Weinhold 	depot->empty_count--;
137a8806e5eSIngo Weinhold 
138a8806e5eSIngo Weinhold 	if (magazine) {
139a8806e5eSIngo Weinhold 		_push(depot->full, magazine);
140a8806e5eSIngo Weinhold 		depot->full_count++;
141a8806e5eSIngo Weinhold 	}
142a8806e5eSIngo Weinhold 
143a8806e5eSIngo Weinhold 	magazine = _pop(depot->empty);
144a8806e5eSIngo Weinhold 	return true;
145a8806e5eSIngo Weinhold }
146a8806e5eSIngo Weinhold 
147a8806e5eSIngo Weinhold 
148453a2bddSIngo Weinhold static void
149453a2bddSIngo Weinhold push_empty_magazine(object_depot* depot, DepotMagazine* magazine)
150453a2bddSIngo Weinhold {
151453a2bddSIngo Weinhold 	SpinLocker _(depot->inner_lock);
152453a2bddSIngo Weinhold 
153453a2bddSIngo Weinhold 	_push(depot->empty, magazine);
154453a2bddSIngo Weinhold }
155453a2bddSIngo Weinhold 
156453a2bddSIngo Weinhold 
157a8806e5eSIngo Weinhold static inline depot_cpu_store*
158a8806e5eSIngo Weinhold object_depot_cpu(object_depot* depot)
159a8806e5eSIngo Weinhold {
160a8806e5eSIngo Weinhold 	return &depot->stores[smp_get_current_cpu()];
161a8806e5eSIngo Weinhold }
162a8806e5eSIngo Weinhold 
163a8806e5eSIngo Weinhold 
164453a2bddSIngo Weinhold // #pragma mark - public API
165453a2bddSIngo Weinhold 
166453a2bddSIngo Weinhold 
167453a2bddSIngo Weinhold status_t
168453a2bddSIngo Weinhold object_depot_init(object_depot* depot, uint32 flags, void* cookie,
169*86c794e5SIngo Weinhold 	void (*return_object)(object_depot* depot, void* cookie, void* object,
170*86c794e5SIngo Weinhold 		uint32 flags))
171a8806e5eSIngo Weinhold {
172453a2bddSIngo Weinhold 	depot->full = NULL;
173453a2bddSIngo Weinhold 	depot->empty = NULL;
174453a2bddSIngo Weinhold 	depot->full_count = depot->empty_count = 0;
175453a2bddSIngo Weinhold 
176453a2bddSIngo Weinhold 	rw_lock_init(&depot->outer_lock, "object depot");
177453a2bddSIngo Weinhold 	B_INITIALIZE_SPINLOCK(&depot->inner_lock);
178453a2bddSIngo Weinhold 
179453a2bddSIngo Weinhold 	int cpuCount = smp_get_num_cpus();
180453a2bddSIngo Weinhold 	depot->stores = (depot_cpu_store*)slab_internal_alloc(
181453a2bddSIngo Weinhold 		sizeof(depot_cpu_store) * cpuCount, flags);
182453a2bddSIngo Weinhold 	if (depot->stores == NULL) {
183453a2bddSIngo Weinhold 		rw_lock_destroy(&depot->outer_lock);
184453a2bddSIngo Weinhold 		return B_NO_MEMORY;
185453a2bddSIngo Weinhold 	}
186453a2bddSIngo Weinhold 
187453a2bddSIngo Weinhold 	for (int i = 0; i < cpuCount; i++) {
188453a2bddSIngo Weinhold 		depot->stores[i].loaded = NULL;
189453a2bddSIngo Weinhold 		depot->stores[i].previous = NULL;
190453a2bddSIngo Weinhold 	}
191453a2bddSIngo Weinhold 
192453a2bddSIngo Weinhold 	depot->cookie = cookie;
193453a2bddSIngo Weinhold 	depot->return_object = return_object;
194453a2bddSIngo Weinhold 
195453a2bddSIngo Weinhold 	return B_OK;
196453a2bddSIngo Weinhold }
197453a2bddSIngo Weinhold 
198453a2bddSIngo Weinhold 
199453a2bddSIngo Weinhold void
200*86c794e5SIngo Weinhold object_depot_destroy(object_depot* depot, uint32 flags)
201453a2bddSIngo Weinhold {
202*86c794e5SIngo Weinhold 	object_depot_make_empty(depot, flags);
203453a2bddSIngo Weinhold 
204*86c794e5SIngo Weinhold 	slab_internal_free(depot->stores, flags);
205453a2bddSIngo Weinhold 
206453a2bddSIngo Weinhold 	rw_lock_destroy(&depot->outer_lock);
207453a2bddSIngo Weinhold }
208453a2bddSIngo Weinhold 
209453a2bddSIngo Weinhold 
210453a2bddSIngo Weinhold void*
211453a2bddSIngo Weinhold object_depot_obtain(object_depot* depot)
212453a2bddSIngo Weinhold {
213453a2bddSIngo Weinhold 	ReadLocker readLocker(depot->outer_lock);
214453a2bddSIngo Weinhold 	InterruptsLocker interruptsLocker;
215453a2bddSIngo Weinhold 
216453a2bddSIngo Weinhold 	depot_cpu_store* store = object_depot_cpu(depot);
217a8806e5eSIngo Weinhold 
218a8806e5eSIngo Weinhold 	// To better understand both the Alloc() and Free() logic refer to
219a8806e5eSIngo Weinhold 	// Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings]
220a8806e5eSIngo Weinhold 
221a8806e5eSIngo Weinhold 	// In a nutshell, we try to get an object from the loaded magazine
222a8806e5eSIngo Weinhold 	// if it's not empty, or from the previous magazine if it's full
223a8806e5eSIngo Weinhold 	// and finally from the Slab if the magazine depot has no full magazines.
224a8806e5eSIngo Weinhold 
225a8806e5eSIngo Weinhold 	if (store->loaded == NULL)
226a8806e5eSIngo Weinhold 		return NULL;
227a8806e5eSIngo Weinhold 
228a8806e5eSIngo Weinhold 	while (true) {
229825566f8SIngo Weinhold 		if (!store->loaded->IsEmpty())
230825566f8SIngo Weinhold 			return store->loaded->Pop();
231a8806e5eSIngo Weinhold 
232825566f8SIngo Weinhold 		if (store->previous
233825566f8SIngo Weinhold 			&& (store->previous->IsFull()
234825566f8SIngo Weinhold 				|| exchange_with_full(depot, store->previous))) {
235a8806e5eSIngo Weinhold 			std::swap(store->previous, store->loaded);
236825566f8SIngo Weinhold 		} else
237a8806e5eSIngo Weinhold 			return NULL;
238a8806e5eSIngo Weinhold 	}
239a8806e5eSIngo Weinhold }
240a8806e5eSIngo Weinhold 
241a8806e5eSIngo Weinhold 
242453a2bddSIngo Weinhold int
243*86c794e5SIngo Weinhold object_depot_store(object_depot* depot, void* object, uint32 flags)
244a8806e5eSIngo Weinhold {
245453a2bddSIngo Weinhold 	ReadLocker readLocker(depot->outer_lock);
246453a2bddSIngo Weinhold 	InterruptsLocker interruptsLocker;
247453a2bddSIngo Weinhold 
248453a2bddSIngo Weinhold 	depot_cpu_store* store = object_depot_cpu(depot);
249a8806e5eSIngo Weinhold 
250a8806e5eSIngo Weinhold 	// We try to add the object to the loaded magazine if we have one
251a8806e5eSIngo Weinhold 	// and it's not full, or to the previous one if it is empty. If
252a8806e5eSIngo Weinhold 	// the magazine depot doesn't provide us with a new empty magazine
253a8806e5eSIngo Weinhold 	// we return the object directly to the slab.
254a8806e5eSIngo Weinhold 
255a8806e5eSIngo Weinhold 	while (true) {
256825566f8SIngo Weinhold 		if (store->loaded && store->loaded->Push(object))
257a8806e5eSIngo Weinhold 			return 1;
258a8806e5eSIngo Weinhold 
259825566f8SIngo Weinhold 		if ((store->previous && store->previous->IsEmpty())
260453a2bddSIngo Weinhold 			|| exchange_with_empty(depot, store->previous)) {
261a8806e5eSIngo Weinhold 			std::swap(store->loaded, store->previous);
262453a2bddSIngo Weinhold 		} else {
263453a2bddSIngo Weinhold 			// allocate a new empty magazine
264453a2bddSIngo Weinhold 			interruptsLocker.Unlock();
265453a2bddSIngo Weinhold 			readLocker.Unlock();
266453a2bddSIngo Weinhold 
267453a2bddSIngo Weinhold 			DepotMagazine* magazine = alloc_magazine();
268453a2bddSIngo Weinhold 			if (magazine == NULL)
269a8806e5eSIngo Weinhold 				return 0;
270453a2bddSIngo Weinhold 
271453a2bddSIngo Weinhold 			readLocker.Lock();
272453a2bddSIngo Weinhold 			interruptsLocker.Lock();
273453a2bddSIngo Weinhold 
274453a2bddSIngo Weinhold 			push_empty_magazine(depot, magazine);
275453a2bddSIngo Weinhold 			store = object_depot_cpu(depot);
276a8806e5eSIngo Weinhold 		}
277a8806e5eSIngo Weinhold 	}
278a8806e5eSIngo Weinhold }
279a8806e5eSIngo Weinhold 
280a8806e5eSIngo Weinhold 
281a8806e5eSIngo Weinhold void
282*86c794e5SIngo Weinhold object_depot_make_empty(object_depot* depot, uint32 flags)
283a8806e5eSIngo Weinhold {
284453a2bddSIngo Weinhold 	WriteLocker writeLocker(depot->outer_lock);
285453a2bddSIngo Weinhold 
286453a2bddSIngo Weinhold 	// collect the store magazines
287453a2bddSIngo Weinhold 
288453a2bddSIngo Weinhold 	DepotMagazine* storeMagazines = NULL;
289453a2bddSIngo Weinhold 
290bcf73b3aSIngo Weinhold 	int cpuCount = smp_get_num_cpus();
291bcf73b3aSIngo Weinhold 	for (int i = 0; i < cpuCount; i++) {
292453a2bddSIngo Weinhold 		depot_cpu_store& store = depot->stores[i];
293a8806e5eSIngo Weinhold 
294453a2bddSIngo Weinhold 		if (store.loaded) {
295453a2bddSIngo Weinhold 			_push(storeMagazines, store.loaded);
296453a2bddSIngo Weinhold 			store.loaded = NULL;
297a8806e5eSIngo Weinhold 		}
298a8806e5eSIngo Weinhold 
299453a2bddSIngo Weinhold 		if (store.previous) {
300453a2bddSIngo Weinhold 			_push(storeMagazines, store.previous);
301453a2bddSIngo Weinhold 			store.previous = NULL;
302453a2bddSIngo Weinhold 		}
303453a2bddSIngo Weinhold 	}
304a8806e5eSIngo Weinhold 
305453a2bddSIngo Weinhold 	// detach the depot's full and empty magazines
306a8806e5eSIngo Weinhold 
307453a2bddSIngo Weinhold 	DepotMagazine* fullMagazines = depot->full;
308453a2bddSIngo Weinhold 	depot->full = NULL;
309453a2bddSIngo Weinhold 
310453a2bddSIngo Weinhold 	DepotMagazine* emptyMagazines = depot->empty;
311453a2bddSIngo Weinhold 	depot->empty = NULL;
312453a2bddSIngo Weinhold 
313453a2bddSIngo Weinhold 	writeLocker.Unlock();
314453a2bddSIngo Weinhold 
315453a2bddSIngo Weinhold 	// free all magazines
316453a2bddSIngo Weinhold 
317453a2bddSIngo Weinhold 	while (storeMagazines != NULL)
318*86c794e5SIngo Weinhold 		empty_magazine(depot, _pop(storeMagazines), flags);
319453a2bddSIngo Weinhold 
320453a2bddSIngo Weinhold 	while (fullMagazines != NULL)
321*86c794e5SIngo Weinhold 		empty_magazine(depot, _pop(fullMagazines), flags);
322453a2bddSIngo Weinhold 
323453a2bddSIngo Weinhold 	while (emptyMagazines)
324*86c794e5SIngo Weinhold 		free_magazine(_pop(emptyMagazines), flags);
325a8806e5eSIngo Weinhold }
326