The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
slab.c
Go to the documentation of this file.
1 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <stdint.h>
33 #include <stddef.h>
34 #include <sys/mman.h>
35 #include <math.h>
36 #include <assert.h>
37 
38 #include <core/core.h>
39 #include <mm/mm.h>
40 
41 #define SLOTS_ALL_ZERO ((uint64_t) 0)
42 #define SLOTS_FIRST ((uint64_t) 1)
43 #define FIRST_FREE_SLOT(s) ((size_t) __builtin_ctzll(s))
44 #define FREE_SLOTS(s) ((size_t) __builtin_popcountll(s))
45 #define ONE_USED_SLOT(slots, empty_slotmask) \
46  ( \
47  ( \
48  (~(slots) & (empty_slotmask)) & \
49  ((~(slots) & (empty_slotmask)) - 1) \
50  ) == SLOTS_ALL_ZERO \
51  )
52 
53 #ifndef NDEBUG
54 static int slab_is_valid(const struct slab_chain *const sch)
55 {
56  assert(POWEROF2(PAGE_SIZE));
57  assert(POWEROF2(sch->slabsize));
58  assert(POWEROF2(sch->pages_per_alloc));
59 
60  assert(sch->itemcount >= 2 && sch->itemcount <= 64);
61  assert(sch->itemsize >= 1 && sch->itemsize <= SIZE_MAX);
62  assert(sch->pages_per_alloc >= PAGE_SIZE);
63  assert(sch->pages_per_alloc >= sch->slabsize);
64 
65  assert(offsetof(struct slab_header, data) + sch->itemsize * sch->itemcount <= sch->slabsize);
66 
67  assert(sch->empty_slotmask == ~SLOTS_ALL_ZERO >> (64 - sch->itemcount));
68  assert(sch->initial_slotmask == (sch->empty_slotmask ^ SLOTS_FIRST));
69  assert(sch->alignment_mask == ~(sch->slabsize - 1));
70 
71  const struct slab_header *const heads[] =
72  { sch->full, sch->empty, sch->partial };
73 
74  for (size_t head = 0; head < 3; ++head) {
75  const struct slab_header *prev = NULL, *slab;
76 
77  for (slab = heads[head]; slab != NULL; slab = slab->next) {
78  if (prev == NULL)
79  assert(slab->prev == NULL);
80  else
81  assert(slab->prev == prev);
82 
83  switch (head) {
84  case 0:
85  assert(slab->slots == SLOTS_ALL_ZERO);
86  break;
87 
88  case 1:
89  assert(slab->slots == sch->empty_slotmask);
90  break;
91 
92  case 2:
93  assert((slab->slots & ~sch->empty_slotmask) == SLOTS_ALL_ZERO);
94  assert(FREE_SLOTS(slab->slots) >= 1);
95  assert(FREE_SLOTS(slab->slots) < sch->itemcount);
96  break;
97  }
98 
99  if (slab->refcount == 0) {
100  assert((uintptr_t) slab % sch->slabsize == 0);
101 
102  if (sch->slabsize >= PAGE_SIZE)
103  assert((uintptr_t) slab->page % sch->slabsize == 0);
104  else
105  assert((uintptr_t) slab->page % PAGE_SIZE == 0);
106  } else {
107  if (sch->slabsize >= PAGE_SIZE)
108  assert((uintptr_t) slab % sch->slabsize == 0);
109  else
110  assert((uintptr_t) slab % PAGE_SIZE == 0);
111  }
112 
113  prev = slab;
114  }
115  }
116 
117  return 1;
118 }
119 #endif
120 
121 struct slab_chain *slab_init(const size_t itemsize)
122 {
123  assert(itemsize >= 1 && itemsize <= SIZE_MAX);
124  assert(POWEROF2(PAGE_SIZE));
125 
126  struct slab_chain *sch = rsalloc(sizeof(struct slab_chain));
127  sch->itemsize = itemsize;
128  spinlock_init(&sch->lock);
129 
130  const size_t data_offset = offsetof(struct slab_header, data);
131  const size_t least_slabsize = data_offset + 64 * sch->itemsize;
132  sch->slabsize = (size_t)1 << (size_t)ceil(log2(least_slabsize));
133  sch->itemcount = 64;
134 
135  if (sch->slabsize - least_slabsize != 0) {
136  const size_t shrinked_slabsize = sch->slabsize >> 1;
137 
138  if (data_offset < shrinked_slabsize && shrinked_slabsize - data_offset >= 2 * sch->itemsize) {
139  sch->slabsize = shrinked_slabsize;
140  sch->itemcount = (shrinked_slabsize - data_offset) / sch->itemsize;
141  }
142  }
143 
144  sch->pages_per_alloc = sch->slabsize > PAGE_SIZE ? sch->slabsize : PAGE_SIZE;
145 
146  sch->empty_slotmask = ~SLOTS_ALL_ZERO >> (64 - sch->itemcount);
147  sch->initial_slotmask = sch->empty_slotmask ^ SLOTS_FIRST;
148  sch->alignment_mask = ~(sch->slabsize - 1);
149  sch->partial = sch->empty = sch->full = NULL;
150 
151  assert(slab_is_valid(sch));
152 
153  return sch;
154 }
155 
156 void *slab_alloc(struct slab_chain *const sch)
157 {
158  void *ret = NULL;
159  assert(sch != NULL);
160 
161  spin_lock(&sch->lock);
162 
163  assert(slab_is_valid(sch));
164 
165  if (likely(sch->partial != NULL)) {
166  /* found a partial slab, locate the first free slot */
167  register const size_t slot = FIRST_FREE_SLOT(sch->partial->slots);
168  sch->partial->slots ^= SLOTS_FIRST << slot;
169 
170  if (unlikely(sch->partial->slots == SLOTS_ALL_ZERO)) {
171  /* slab has become full, change state from partial to full */
172  struct slab_header *const tmp = sch->partial;
173 
174  /* skip first slab from partial list */
175  if (likely((sch->partial = sch->partial->next) != NULL))
176  sch->partial->prev = NULL;
177 
178  if (likely((tmp->next = sch->full) != NULL))
179  sch->full->prev = tmp;
180 
181  sch->full = tmp;
182  ret = sch->full->data + slot * sch->itemsize;
183  goto out;
184  } else {
185  ret = sch->partial->data + slot * sch->itemsize;
186  goto out;
187  }
188  } else if (likely((sch->partial = sch->empty) != NULL)) {
189  /* found an empty slab, change state from empty to partial */
190  if (likely((sch->empty = sch->empty->next) != NULL))
191  sch->empty->prev = NULL;
192 
193  sch->partial->next = NULL;
194 
195  /* slab is located either at the beginning of page, or beyond */
196  unlikely(sch->partial->refcount != 0) ? sch->partial->refcount++ : sch->partial->page->refcount++;
197 
198  sch->partial->slots = sch->initial_slotmask;
199  ret = sch->partial->data;
200  goto out;
201  } else {
202  /* no empty or partial slabs available, create a new one */
203  if (sch->slabsize <= PAGE_SIZE) {
204  sch->partial = mmap(NULL, sch->pages_per_alloc, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
205 
206  if (unlikely(sch->partial == MAP_FAILED)) {
207  perror("mmap");
208  ret = sch->partial = NULL;
209  goto out;
210  }
211  } else {
212  const int err = posix_memalign((void **)&sch->partial, sch->slabsize, sch->pages_per_alloc);
213 
214  if (unlikely(err != 0)) {
215  fprintf(stderr, "posix_memalign(align=%zu, size=%zu): %d\n", sch->slabsize, sch->pages_per_alloc, err);
216 
217  ret = sch->partial = NULL;
218  goto out;
219  }
220  }
221 
222  struct slab_header *prev = NULL;
223 
224  const char *const page_end = (char *)sch->partial + sch->pages_per_alloc;
225 
226  union {
227  const char *c;
228  struct slab_header *const s;
229  } curr = {
230  .c = (const char *)sch->partial + sch->slabsize
231  };
232 
233  __builtin_prefetch(sch->partial, 1);
234 
235  sch->partial->prev = sch->partial->next = NULL;
236  sch->partial->refcount = 1;
237  sch->partial->slots = sch->initial_slotmask;
238 
239  if (likely(curr.c != page_end)) {
240  curr.s->prev = NULL;
241  curr.s->refcount = 0;
242  curr.s->page = sch->partial;
243  curr.s->slots = sch->empty_slotmask;
244  sch->empty = prev = curr.s;
245 
246  while (likely((curr.c += sch->slabsize) != page_end)) {
247  prev->next = curr.s;
248  curr.s->prev = prev;
249  curr.s->refcount = 0;
250  curr.s->page = sch->partial;
251  curr.s->slots = sch->empty_slotmask;
252  prev = curr.s;
253  }
254 
255  prev->next = NULL;
256  }
257 
258  ret = sch->partial->data;
259  }
260 
261  out:
262  spin_unlock(&sch->lock);
263  return ret;
264 }
265 
266 void slab_free(struct slab_chain *const sch, const void *const addr)
267 {
268  assert(sch != NULL);
269  spin_lock(&sch->lock);
270  assert(slab_is_valid(sch));
271 
272  if (addr == NULL)
273  goto out;
274 
275  struct slab_header *const slab = (void *)
276  ((uintptr_t) addr & sch->alignment_mask);
277 
278  register const int slot = ((char *)addr - (char *)slab - offsetof(struct slab_header, data)) / sch->itemsize;
279 
280  if (unlikely(slab->slots == SLOTS_ALL_ZERO)) {
281  /* target slab is full, change state to partial */
282  slab->slots = SLOTS_FIRST << slot;
283 
284  if (likely(slab != sch->full)) {
285  if (likely((slab->prev->next = slab->next) != NULL))
286  slab->next->prev = slab->prev;
287 
288  slab->prev = NULL;
289  } else if (likely((sch->full = sch->full->next) != NULL)) {
290  sch->full->prev = NULL;
291  }
292 
293  slab->next = sch->partial;
294 
295  if (likely(sch->partial != NULL))
296  sch->partial->prev = slab;
297 
298  sch->partial = slab;
299  } else if (unlikely(ONE_USED_SLOT(slab->slots, sch->empty_slotmask))) {
300  /* target slab is partial and has only one filled slot */
301  if (unlikely(slab->refcount == 1 || (slab->refcount == 0 && slab->page->refcount == 1))) {
302 
303  /* unmap the whole page if this slab is the only partial one */
304  if (likely(slab != sch->partial)) {
305  if (likely((slab->prev->next = slab->next) != NULL))
306  slab->next->prev = slab->prev;
307  } else
308  if (likely((sch->partial = sch->partial->next) != NULL)) {
309  sch->partial->prev = NULL;
310  }
311 
312  void *const page = unlikely(slab->refcount != 0) ? slab : slab->page;
313  const char *const page_end = (char *)page + sch->pages_per_alloc;
314  char found_head = 0;
315 
316  union {
317  const char *c;
318  const struct slab_header *const s;
319  } s;
320 
321  for (s.c = page; s.c != page_end; s.c += sch->slabsize) {
322  if (unlikely(s.s == sch->empty))
323  found_head = 1;
324  else if (unlikely(s.s == slab))
325  continue;
326  else if (likely((s.s->prev->next = s.s->next) != NULL))
327  s.s->next->prev = s.s->prev;
328  }
329 
330  if (unlikely(found_head && (sch->empty = sch->empty->next) != NULL))
331  sch->empty->prev = NULL;
332 
333  if (sch->slabsize <= PAGE_SIZE) {
334  if (unlikely(munmap(page, sch->pages_per_alloc) == -1))
335  perror("munmap");
336  } else {
337  rsfree(page);
338  }
339  } else {
340  slab->slots = sch->empty_slotmask;
341 
342  if (likely(slab != sch->partial)) {
343  if (likely((slab->prev->next = slab->next) != NULL))
344  slab->next->prev = slab->prev;
345 
346  slab->prev = NULL;
347  } else
348  if (likely((sch->partial = sch->partial->next) != NULL)) {
349  sch->partial->prev = NULL;
350  }
351 
352  slab->next = sch->empty;
353 
354  if (likely(sch->empty != NULL))
355  sch->empty->prev = slab;
356 
357  sch->empty = slab;
358 
359  unlikely(slab->refcount != 0) ? slab->refcount-- : slab->page->refcount--;
360  }
361  } else {
362  /* target slab is partial, no need to change state */
363  slab->slots |= SLOTS_FIRST << slot;
364  }
365 
366  out:
367  spin_unlock(&sch->lock);
368 }
369 
370 void slab_destroy(const struct slab_chain *const sch)
371 {
372  assert(sch != NULL);
373  assert(slab_is_valid(sch));
374 
375  struct slab_header *const heads[] = { sch->partial, sch->empty, sch->full };
376  struct slab_header *pages_head = NULL, *pages_tail;
377 
378  for (size_t i = 0; i < 3; ++i) {
379  struct slab_header *slab = heads[i];
380 
381  while (slab != NULL) {
382  if (slab->refcount != 0) {
383  struct slab_header *const page = slab;
384  slab = slab->next;
385 
386  if (unlikely(pages_head == NULL))
387  pages_head = page;
388  else
389  pages_tail->next = page;
390 
391  pages_tail = page;
392  } else {
393  slab = slab->next;
394  }
395  }
396  }
397 
398  if (likely(pages_head != NULL)) {
399  pages_tail->next = NULL;
400  struct slab_header *page = pages_head;
401 
402  if (sch->slabsize <= PAGE_SIZE) {
403  do {
404  void *const target = page;
405  page = page->next;
406 
407  if (unlikely(munmap(target, sch->pages_per_alloc) == -1))
408  perror("munmap");
409  } while (page != NULL);
410  } else {
411  do {
412  void *const target = page;
413  page = page->next;
414  rsfree(target);
415  } while (page != NULL);
416  }
417  }
418 }
#define likely(exp)
Optimize the branch as likely taken.
Definition: core.h:72
Definition: mm.h:57
#define spinlock_init(s)
Spinlock initialization.
Definition: atomic.h:72
Core ROOT-Sim functionalities.
Memory Manager main header.
void spin_lock(spinlock_t *s)
Definition: x86.c:135
Definition: mm.h:68
#define unlikely(exp)
Optimize the branch as likely not taken.
Definition: core.h:74
void spin_unlock(spinlock_t *s)
Definition: x86.c:161