xref: /haiku/src/system/kernel/slab/ObjectDepot.cpp (revision a8806e5e0dad28238343bd4dcc8d035657d358ff)
1*a8806e5eSIngo Weinhold /*
2*a8806e5eSIngo Weinhold  * Copyright 2008, Axel Dörfler. All Rights Reserved.
3*a8806e5eSIngo Weinhold  * Copyright 2007, Hugo Santos. All Rights Reserved.
4*a8806e5eSIngo Weinhold  *
5*a8806e5eSIngo Weinhold  * Distributed under the terms of the MIT License.
6*a8806e5eSIngo Weinhold  */
7*a8806e5eSIngo Weinhold 
8*a8806e5eSIngo Weinhold 
9*a8806e5eSIngo Weinhold #include <slab/ObjectDepot.h>
10*a8806e5eSIngo Weinhold 
11*a8806e5eSIngo Weinhold #include <algorithm>
12*a8806e5eSIngo Weinhold 
13*a8806e5eSIngo Weinhold #include <util/AutoLock.h>
14*a8806e5eSIngo Weinhold 
15*a8806e5eSIngo Weinhold #include "slab_private.h"
16*a8806e5eSIngo Weinhold 
17*a8806e5eSIngo Weinhold 
18*a8806e5eSIngo Weinhold static const int kMagazineCapacity = 32;
19*a8806e5eSIngo Weinhold 	// TODO: Should be dynamically tuned per cache.
20*a8806e5eSIngo Weinhold 
21*a8806e5eSIngo Weinhold 
22*a8806e5eSIngo Weinhold struct depot_magazine {
23*a8806e5eSIngo Weinhold 	struct depot_magazine *next;
24*a8806e5eSIngo Weinhold 	uint16 current_round, round_count;
25*a8806e5eSIngo Weinhold 	void *rounds[0];
26*a8806e5eSIngo Weinhold };
27*a8806e5eSIngo Weinhold 
28*a8806e5eSIngo Weinhold 
29*a8806e5eSIngo Weinhold struct depot_cpu_store {
30*a8806e5eSIngo Weinhold 	recursive_lock lock;
31*a8806e5eSIngo Weinhold 	struct depot_magazine *loaded, *previous;
32*a8806e5eSIngo Weinhold };
33*a8806e5eSIngo Weinhold 
34*a8806e5eSIngo Weinhold 
35*a8806e5eSIngo Weinhold static inline bool
36*a8806e5eSIngo Weinhold is_magazine_empty(depot_magazine *magazine)
37*a8806e5eSIngo Weinhold {
38*a8806e5eSIngo Weinhold 	return magazine->current_round == 0;
39*a8806e5eSIngo Weinhold }
40*a8806e5eSIngo Weinhold 
41*a8806e5eSIngo Weinhold 
42*a8806e5eSIngo Weinhold static inline bool
43*a8806e5eSIngo Weinhold is_magazine_full(depot_magazine *magazine)
44*a8806e5eSIngo Weinhold {
45*a8806e5eSIngo Weinhold 	return magazine->current_round == magazine->round_count;
46*a8806e5eSIngo Weinhold }
47*a8806e5eSIngo Weinhold 
48*a8806e5eSIngo Weinhold 
49*a8806e5eSIngo Weinhold static inline void *
50*a8806e5eSIngo Weinhold pop_magazine(depot_magazine *magazine)
51*a8806e5eSIngo Weinhold {
52*a8806e5eSIngo Weinhold 	return magazine->rounds[--magazine->current_round];
53*a8806e5eSIngo Weinhold }
54*a8806e5eSIngo Weinhold 
55*a8806e5eSIngo Weinhold 
56*a8806e5eSIngo Weinhold static inline bool
57*a8806e5eSIngo Weinhold push_magazine(depot_magazine *magazine, void *object)
58*a8806e5eSIngo Weinhold {
59*a8806e5eSIngo Weinhold 	if (is_magazine_full(magazine))
60*a8806e5eSIngo Weinhold 		return false;
61*a8806e5eSIngo Weinhold 	magazine->rounds[magazine->current_round++] = object;
62*a8806e5eSIngo Weinhold 	return true;
63*a8806e5eSIngo Weinhold }
64*a8806e5eSIngo Weinhold 
65*a8806e5eSIngo Weinhold 
66*a8806e5eSIngo Weinhold static depot_magazine *
67*a8806e5eSIngo Weinhold alloc_magazine()
68*a8806e5eSIngo Weinhold {
69*a8806e5eSIngo Weinhold 	depot_magazine *magazine = (depot_magazine *)internal_alloc(
70*a8806e5eSIngo Weinhold 		sizeof(depot_magazine) + kMagazineCapacity * sizeof(void *), 0);
71*a8806e5eSIngo Weinhold 	if (magazine) {
72*a8806e5eSIngo Weinhold 		magazine->next = NULL;
73*a8806e5eSIngo Weinhold 		magazine->current_round = 0;
74*a8806e5eSIngo Weinhold 		magazine->round_count = kMagazineCapacity;
75*a8806e5eSIngo Weinhold 	}
76*a8806e5eSIngo Weinhold 
77*a8806e5eSIngo Weinhold 	return magazine;
78*a8806e5eSIngo Weinhold }
79*a8806e5eSIngo Weinhold 
80*a8806e5eSIngo Weinhold 
81*a8806e5eSIngo Weinhold static void
82*a8806e5eSIngo Weinhold free_magazine(depot_magazine *magazine)
83*a8806e5eSIngo Weinhold {
84*a8806e5eSIngo Weinhold 	internal_free(magazine);
85*a8806e5eSIngo Weinhold }
86*a8806e5eSIngo Weinhold 
87*a8806e5eSIngo Weinhold 
88*a8806e5eSIngo Weinhold static void
89*a8806e5eSIngo Weinhold empty_magazine(object_depot *depot, depot_magazine *magazine)
90*a8806e5eSIngo Weinhold {
91*a8806e5eSIngo Weinhold 	for (uint16 i = 0; i < magazine->current_round; i++)
92*a8806e5eSIngo Weinhold 		depot->return_object(depot, magazine->rounds[i]);
93*a8806e5eSIngo Weinhold 	free_magazine(magazine);
94*a8806e5eSIngo Weinhold }
95*a8806e5eSIngo Weinhold 
96*a8806e5eSIngo Weinhold 
97*a8806e5eSIngo Weinhold static bool
98*a8806e5eSIngo Weinhold exchange_with_full(object_depot *depot, depot_magazine* &magazine)
99*a8806e5eSIngo Weinhold {
100*a8806e5eSIngo Weinhold 	RecursiveLocker _(depot->lock);
101*a8806e5eSIngo Weinhold 
102*a8806e5eSIngo Weinhold 	if (depot->full == NULL)
103*a8806e5eSIngo Weinhold 		return false;
104*a8806e5eSIngo Weinhold 
105*a8806e5eSIngo Weinhold 	depot->full_count--;
106*a8806e5eSIngo Weinhold 	depot->empty_count++;
107*a8806e5eSIngo Weinhold 
108*a8806e5eSIngo Weinhold 	_push(depot->empty, magazine);
109*a8806e5eSIngo Weinhold 	magazine = _pop(depot->full);
110*a8806e5eSIngo Weinhold 	return true;
111*a8806e5eSIngo Weinhold }
112*a8806e5eSIngo Weinhold 
113*a8806e5eSIngo Weinhold 
114*a8806e5eSIngo Weinhold static bool
115*a8806e5eSIngo Weinhold exchange_with_empty(object_depot *depot, depot_magazine* &magazine)
116*a8806e5eSIngo Weinhold {
117*a8806e5eSIngo Weinhold 	RecursiveLocker _(depot->lock);
118*a8806e5eSIngo Weinhold 
119*a8806e5eSIngo Weinhold 	if (depot->empty == NULL) {
120*a8806e5eSIngo Weinhold 		depot->empty = alloc_magazine();
121*a8806e5eSIngo Weinhold 		if (depot->empty == NULL)
122*a8806e5eSIngo Weinhold 			return false;
123*a8806e5eSIngo Weinhold 	} else {
124*a8806e5eSIngo Weinhold 		depot->empty_count--;
125*a8806e5eSIngo Weinhold 	}
126*a8806e5eSIngo Weinhold 
127*a8806e5eSIngo Weinhold 	if (magazine) {
128*a8806e5eSIngo Weinhold 		_push(depot->full, magazine);
129*a8806e5eSIngo Weinhold 		depot->full_count++;
130*a8806e5eSIngo Weinhold 	}
131*a8806e5eSIngo Weinhold 
132*a8806e5eSIngo Weinhold 	magazine = _pop(depot->empty);
133*a8806e5eSIngo Weinhold 	return true;
134*a8806e5eSIngo Weinhold }
135*a8806e5eSIngo Weinhold 
136*a8806e5eSIngo Weinhold 
137*a8806e5eSIngo Weinhold static inline depot_cpu_store *
138*a8806e5eSIngo Weinhold object_depot_cpu(object_depot *depot)
139*a8806e5eSIngo Weinhold {
140*a8806e5eSIngo Weinhold 	return &depot->stores[smp_get_current_cpu()];
141*a8806e5eSIngo Weinhold }
142*a8806e5eSIngo Weinhold 
143*a8806e5eSIngo Weinhold 
144*a8806e5eSIngo Weinhold // #pragma mark -
145*a8806e5eSIngo Weinhold 
146*a8806e5eSIngo Weinhold 
147*a8806e5eSIngo Weinhold status_t
148*a8806e5eSIngo Weinhold object_depot_init(object_depot *depot, uint32 flags,
149*a8806e5eSIngo Weinhold 	void (*return_object)(object_depot *depot, void *object))
150*a8806e5eSIngo Weinhold {
151*a8806e5eSIngo Weinhold 	depot->full = NULL;
152*a8806e5eSIngo Weinhold 	depot->empty = NULL;
153*a8806e5eSIngo Weinhold 	depot->full_count = depot->empty_count = 0;
154*a8806e5eSIngo Weinhold 
155*a8806e5eSIngo Weinhold 	recursive_lock_init(&depot->lock, "depot");
156*a8806e5eSIngo Weinhold 
157*a8806e5eSIngo Weinhold 	depot->stores = (depot_cpu_store *)internal_alloc(sizeof(depot_cpu_store)
158*a8806e5eSIngo Weinhold 		* smp_get_num_cpus(), flags);
159*a8806e5eSIngo Weinhold 	if (depot->stores == NULL) {
160*a8806e5eSIngo Weinhold 		recursive_lock_destroy(&depot->lock);
161*a8806e5eSIngo Weinhold 		return B_NO_MEMORY;
162*a8806e5eSIngo Weinhold 	}
163*a8806e5eSIngo Weinhold 
164*a8806e5eSIngo Weinhold 	for (int i = 0; i < smp_get_num_cpus(); i++) {
165*a8806e5eSIngo Weinhold 		recursive_lock_init(&depot->stores[i].lock, "cpu store");
166*a8806e5eSIngo Weinhold 		depot->stores[i].loaded = depot->stores[i].previous = NULL;
167*a8806e5eSIngo Weinhold 	}
168*a8806e5eSIngo Weinhold 
169*a8806e5eSIngo Weinhold 	depot->return_object = return_object;
170*a8806e5eSIngo Weinhold 
171*a8806e5eSIngo Weinhold 	return B_OK;
172*a8806e5eSIngo Weinhold }
173*a8806e5eSIngo Weinhold 
174*a8806e5eSIngo Weinhold 
175*a8806e5eSIngo Weinhold void
176*a8806e5eSIngo Weinhold object_depot_destroy(object_depot *depot)
177*a8806e5eSIngo Weinhold {
178*a8806e5eSIngo Weinhold 	object_depot_make_empty(depot);
179*a8806e5eSIngo Weinhold 
180*a8806e5eSIngo Weinhold 	for (int i = 0; i < smp_get_num_cpus(); i++) {
181*a8806e5eSIngo Weinhold 		recursive_lock_destroy(&depot->stores[i].lock);
182*a8806e5eSIngo Weinhold 	}
183*a8806e5eSIngo Weinhold 
184*a8806e5eSIngo Weinhold 	internal_free(depot->stores);
185*a8806e5eSIngo Weinhold 
186*a8806e5eSIngo Weinhold 	recursive_lock_destroy(&depot->lock);
187*a8806e5eSIngo Weinhold }
188*a8806e5eSIngo Weinhold 
189*a8806e5eSIngo Weinhold 
190*a8806e5eSIngo Weinhold static void *
191*a8806e5eSIngo Weinhold object_depot_obtain_from_store(object_depot *depot, depot_cpu_store *store)
192*a8806e5eSIngo Weinhold {
193*a8806e5eSIngo Weinhold 	RecursiveLocker _(store->lock);
194*a8806e5eSIngo Weinhold 
195*a8806e5eSIngo Weinhold 	// To better understand both the Alloc() and Free() logic refer to
196*a8806e5eSIngo Weinhold 	// Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings]
197*a8806e5eSIngo Weinhold 
198*a8806e5eSIngo Weinhold 	// In a nutshell, we try to get an object from the loaded magazine
199*a8806e5eSIngo Weinhold 	// if it's not empty, or from the previous magazine if it's full
200*a8806e5eSIngo Weinhold 	// and finally from the Slab if the magazine depot has no full magazines.
201*a8806e5eSIngo Weinhold 
202*a8806e5eSIngo Weinhold 	if (store->loaded == NULL)
203*a8806e5eSIngo Weinhold 		return NULL;
204*a8806e5eSIngo Weinhold 
205*a8806e5eSIngo Weinhold 	while (true) {
206*a8806e5eSIngo Weinhold 		if (!is_magazine_empty(store->loaded))
207*a8806e5eSIngo Weinhold 			return pop_magazine(store->loaded);
208*a8806e5eSIngo Weinhold 
209*a8806e5eSIngo Weinhold 		if (store->previous && (is_magazine_full(store->previous)
210*a8806e5eSIngo Weinhold 			|| exchange_with_full(depot, store->previous)))
211*a8806e5eSIngo Weinhold 			std::swap(store->previous, store->loaded);
212*a8806e5eSIngo Weinhold 		else
213*a8806e5eSIngo Weinhold 			return NULL;
214*a8806e5eSIngo Weinhold 	}
215*a8806e5eSIngo Weinhold }
216*a8806e5eSIngo Weinhold 
217*a8806e5eSIngo Weinhold 
218*a8806e5eSIngo Weinhold static int
219*a8806e5eSIngo Weinhold object_depot_return_to_store(object_depot *depot, depot_cpu_store *store,
220*a8806e5eSIngo Weinhold 	void *object)
221*a8806e5eSIngo Weinhold {
222*a8806e5eSIngo Weinhold 	RecursiveLocker _(store->lock);
223*a8806e5eSIngo Weinhold 
224*a8806e5eSIngo Weinhold 	// We try to add the object to the loaded magazine if we have one
225*a8806e5eSIngo Weinhold 	// and it's not full, or to the previous one if it is empty. If
226*a8806e5eSIngo Weinhold 	// the magazine depot doesn't provide us with a new empty magazine
227*a8806e5eSIngo Weinhold 	// we return the object directly to the slab.
228*a8806e5eSIngo Weinhold 
229*a8806e5eSIngo Weinhold 	while (true) {
230*a8806e5eSIngo Weinhold 		if (store->loaded && push_magazine(store->loaded, object))
231*a8806e5eSIngo Weinhold 			return 1;
232*a8806e5eSIngo Weinhold 
233*a8806e5eSIngo Weinhold 		if ((store->previous && is_magazine_empty(store->previous))
234*a8806e5eSIngo Weinhold 			|| exchange_with_empty(depot, store->previous))
235*a8806e5eSIngo Weinhold 			std::swap(store->loaded, store->previous);
236*a8806e5eSIngo Weinhold 		else
237*a8806e5eSIngo Weinhold 			return 0;
238*a8806e5eSIngo Weinhold 	}
239*a8806e5eSIngo Weinhold }
240*a8806e5eSIngo Weinhold 
241*a8806e5eSIngo Weinhold 
242*a8806e5eSIngo Weinhold void *
243*a8806e5eSIngo Weinhold object_depot_obtain(object_depot *depot)
244*a8806e5eSIngo Weinhold {
245*a8806e5eSIngo Weinhold 	return object_depot_obtain_from_store(depot, object_depot_cpu(depot));
246*a8806e5eSIngo Weinhold }
247*a8806e5eSIngo Weinhold 
248*a8806e5eSIngo Weinhold 
249*a8806e5eSIngo Weinhold int
250*a8806e5eSIngo Weinhold object_depot_store(object_depot *depot, void *object)
251*a8806e5eSIngo Weinhold {
252*a8806e5eSIngo Weinhold 	return object_depot_return_to_store(depot, object_depot_cpu(depot),
253*a8806e5eSIngo Weinhold 		object);
254*a8806e5eSIngo Weinhold }
255*a8806e5eSIngo Weinhold 
256*a8806e5eSIngo Weinhold 
257*a8806e5eSIngo Weinhold void
258*a8806e5eSIngo Weinhold object_depot_make_empty(object_depot *depot)
259*a8806e5eSIngo Weinhold {
260*a8806e5eSIngo Weinhold 	for (int i = 0; i < smp_get_num_cpus(); i++) {
261*a8806e5eSIngo Weinhold 		depot_cpu_store *store = &depot->stores[i];
262*a8806e5eSIngo Weinhold 
263*a8806e5eSIngo Weinhold 		RecursiveLocker _(store->lock);
264*a8806e5eSIngo Weinhold 
265*a8806e5eSIngo Weinhold 		if (depot->stores[i].loaded)
266*a8806e5eSIngo Weinhold 			empty_magazine(depot, depot->stores[i].loaded);
267*a8806e5eSIngo Weinhold 		if (depot->stores[i].previous)
268*a8806e5eSIngo Weinhold 			empty_magazine(depot, depot->stores[i].previous);
269*a8806e5eSIngo Weinhold 		depot->stores[i].loaded = depot->stores[i].previous = NULL;
270*a8806e5eSIngo Weinhold 	}
271*a8806e5eSIngo Weinhold 
272*a8806e5eSIngo Weinhold 	RecursiveLocker _(depot->lock);
273*a8806e5eSIngo Weinhold 
274*a8806e5eSIngo Weinhold 	while (depot->full)
275*a8806e5eSIngo Weinhold 		empty_magazine(depot, _pop(depot->full));
276*a8806e5eSIngo Weinhold 
277*a8806e5eSIngo Weinhold 	while (depot->empty)
278*a8806e5eSIngo Weinhold 		empty_magazine(depot, _pop(depot->empty));
279*a8806e5eSIngo Weinhold }
280