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