xref: /memcached-1.4.29/slabs.c (revision bed5f9bb)
1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Slabs memory allocation, based on powers-of-N. Slabs are up to 1MB in size
4  * and are divided into chunks. The chunk sizes start off at the size of the
5  * "item" structure plus space for a small key and value. They increase by
6  * a multiplier factor from there, up to half the maximum slab size. The last
7  * slab size is always 1MB, since that's the maximum item size allowed by the
8  * memcached protocol.
9  */
10 #include "memcached.h"
11 #include <sys/stat.h>
12 #include <sys/socket.h>
13 #include <sys/signal.h>
14 #include <sys/resource.h>
15 #include <fcntl.h>
16 #include <netinet/in.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <assert.h>
22 #include <pthread.h>
23 
24 /* powers-of-N allocation structures */
25 
26 typedef struct {
27     unsigned int size;      /* sizes of items */
28     unsigned int perslab;   /* how many items per slab */
29 
30     void **slots;           /* list of item ptrs */
31     unsigned int sl_total;  /* size of previous array */
32     unsigned int sl_curr;   /* first free slot */
33 
34     void *end_page_ptr;         /* pointer to next free item at end of page, or 0 */
35     unsigned int end_page_free; /* number of items remaining at end of last alloced page */
36 
37     unsigned int slabs;     /* how many slabs were allocated for this class */
38 
39     void **slab_list;       /* array of slab pointers */
40     unsigned int list_size; /* size of prev array */
41 
42     unsigned int killing;  /* index+1 of dying slab, or zero if none */
43     size_t requested; /* The number of requested bytes */
44 } slabclass_t;
45 
46 static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];
47 static size_t mem_limit = 0;
48 static size_t mem_malloced = 0;
49 static int power_largest;
50 
51 static void *mem_base = NULL;
52 static void *mem_current = NULL;
53 static size_t mem_avail = 0;
54 
55 /**
56  * Access to the slab allocator is protected by this lock
57  */
58 static pthread_mutex_t slabs_lock = PTHREAD_MUTEX_INITIALIZER;
59 
60 /*
61  * Forward Declarations
62  */
63 static int do_slabs_newslab(const unsigned int id);
64 static void *memory_allocate(size_t size);
65 
66 #ifndef DONT_PREALLOC_SLABS
67 /* Preallocate as many slab pages as possible (called from slabs_init)
68    on start-up, so users don't get confused out-of-memory errors when
69    they do have free (in-slab) space, but no space to make new slabs.
70    if maxslabs is 18 (POWER_LARGEST - POWER_SMALLEST + 1), then all
71    slab types can be made.  if max memory is less than 18 MB, only the
72    smaller ones will be made.  */
73 static void slabs_preallocate (const unsigned int maxslabs);
74 #endif
75 
76 /*
77  * Figures out which slab class (chunk size) is required to store an item of
78  * a given size.
79  *
80  * Given object size, return id to use when allocating/freeing memory for object
81  * 0 means error: can't store such a large object
82  */
83 
84 unsigned int slabs_clsid(const size_t size) {
85     int res = POWER_SMALLEST;
86 
87     if (size == 0)
88         return 0;
89     while (size > slabclass[res].size)
90         if (res++ == power_largest)     /* won't fit in the biggest slab */
91             return 0;
92     return res;
93 }
94 
95 /**
96  * Determines the chunk sizes and initializes the slab class descriptors
97  * accordingly.
98  */
99 void slabs_init(const size_t limit, const double factor, const bool prealloc) {
100     int i = POWER_SMALLEST - 1;
101     unsigned int size = sizeof(item) + settings.chunk_size;
102 
103     /* Factor of 2.0 means use the default memcached behavior */
104     if (factor == 2.0 && size < 128)
105         size = 128;
106 
107     mem_limit = limit;
108 
109     if (prealloc) {
110         /* Allocate everything in a big chunk with malloc */
111         mem_base = malloc(mem_limit);
112         if (mem_base != NULL) {
113             mem_current = mem_base;
114             mem_avail = mem_limit;
115         } else {
116             fprintf(stderr, "Warning: Failed to allocate requested memory in"
117                     " one large chunk.\nWill allocate in smaller chunks\n");
118         }
119     }
120 
121     memset(slabclass, 0, sizeof(slabclass));
122 
123     while (++i < POWER_LARGEST && size <= settings.item_size_max / 2) {
124         /* Make sure items are always n-byte aligned */
125         if (size % CHUNK_ALIGN_BYTES)
126             size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
127 
128         slabclass[i].size = size;
129         slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
130         size *= factor;
131         if (settings.verbose > 1) {
132             fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
133                     i, slabclass[i].size, slabclass[i].perslab);
134         }
135     }
136 
137     power_largest = i;
138     slabclass[power_largest].size = settings.item_size_max;
139     slabclass[power_largest].perslab = 1;
140     if (settings.verbose > 1) {
141         fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
142                 i, slabclass[i].size, slabclass[i].perslab);
143     }
144 
145     /* for the test suite:  faking of how much we've already malloc'd */
146     {
147         char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
148         if (t_initial_malloc) {
149             mem_malloced = (size_t)atol(t_initial_malloc);
150         }
151 
152     }
153 
154 #ifndef DONT_PREALLOC_SLABS
155     {
156         char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");
157 
158         if (pre_alloc == NULL || atoi(pre_alloc) != 0) {
159             slabs_preallocate(power_largest);
160         }
161     }
162 #endif
163 }
164 
165 #ifndef DONT_PREALLOC_SLABS
166 static void slabs_preallocate (const unsigned int maxslabs) {
167     int i;
168     unsigned int prealloc = 0;
169 
170     /* pre-allocate a 1MB slab in every size class so people don't get
171        confused by non-intuitive "SERVER_ERROR out of memory"
172        messages.  this is the most common question on the mailing
173        list.  if you really don't want this, you can rebuild without
174        these three lines.  */
175 
176     for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {
177         if (++prealloc > maxslabs)
178             return;
179         do_slabs_newslab(i);
180     }
181 
182 }
183 #endif
184 
185 static int grow_slab_list (const unsigned int id) {
186     slabclass_t *p = &slabclass[id];
187     if (p->slabs == p->list_size) {
188         size_t new_size =  (p->list_size != 0) ? p->list_size * 2 : 16;
189         void *new_list = realloc(p->slab_list, new_size * sizeof(void *));
190         if (new_list == 0) return 0;
191         p->list_size = new_size;
192         p->slab_list = new_list;
193     }
194     return 1;
195 }
196 
197 static int do_slabs_newslab(const unsigned int id) {
198     slabclass_t *p = &slabclass[id];
199 #ifdef ALLOW_SLABS_REASSIGN
200     int len = settings.item_size_max;
201 #else
202     int len = p->size * p->perslab;
203 #endif
204     char *ptr;
205 
206     if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
207         (grow_slab_list(id) == 0) ||
208         ((ptr = memory_allocate((size_t)len)) == 0)) {
209 
210         MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
211         return 0;
212     }
213 
214     memset(ptr, 0, (size_t)len);
215     p->end_page_ptr = ptr;
216     p->end_page_free = p->perslab;
217 
218     p->slab_list[p->slabs++] = ptr;
219     mem_malloced += len;
220     MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);
221 
222     return 1;
223 }
224 
225 /*@null@*/
226 static void *do_slabs_alloc(const size_t size, unsigned int id) {
227     slabclass_t *p;
228     void *ret = NULL;
229 
230     if (id < POWER_SMALLEST || id > power_largest) {
231         MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0);
232         return NULL;
233     }
234 
235     p = &slabclass[id];
236     assert(p->sl_curr == 0 || ((item *)p->slots[p->sl_curr - 1])->slabs_clsid == 0);
237 
238 #ifdef USE_SYSTEM_MALLOC
239     if (mem_limit && mem_malloced + size > mem_limit) {
240         MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
241         return 0;
242     }
243     mem_malloced += size;
244     ret = malloc(size);
245     MEMCACHED_SLABS_ALLOCATE(size, id, 0, ret);
246     return ret;
247 #endif
248 
249     /* fail unless we have space at the end of a recently allocated page,
250        we have something on our freelist, or we could allocate a new page */
251     if (! (p->end_page_ptr != 0 || p->sl_curr != 0 ||
252            do_slabs_newslab(id) != 0)) {
253         /* We don't have more memory available */
254         ret = NULL;
255     } else if (p->sl_curr != 0) {
256         /* return off our freelist */
257         ret = p->slots[--p->sl_curr];
258     } else {
259         /* if we recently allocated a whole page, return from that */
260         assert(p->end_page_ptr != NULL);
261         ret = p->end_page_ptr;
262         if (--p->end_page_free != 0) {
263             p->end_page_ptr = ((caddr_t)p->end_page_ptr) + p->size;
264         } else {
265             p->end_page_ptr = 0;
266         }
267     }
268 
269     if (ret) {
270         p->requested += size;
271         MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);
272     } else {
273         MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
274     }
275 
276     return ret;
277 }
278 
279 static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
280     slabclass_t *p;
281 
282     assert(((item *)ptr)->slabs_clsid == 0);
283     assert(id >= POWER_SMALLEST && id <= power_largest);
284     if (id < POWER_SMALLEST || id > power_largest)
285         return;
286 
287     MEMCACHED_SLABS_FREE(size, id, ptr);
288     p = &slabclass[id];
289 
290 #ifdef USE_SYSTEM_MALLOC
291     mem_malloced -= size;
292     free(ptr);
293     return;
294 #endif
295 
296     if (p->sl_curr == p->sl_total) { /* need more space on the free list */
297         int new_size = (p->sl_total != 0) ? p->sl_total * 2 : 16;  /* 16 is arbitrary */
298         void **new_slots = realloc(p->slots, new_size * sizeof(void *));
299         if (new_slots == 0)
300             return;
301         p->slots = new_slots;
302         p->sl_total = new_size;
303     }
304     p->slots[p->sl_curr++] = ptr;
305     p->requested -= size;
306     return;
307 }
308 
309 static int nz_strcmp(int nzlength, const char *nz, const char *z) {
310     int zlength=strlen(z);
311     return (zlength == nzlength) && (strncmp(nz, z, zlength) == 0) ? 0 : -1;
312 }
313 
314 bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c) {
315     bool ret = true;
316 
317     if (add_stats != NULL) {
318         if (!stat_type) {
319             /* prepare general statistics for the engine */
320             APPEND_STAT("bytes", "%llu", (unsigned long long)stats.curr_bytes);
321             APPEND_STAT("curr_items", "%u", stats.curr_items);
322             APPEND_STAT("total_items", "%u", stats.total_items);
323             APPEND_STAT("evictions", "%llu",
324                         (unsigned long long)stats.evictions);
325         } else if (nz_strcmp(nkey, stat_type, "items") == 0) {
326             item_stats(add_stats, c);
327         } else if (nz_strcmp(nkey, stat_type, "slabs") == 0) {
328             slabs_stats(add_stats, c);
329         } else if (nz_strcmp(nkey, stat_type, "sizes") == 0) {
330             item_stats_sizes(add_stats, c);
331         } else {
332             ret = false;
333         }
334     } else {
335         ret = false;
336     }
337 
338     return ret;
339 }
340 
341 /*@null@*/
342 static void do_slabs_stats(ADD_STAT add_stats, void *c) {
343     int i, total;
344     /* Get the per-thread stats which contain some interesting aggregates */
345     struct thread_stats thread_stats;
346     threadlocal_stats_aggregate(&thread_stats);
347 
348     total = 0;
349     for(i = POWER_SMALLEST; i <= power_largest; i++) {
350         slabclass_t *p = &slabclass[i];
351         if (p->slabs != 0) {
352             uint32_t perslab, slabs;
353             slabs = p->slabs;
354             perslab = p->perslab;
355 
356             char key_str[STAT_KEY_LEN];
357             char val_str[STAT_VAL_LEN];
358             int klen = 0, vlen = 0;
359 
360             APPEND_NUM_STAT(i, "chunk_size", "%u", p->size);
361             APPEND_NUM_STAT(i, "chunks_per_page", "%u", perslab);
362             APPEND_NUM_STAT(i, "total_pages", "%u", slabs);
363             APPEND_NUM_STAT(i, "total_chunks", "%u", slabs * perslab);
364             APPEND_NUM_STAT(i, "used_chunks", "%u",
365                             slabs*perslab - p->sl_curr - p->end_page_free);
366             APPEND_NUM_STAT(i, "free_chunks", "%u", p->sl_curr);
367             APPEND_NUM_STAT(i, "free_chunks_end", "%u", p->end_page_free);
368             APPEND_NUM_STAT(i, "mem_requested", "%llu",
369                             (unsigned long long)p->requested);
370             APPEND_NUM_STAT(i, "get_hits", "%llu",
371                     (unsigned long long)thread_stats.slab_stats[i].get_hits);
372             APPEND_NUM_STAT(i, "cmd_set", "%llu",
373                     (unsigned long long)thread_stats.slab_stats[i].set_cmds);
374             APPEND_NUM_STAT(i, "delete_hits", "%llu",
375                     (unsigned long long)thread_stats.slab_stats[i].delete_hits);
376             APPEND_NUM_STAT(i, "incr_hits", "%llu",
377                     (unsigned long long)thread_stats.slab_stats[i].incr_hits);
378             APPEND_NUM_STAT(i, "decr_hits", "%llu",
379                     (unsigned long long)thread_stats.slab_stats[i].decr_hits);
380             APPEND_NUM_STAT(i, "cas_hits", "%llu",
381                     (unsigned long long)thread_stats.slab_stats[i].cas_hits);
382             APPEND_NUM_STAT(i, "cas_badval", "%llu",
383                     (unsigned long long)thread_stats.slab_stats[i].cas_badval);
384 
385             total++;
386         }
387     }
388 
389     /* add overall slab stats and append terminator */
390 
391     APPEND_STAT("active_slabs", "%d", total);
392     APPEND_STAT("total_malloced", "%llu", (unsigned long long)mem_malloced);
393     add_stats(NULL, 0, NULL, 0, c);
394 }
395 
396 #ifdef ALLOW_SLABS_REASSIGN
397 /* Blows away all the items in a slab class and moves its slabs to another
398    class. This is only used by the "slabs reassign" command, for manual tweaking
399    of memory allocation. It's disabled by default since it requires that all
400    slabs be the same size (which can waste space for chunk size mantissas of
401    other than 2.0).
402    1 = success
403    0 = fail
404    -1 = tried. busy. send again shortly. */
405 int do_slabs_reassign(unsigned char srcid, unsigned char dstid) {
406     void *slab, *slab_end;
407     slabclass_t *p, *dp;
408     void *iter;
409     bool was_busy = false;
410 
411     if (srcid < POWER_SMALLEST || srcid > power_largest ||
412         dstid < POWER_SMALLEST || dstid > power_largest)
413         return 0;
414 
415     p = &slabclass[srcid];
416     dp = &slabclass[dstid];
417 
418     /* fail if src still populating, or no slab to give up in src */
419     if (p->end_page_ptr || ! p->slabs)
420         return 0;
421 
422     /* fail if dst is still growing or we can't make room to hold its new one */
423     if (dp->end_page_ptr || ! grow_slab_list(dstid))
424         return 0;
425 
426     if (p->killing == 0) p->killing = 1;
427 
428     slab = p->slab_list[p->killing - 1];
429     slab_end = (char*)slab + settings.item_size_max;
430 
431     for (iter = slab; iter < slab_end; (char*)iter += p->size) {
432         item *it = (item *)iter;
433         if (it->slabs_clsid) {
434             if (it->refcount) was_busy = true;
435             item_unlink(it);
436         }
437     }
438 
439     /* go through free list and discard items that are no longer part of this slab */
440     {
441         int fi;
442         for (fi = p->sl_curr - 1; fi >= 0; fi--) {
443             if (p->slots[fi] >= slab && p->slots[fi] < slab_end) {
444                 p->sl_curr--;
445                 if (p->sl_curr > fi) p->slots[fi] = p->slots[p->sl_curr];
446             }
447         }
448     }
449 
450     if (was_busy) return -1;
451 
452     /* if good, now move it to the dst slab class */
453     p->slab_list[p->killing - 1] = p->slab_list[p->slabs - 1];
454     p->slabs--;
455     p->killing = 0;
456     dp->slab_list[dp->slabs++] = slab;
457     dp->end_page_ptr = slab;
458     dp->end_page_free = dp->perslab;
459     /* this isn't too critical, but other parts of the code do asserts to
460        make sure this field is always 0.  */
461     for (iter = slab; iter < slab_end; (char*)iter += dp->size) {
462         ((item *)iter)->slabs_clsid = 0;
463     }
464     return 1;
465 }
466 
467 int slabs_reassign(unsigned char srcid, unsigned char dstid) {
468     int ret;
469 
470     pthread_mutex_lock(&slabs_lock);
471     ret = do_slabs_reassign(srcid, dstid);
472     pthread_mutex_unlock(&slabs_lock);
473     return ret;
474 }
475 #endif
476 
477 static void *memory_allocate(size_t size) {
478     void *ret;
479 
480     if (mem_base == NULL) {
481         /* We are not using a preallocated large memory chunk */
482         ret = malloc(size);
483     } else {
484         ret = mem_current;
485 
486         if (size > mem_avail) {
487             return NULL;
488         }
489 
490         /* mem_current pointer _must_ be aligned!!! */
491         if (size % CHUNK_ALIGN_BYTES) {
492             size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
493         }
494 
495         mem_current = ((char*)mem_current) + size;
496         if (size < mem_avail) {
497             mem_avail -= size;
498         } else {
499             mem_avail = 0;
500         }
501     }
502 
503     return ret;
504 }
505 
506 void *slabs_alloc(size_t size, unsigned int id) {
507     void *ret;
508 
509     pthread_mutex_lock(&slabs_lock);
510     ret = do_slabs_alloc(size, id);
511     pthread_mutex_unlock(&slabs_lock);
512     return ret;
513 }
514 
515 void slabs_free(void *ptr, size_t size, unsigned int id) {
516     pthread_mutex_lock(&slabs_lock);
517     do_slabs_free(ptr, size, id);
518     pthread_mutex_unlock(&slabs_lock);
519 }
520 
521 void slabs_stats(ADD_STAT add_stats, void *c) {
522     pthread_mutex_lock(&slabs_lock);
523     do_slabs_stats(add_stats, c);
524     pthread_mutex_unlock(&slabs_lock);
525 }
526