]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/lib/libk/slab_cache_anon.cpp
update
[l4.git] / kernel / fiasco / src / lib / libk / slab_cache_anon.cpp
1 INTERFACE:
2
3 #include <spin_lock.h>
4
5 // The anonymous slab allocator.  You can specialize this allocator by
6 // providing your own initialization functions and your own low-level
7 // allocation functions.
8
9 class slab;
10
11 class slab_cache_anon
12 {
13 protected:
14   friend class slab;
15
16   // Low-level allocator functions:
17
18   // Allocate/free a block.  "size" is always a multiple of PAGE_SIZE.
19   virtual void *block_alloc(unsigned long size, unsigned long alignment) = 0;
20   virtual void block_free(void *block, unsigned long size) = 0;
21
22 private:
23   typedef Spin_lock Lock;
24
25   Lock lock;
26   slab_cache_anon();
27   slab_cache_anon(const slab_cache_anon&); // default constructor is undefined
28
29   //
30   // data declaration follows
31   // 
32
33   // The slabs of this cache are held in a partially-sorted
34   // doubly-linked list.  First come the fully-active slabs (all
35   // elements in use), then the partially active slabs, then empty
36   // slabs.
37   slab *_first_slab, *_first_available_slab, *_last_slab;
38   unsigned long _slab_size;
39   unsigned _elem_size, _latest_offset, _alignment;
40   char const *_name;
41 };                              // end declaration of class slab_cache
42
43 IMPLEMENTATION:
44
45 #include <cassert>
46 #include <cstddef>
47 #include <cstdlib>
48 #include <lock_guard.h>
49
50 #ifndef offsetof                // should be defined in stddef.h, but isn't
51 #define offsetof(TYPE, MEMBER) (((size_t) &((TYPE *)10)->MEMBER) - 10)
52 #endif
53
54 // 
55 // class slab
56 // 
57
58 class slab                      // a slab of the cache
59 {
60   slab();
61   slab(const slab&);    // default constructors remain undefined
62
63   struct slab_entry
64   {
65     slab_entry *_next_free;
66     char _entry[0];
67   };
68   
69   struct slab_data
70   {
71     slab_cache_anon *_cache;
72     slab_entry *_first_free;
73     slab *_next, *_prev;
74     unsigned short _in_use, _free;
75   };
76   
77   // slabs for CACHE_ENTRY should contain at least min_cache_items
78   // cache entries
79   static const unsigned min_cache_items = 4;
80   
81   // 
82   // data declaration follows 
83   // 
84   slab_data _data;
85 };                              // end declaration of class slab
86
87 // 
88 // class slab
89 //- 
90
91 // default deallocator must not be called -- must use explicit destruction
92 inline NOEXPORT
93 void 
94 slab::operator delete(void* /*block*/)
95 {
96   assert (!"slab::operator delete called");
97 }
98
99   
100 PUBLIC
101 slab::slab(slab_cache_anon *cache)
102 {
103   _data._cache = cache;
104   _data._in_use = 0;
105   _data._next = _data._prev = 0;
106
107   // Compute offset of first slab_entry in slab, not taking into
108   // account the colorization offset.  "slab_entry._entry[]" needs to
109   // be "cache->_alignment"-aligned
110   unsigned long offset_first_elem = 
111     ((sizeof(slab_data) + sizeof (slab_entry) + cache->_alignment - 1) 
112      & ~(cache->_alignment - 1)) 
113     - sizeof (slab_entry);
114
115   // Compute size of a slab entry, including alignment padding at end
116   // of entry
117   unsigned entry_size = 
118     (sizeof(slab_entry) + cache->_elem_size + cache->_alignment - 1)
119     & ~(cache->_alignment - 1);
120
121   // Compute number of elements fitting into a slab
122   unsigned elem_num = 
123     (cache->_slab_size - offset_first_elem) / entry_size;
124
125   // Compute pointer to first data element, now taking into account
126   // the latest colorization offset
127   char* data = 
128     reinterpret_cast<char*>(this) + offset_first_elem + cache->_latest_offset;
129
130   // Update the colorization offset
131   cache->_latest_offset += cache->_alignment;
132   if (offset_first_elem + cache->_latest_offset + entry_size * elem_num 
133       > cache->_slab_size)
134     {
135       cache->_latest_offset = 0;
136     }
137
138   // Initialize the cache elements
139   slab_entry *e = 0, *e_prev = 0;
140
141   for (unsigned i = 0; i < elem_num; i++)
142     {
143       e = reinterpret_cast<slab_entry *>(data);
144       
145       e->_next_free = e_prev;
146       cache->elem_ctor(& e->_entry[0]);
147       
148       e_prev = e;
149       
150       data += 
151         (sizeof(slab_entry) + cache->_elem_size + cache->_alignment - 1) 
152         & ~(cache->_alignment - 1);
153     }
154
155   _data._first_free = e;
156   _data._free = elem_num;
157 }
158
159 PUBLIC
160 void *
161 slab::alloc()
162 {
163   slab_entry *e = _data._first_free;
164
165   if (! e)
166     return 0;
167
168   _data._first_free = e->_next_free;
169   _data._in_use ++;
170   _data._free --;
171     
172   return & e->_entry;
173 }
174
175 PUBLIC
176 void
177 slab::free(void *entry)
178 {
179   slab_entry *e = reinterpret_cast<slab_entry *>
180     (reinterpret_cast<char*>(entry) - offsetof(slab_entry, _entry));
181
182   e->_next_free = _data._first_free;
183   _data._first_free = e;
184
185   assert(_data._in_use);
186   _data._in_use --;
187   _data._free ++;
188 }
189
190 PUBLIC
191 inline bool
192 slab::is_empty()
193 {
194   return _data._in_use == 0;
195 }
196
197 PUBLIC
198 inline bool
199 slab::is_full()
200 {
201   return _data._free == 0;
202 }
203
204 PUBLIC
205 inline unsigned
206 slab::in_use()
207 {
208   return _data._in_use;
209 }
210
211 PUBLIC
212 void
213 slab::enqueue(slab *prev)
214 {
215   assert(prev);
216
217   if ((_data._next = prev->_data._next))
218     _data._next->_data._prev = this;
219   _data._prev = prev;
220   _data._prev->_data._next = this;
221 }
222
223 PUBLIC
224 void
225 slab::dequeue()
226 {
227   if (_data._prev)
228     _data._prev->_data._next = _data._next;
229   if (_data._next)
230     _data._next->_data._prev = _data._prev;
231
232   _data._prev = _data._next = 0;
233 }
234
235 PUBLIC
236 inline slab *
237 slab::prev()
238 {
239   return _data._prev;
240 }
241
242 PUBLIC
243 inline slab *
244 slab::next()
245 {
246   return _data._next;
247 }
248
249 PUBLIC
250 inline void *
251 slab::operator new(size_t,
252                    slab_cache_anon *cache) throw()
253 {
254   // slabs must be size-aligned so that we can compute their addresses
255   // from element addresses
256   return cache->block_alloc(cache->_slab_size, cache->_slab_size);
257 }
258
259 PUBLIC static
260 unsigned
261 slab::num_elems(unsigned long slab_size,
262     unsigned elem_size,
263     unsigned alignment)
264 {
265   // Compute offset of first slab_entry in slab, not taking into
266   // account the colorization offset.  "slab_entry._entry[]" needs to
267   // be "cache->_alignment"-aligned
268   unsigned long offset_first_elem = 
269     ((sizeof(slab::slab_data) + sizeof (slab::slab_entry) + alignment - 1) 
270      & ~(alignment - 1)) 
271     - sizeof (slab::slab_entry);
272
273   // Compute size of a slab entry, including alignment padding at end
274   // of entry
275   unsigned entry_size = 
276     (sizeof(slab::slab_entry) + elem_size + alignment - 1)
277     & ~(alignment - 1);
278
279   // Compute number of elements fitting into a slab
280   return (slab_size - offset_first_elem) / entry_size;
281 }
282
283 PUBLIC static
284 unsigned
285 slab_cache_anon::num_elems(unsigned long slab_size,
286     unsigned elem_size,
287     unsigned alignment)
288 { return slab::num_elems(slab_size, elem_size, alignment); }
289
290 // 
291 // slab_cache_anon
292 // 
293 PUBLIC inline
294 slab_cache_anon::slab_cache_anon(unsigned elem_size, 
295                                  unsigned alignment,
296                                  char const * name, 
297                                  unsigned long min_size,
298                                  unsigned long max_size)
299   : _first_slab(0), _first_available_slab(0), _last_slab(0),
300     _elem_size(elem_size), 
301     _latest_offset(0), _alignment(alignment),
302     _name (name)
303 {
304   lock.init();
305
306   for (
307       _slab_size = min_size;
308       num_elems(_slab_size, elem_size, alignment) < 8
309         && _slab_size < max_size;
310       _slab_size <<= 1) ;
311 }
312
313 // 
314 // slab_cache_anon
315 // 
316 PUBLIC inline
317 slab_cache_anon::slab_cache_anon(unsigned long slab_size, 
318                                  unsigned elem_size, 
319                                  unsigned alignment,
320                                  char const * name)
321   : _first_slab(0), _first_available_slab(0), _last_slab(0),
322     _slab_size(slab_size), _elem_size(elem_size), 
323     _latest_offset(0), _alignment(alignment),
324     _name (name)
325 {
326   lock.init();
327 }
328
329 PUBLIC
330 virtual
331 slab_cache_anon::~slab_cache_anon()
332 {
333   // the derived class should call destroy() before deleting us.
334   // assert(_first_slab == 0);
335 }
336
337 PROTECTED inline
338 void
339 slab_cache_anon::destroy()      // descendant should call this in destructor
340 {
341 #if 0
342   slab *n, *s = _first_slab;
343
344   while (s)
345     {
346       n = s->next();
347
348       // explicitly call destructor to delete s;
349       s->~slab();
350       block_free(s, _slab_size);
351
352       s = n;
353     }
354
355   _first_slab = 0;
356 #endif
357 }
358
359 PUBLIC
360 virtual void *
361 slab_cache_anon::alloc()        // request initialized member from cache
362 {
363   Lock_guard<Lock> guard(&lock);
364
365   if (! _first_available_slab)
366     {
367       slab *s = new (this) slab(this);
368       if (! s)
369         return 0;
370
371       _first_available_slab = s;
372       
373       if (_last_slab)
374         {
375           assert(_last_slab->is_full());
376           
377           _first_available_slab->enqueue(_last_slab);
378           _last_slab = _first_available_slab;
379         }
380       else                      // this was the first slab we allocated
381         _first_slab = _last_slab = _first_available_slab;
382     }
383
384   assert(_first_available_slab 
385          && ! _first_available_slab->is_full());
386   assert(! _first_available_slab->prev()
387          || _first_available_slab->prev()->is_full());
388
389   void *ret = _first_available_slab->alloc();
390   assert(ret);
391
392   if (_first_available_slab->is_full())
393     _first_available_slab = _first_available_slab->next();
394
395   return ret;
396 }
397
398 PUBLIC template< typename Q >
399 inline
400 void *
401 slab_cache_anon::q_alloc(Q *quota)
402 {
403   if (EXPECT_FALSE(!quota->alloc(_elem_size)))
404     return 0;
405
406   void *r;
407   if (EXPECT_FALSE(!(r=alloc())))
408     {
409       quota->free(_elem_size);
410       return 0;
411     }
412
413   return r;
414 }
415
416 PUBLIC
417 virtual void 
418 slab_cache_anon::free(void *cache_entry) // return initialized member to cache
419 {
420   Lock_guard<Lock> guard(&lock);
421
422   slab *s = reinterpret_cast<slab*>
423     (reinterpret_cast<unsigned long>(cache_entry) & ~(_slab_size - 1));
424
425   bool was_full = s->is_full();
426
427   s->free(cache_entry);
428
429   if (was_full)
430     {
431       if (s->next() == 0)       // have no right neighbor?
432         {
433           assert(! _first_available_slab);
434         }
435       else if (s->next()->is_full()) // right neigbor full?
436         {
437           // We requeue to become the first non-full slab in the queue
438           // so that all full slabs are at the beginning of the queue.
439
440           if (s == _first_slab)
441             _first_slab = s->next();
442           // don't care about _first_available_slab, _last_slab --
443           // they cannot point to s because we were full and have a
444           // right neighbor
445
446           s->dequeue();
447
448           if (_first_available_slab)
449             {
450               // _first_available_slab->prev() is defined because we
451               // had a right neighbor which is full, that is,
452               // _first_available_slab wasn't our right neighbor and
453               // now isn't the first slab in the queue
454               assert(_first_available_slab->prev()->is_full());
455               s->enqueue(_first_available_slab->prev());
456             }
457           else
458             {
459               // all slabs were full
460               assert(_last_slab->is_full());
461               s->enqueue(_last_slab);
462               _last_slab = s;
463             }
464         }
465
466       _first_available_slab = s;
467
468     }
469   else if (s->is_empty())
470     {
471       if (s->next() && (! s->next()->is_empty())) // right neighbor not empty?
472         {
473           // Move to tail of list
474
475           if (s == _first_slab)
476             _first_slab = s->next();
477           if (s == _first_available_slab)
478             _first_available_slab = s->next();
479           // don't care about _last_slab because we know we have a
480           // right neighbor
481
482           s->dequeue();
483
484           s->enqueue(_last_slab);
485           _last_slab = s;
486         }
487     }
488   else
489     {
490       // We weren't either full or empty; we already had free
491       // elements.  This changes nothing in the queue, and there
492       // already must have been a _first_available_slab.
493     }
494
495   assert(_first_available_slab);
496 }
497
498 PUBLIC template< typename Q >
499 inline
500 void
501 slab_cache_anon::q_free(Q *quota, void *obj)
502 {
503   free(obj);
504   quota->free(_elem_size);
505 }
506
507 PUBLIC
508 virtual unsigned long 
509 slab_cache_anon::reap()         // request that cache returns memory to system
510 {
511   Lock_guard<Lock> guard(&lock);
512
513   if (! _first_slab)
514     return 0;                   // haven't allocated anything yet
515
516   // never delete first slab, even if it is empty
517   if (_last_slab == _first_slab
518       || (! _last_slab->is_empty()))
519     return 0;
520
521   slab *s = _last_slab;
522
523   if (_first_available_slab == s)
524     _first_available_slab = 0;
525
526   _last_slab = s->prev();
527   s->dequeue();
528
529   // explicitly call destructor to delete s;
530   s->~slab();
531   block_free(s, _slab_size);
532
533   return _slab_size;
534 }
535
536 // Element initialization/destruction
537 PROTECTED
538 virtual void 
539 slab_cache_anon::elem_ctor(void *)
540 {}
541
542 PROTECTED
543 virtual void 
544 slab_cache_anon::elem_dtor(void *)
545 {}
546
547 // Debugging output
548
549 #include <cstdio>
550
551 PUBLIC 
552 virtual void
553 slab_cache_anon::debug_dump ()
554 {
555   printf ("%s: %lu-KB slabs (",
556           _name, _slab_size / 1024);
557
558   unsigned count, total = 0, total_elems = 0;
559   slab* s = _first_slab;
560
561   for (count = 0;
562        s && s->is_full();
563        s = s->next())
564     {
565       count++;
566       total_elems += s->in_use();
567     }
568
569   total += count;
570
571   printf ("%u full, ", count);
572
573   for (count = 0;
574        s && ! s->is_empty();
575        s = s->next())
576     {
577       if (s->is_full())
578         printf ("\n*** wrongly-enqueued full slab found\n");
579       count++;
580       total_elems += s->in_use();
581     }
582
583   total += count;
584
585   printf ("%u used, ", count);
586
587   for (count = 0;
588        s;
589        s = s->next())
590     {
591       if (! s->is_empty())
592         printf ("\n*** wrongly-enqueued nonempty slab found\n");
593       count++;
594       total_elems += s->in_use();
595     }
596
597   unsigned total_used = total;
598   total += count;
599
600   printf ("%u empty = %u total) = %lu KB,\n  %u elems (size=%u, align=%u)",
601           count, total, total * _slab_size / 1024, 
602           total_elems, _elem_size, _alignment);
603
604   if (total_elems)
605     printf (", overhead = %lu B (%lu B)  = %lu%% (%lu%%) \n", 
606             total * _slab_size - total_elems * _elem_size,
607             total_used * _slab_size - total_elems * _elem_size,
608             100 - total_elems * _elem_size * 100 / (total * _slab_size),
609             100 - total_elems * _elem_size * 100 / (total_used * _slab_size));
610   else
611     printf ("\n");
612 }