]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/lib/libk/slab_cache_anon.cpp
30fce0cdec505e7fd702ca8d275042ee6e9d9e13
[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 inline 
161 slab::~slab()
162 {
163   assert(_data._in_use == 0);
164
165   slab_entry *e = _data._first_free;
166
167   while (e)
168     {
169       _data._cache->elem_dtor(& e->_entry[0]);
170       e = e->_next_free;
171     }
172 }
173
174 PUBLIC
175 void *
176 slab::alloc()
177 {
178   slab_entry *e = _data._first_free;
179
180   if (! e)
181     return 0;
182
183   _data._first_free = e->_next_free;
184   _data._in_use ++;
185   _data._free --;
186     
187   return & e->_entry;
188 }
189
190 PUBLIC
191 void
192 slab::free(void *entry)
193 {
194   slab_entry *e = reinterpret_cast<slab_entry *>
195     (reinterpret_cast<char*>(entry) - offsetof(slab_entry, _entry));
196
197   e->_next_free = _data._first_free;
198   _data._first_free = e;
199
200   assert(_data._in_use);
201   _data._in_use --;
202   _data._free ++;
203 }
204
205 PUBLIC
206 inline bool
207 slab::is_empty()
208 {
209   return _data._in_use == 0;
210 }
211
212 PUBLIC
213 inline bool
214 slab::is_full()
215 {
216   return _data._free == 0;
217 }
218
219 PUBLIC
220 inline unsigned
221 slab::in_use()
222 {
223   return _data._in_use;
224 }
225
226 PUBLIC
227 void
228 slab::enqueue(slab *prev)
229 {
230   assert(prev);
231
232   if ((_data._next = prev->_data._next))
233     _data._next->_data._prev = this;
234   _data._prev = prev;
235   _data._prev->_data._next = this;
236 }
237
238 PUBLIC
239 void
240 slab::dequeue()
241 {
242   if (_data._prev)
243     _data._prev->_data._next = _data._next;
244   if (_data._next)
245     _data._next->_data._prev = _data._prev;
246
247   _data._prev = _data._next = 0;
248 }
249
250 PUBLIC
251 inline slab *
252 slab::prev()
253 {
254   return _data._prev;
255 }
256
257 PUBLIC
258 inline slab *
259 slab::next()
260 {
261   return _data._next;
262 }
263
264 PUBLIC
265 inline void *
266 slab::operator new(size_t,
267                    slab_cache_anon *cache) throw()
268 {
269   // slabs must be size-aligned so that we can compute their addresses
270   // from element addresses
271   return cache->block_alloc(cache->_slab_size, cache->_slab_size);
272 }
273
274 PUBLIC static
275 unsigned
276 slab::num_elems(unsigned long slab_size,
277     unsigned elem_size,
278     unsigned alignment)
279 {
280   // Compute offset of first slab_entry in slab, not taking into
281   // account the colorization offset.  "slab_entry._entry[]" needs to
282   // be "cache->_alignment"-aligned
283   unsigned long offset_first_elem = 
284     ((sizeof(slab::slab_data) + sizeof (slab::slab_entry) + alignment - 1) 
285      & ~(alignment - 1)) 
286     - sizeof (slab::slab_entry);
287
288   // Compute size of a slab entry, including alignment padding at end
289   // of entry
290   unsigned entry_size = 
291     (sizeof(slab::slab_entry) + elem_size + alignment - 1)
292     & ~(alignment - 1);
293
294   // Compute number of elements fitting into a slab
295   return (slab_size - offset_first_elem) / entry_size;
296 }
297
298 PUBLIC static
299 unsigned
300 slab_cache_anon::num_elems(unsigned long slab_size,
301     unsigned elem_size,
302     unsigned alignment)
303 { return slab::num_elems(slab_size, elem_size, alignment); }
304
305 // 
306 // slab_cache_anon
307 // 
308 PUBLIC inline
309 slab_cache_anon::slab_cache_anon(unsigned elem_size, 
310                                  unsigned alignment,
311                                  char const * name, 
312                                  unsigned long min_size,
313                                  unsigned long max_size)
314   : _first_slab(0), _first_available_slab(0), _last_slab(0),
315     _elem_size(elem_size), 
316     _latest_offset(0), _alignment(alignment),
317     _name (name)
318 {
319   lock.init();
320
321   for (
322       _slab_size = min_size;
323       num_elems(_slab_size, elem_size, alignment) < 8
324         && _slab_size < max_size;
325       _slab_size <<= 1) ;
326 }
327
328 // 
329 // slab_cache_anon
330 // 
331 PUBLIC inline
332 slab_cache_anon::slab_cache_anon(unsigned long slab_size, 
333                                  unsigned elem_size, 
334                                  unsigned alignment,
335                                  char const * name)
336   : _first_slab(0), _first_available_slab(0), _last_slab(0),
337     _slab_size(slab_size), _elem_size(elem_size), 
338     _latest_offset(0), _alignment(alignment),
339     _name (name)
340 {
341   lock.init();
342 }
343
344 PUBLIC
345 virtual
346 slab_cache_anon::~slab_cache_anon()
347 {
348   // the derived class should call destroy() before deleting us.
349   assert(_first_slab == 0);
350 }
351
352 PROTECTED
353 void 
354 slab_cache_anon::destroy()      // descendant should call this in destructor
355 {
356   slab *n, *s = _first_slab;
357
358   while (s)
359     {
360       n = s->next();
361
362       // explicitly call destructor to delete s;
363       s->~slab();
364       block_free(s, _slab_size);
365
366       s = n;
367     }
368
369   _first_slab = 0;
370 }
371
372 PUBLIC
373 virtual void *
374 slab_cache_anon::alloc()        // request initialized member from cache
375 {
376   Lock_guard<Lock> guard(&lock);
377
378   if (! _first_available_slab)
379     {
380       slab *s = new (this) slab(this);
381       if (! s)
382         return 0;
383
384       _first_available_slab = s;
385       
386       if (_last_slab)
387         {
388           assert(_last_slab->is_full());
389           
390           _first_available_slab->enqueue(_last_slab);
391           _last_slab = _first_available_slab;
392         }
393       else                      // this was the first slab we allocated
394         _first_slab = _last_slab = _first_available_slab;
395     }
396
397   assert(_first_available_slab 
398          && ! _first_available_slab->is_full());
399   assert(! _first_available_slab->prev()
400          || _first_available_slab->prev()->is_full());
401
402   void *ret = _first_available_slab->alloc();
403   assert(ret);
404
405   if (_first_available_slab->is_full())
406     _first_available_slab = _first_available_slab->next();
407
408   return ret;
409 }
410
411 PUBLIC template< typename Q >
412 inline
413 void *
414 slab_cache_anon::q_alloc(Q *quota)
415 {
416   if (EXPECT_FALSE(!quota->alloc(_elem_size)))
417     return 0;
418
419   void *r;
420   if (EXPECT_FALSE(!(r=alloc())))
421     {
422       quota->free(_elem_size);
423       return 0;
424     }
425
426   return r;
427 }
428
429 PUBLIC
430 virtual void 
431 slab_cache_anon::free(void *cache_entry) // return initialized member to cache
432 {
433   Lock_guard<Lock> guard(&lock);
434
435   slab *s = reinterpret_cast<slab*>
436     (reinterpret_cast<unsigned long>(cache_entry) & ~(_slab_size - 1));
437
438   bool was_full = s->is_full();
439
440   s->free(cache_entry);
441
442   if (was_full)
443     {
444       if (s->next() == 0)       // have no right neighbor?
445         {
446           assert(! _first_available_slab);
447         }
448       else if (s->next()->is_full()) // right neigbor full?
449         {
450           // We requeue to become the first non-full slab in the queue
451           // so that all full slabs are at the beginning of the queue.
452
453           if (s == _first_slab)
454             _first_slab = s->next();
455           // don't care about _first_available_slab, _last_slab --
456           // they cannot point to s because we were full and have a
457           // right neighbor
458
459           s->dequeue();
460
461           if (_first_available_slab)
462             {
463               // _first_available_slab->prev() is defined because we
464               // had a right neighbor which is full, that is,
465               // _first_available_slab wasn't our right neighbor and
466               // now isn't the first slab in the queue
467               assert(_first_available_slab->prev()->is_full());
468               s->enqueue(_first_available_slab->prev());
469             }
470           else
471             {
472               // all slabs were full
473               assert(_last_slab->is_full());
474               s->enqueue(_last_slab);
475               _last_slab = s;
476             }
477         }
478
479       _first_available_slab = s;
480
481     }
482   else if (s->is_empty())
483     {
484       if (s->next() && (! s->next()->is_empty())) // right neighbor not empty?
485         {
486           // Move to tail of list
487
488           if (s == _first_slab)
489             _first_slab = s->next();
490           if (s == _first_available_slab)
491             _first_available_slab = s->next();
492           // don't care about _last_slab because we know we have a
493           // right neighbor
494
495           s->dequeue();
496
497           s->enqueue(_last_slab);
498           _last_slab = s;
499         }
500     }
501   else
502     {
503       // We weren't either full or empty; we already had free
504       // elements.  This changes nothing in the queue, and there
505       // already must have been a _first_available_slab.
506     }
507
508   assert(_first_available_slab);
509 }
510
511 PUBLIC template< typename Q >
512 inline
513 void
514 slab_cache_anon::q_free(Q *quota, void *obj)
515 {
516   free(obj);
517   quota->free(_elem_size);
518 }
519
520 PUBLIC
521 virtual unsigned long 
522 slab_cache_anon::reap()         // request that cache returns memory to system
523 {
524   Lock_guard<Lock> guard(&lock);
525
526   if (! _first_slab)
527     return 0;                   // haven't allocated anything yet
528
529   // never delete first slab, even if it is empty
530   if (_last_slab == _first_slab
531       || (! _last_slab->is_empty()))
532     return 0;
533
534   slab *s = _last_slab;
535
536   if (_first_available_slab == s)
537     _first_available_slab = 0;
538
539   _last_slab = s->prev();
540   s->dequeue();
541
542   // explicitly call destructor to delete s;
543   s->~slab();
544   block_free(s, _slab_size);
545
546   return _slab_size;
547 }
548
549 // Element initialization/destruction
550 PROTECTED
551 virtual void 
552 slab_cache_anon::elem_ctor(void *)
553 {}
554
555 PROTECTED
556 virtual void 
557 slab_cache_anon::elem_dtor(void *)
558 {}
559
560 // Debugging output
561
562 #include <cstdio>
563
564 PUBLIC 
565 virtual void
566 slab_cache_anon::debug_dump ()
567 {
568   printf ("%s: %lu-KB slabs (",
569           _name, _slab_size / 1024);
570
571   unsigned count, total = 0, total_elems = 0;
572   slab* s = _first_slab;
573
574   for (count = 0;
575        s && s->is_full();
576        s = s->next())
577     {
578       count++;
579       total_elems += s->in_use();
580     }
581
582   total += count;
583
584   printf ("%u full, ", count);
585
586   for (count = 0;
587        s && ! s->is_empty();
588        s = s->next())
589     {
590       if (s->is_full())
591         printf ("\n*** wrongly-enqueued full slab found\n");
592       count++;
593       total_elems += s->in_use();
594     }
595
596   total += count;
597
598   printf ("%u used, ", count);
599
600   for (count = 0;
601        s;
602        s = s->next())
603     {
604       if (! s->is_empty())
605         printf ("\n*** wrongly-enqueued nonempty slab found\n");
606       count++;
607       total_elems += s->in_use();
608     }
609
610   unsigned total_used = total;
611   total += count;
612
613   printf ("%u empty = %u total) = %lu KB,\n  %u elems (size=%u, align=%u)",
614           count, total, total * _slab_size / 1024, 
615           total_elems, _elem_size, _alignment);
616
617   if (total_elems)
618     printf (", overhead = %lu B (%lu B)  = %lu%% (%lu%%) \n", 
619             total * _slab_size - total_elems * _elem_size,
620             total_used * _slab_size - total_elems * _elem_size,
621             100 - total_elems * _elem_size * 100 / (total * _slab_size),
622             100 - total_elems * _elem_size * 100 / (total_used * _slab_size));
623   else
624     printf ("\n");
625 }