~ubuntu-branches/ubuntu/precise/memcached/precise

« back to all changes in this revision

Viewing changes to slabs.c

  • Committer: Bazaar Package Importer
  • Author(s): David Martínez Moreno
  • Date: 2009-10-16 15:09:43 UTC
  • mfrom: (1.3.5 upstream)
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20091016150943-l96biwf7siwdt1ci
Tags: upstream-1.4.2
Import upstream version 1.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
 * a multiplier factor from there, up to half the maximum slab size. The last
7
7
 * slab size is always 1MB, since that's the maximum item size allowed by the
8
8
 * memcached protocol.
9
 
 *
10
 
 * $Id$
11
9
 */
12
10
#include "memcached.h"
13
11
#include <sys/stat.h>
21
19
#include <stdio.h>
22
20
#include <string.h>
23
21
#include <assert.h>
24
 
 
25
 
#define POWER_SMALLEST 1
26
 
#define POWER_LARGEST  200
27
 
#define POWER_BLOCK 1048576
28
 
#define CHUNK_ALIGN_BYTES 8
29
 
#define DONT_PREALLOC_SLABS
 
22
#include <pthread.h>
30
23
 
31
24
/* powers-of-N allocation structures */
32
25
 
47
40
    unsigned int list_size; /* size of prev array */
48
41
 
49
42
    unsigned int killing;  /* index+1 of dying slab, or zero if none */
 
43
    size_t requested; /* The number of requested bytes */
50
44
} slabclass_t;
51
45
 
52
 
static slabclass_t slabclass[POWER_LARGEST + 1];
 
46
static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];
53
47
static size_t mem_limit = 0;
54
48
static size_t mem_malloced = 0;
55
49
static int power_largest;
58
52
static void *mem_current = NULL;
59
53
static size_t mem_avail = 0;
60
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
 
61
60
/*
62
61
 * Forward Declarations
63
62
 */
121
120
 
122
121
    memset(slabclass, 0, sizeof(slabclass));
123
122
 
124
 
    while (++i < POWER_LARGEST && size <= POWER_BLOCK / 2) {
 
123
    while (++i < POWER_LARGEST && size <= settings.item_size_max / 2) {
125
124
        /* Make sure items are always n-byte aligned */
126
125
        if (size % CHUNK_ALIGN_BYTES)
127
126
            size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
128
127
 
129
128
        slabclass[i].size = size;
130
 
        slabclass[i].perslab = POWER_BLOCK / slabclass[i].size;
 
129
        slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
131
130
        size *= factor;
132
131
        if (settings.verbose > 1) {
133
 
            fprintf(stderr, "slab class %3d: chunk size %6u perslab %5u\n",
 
132
            fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
134
133
                    i, slabclass[i].size, slabclass[i].perslab);
135
134
        }
136
135
    }
137
136
 
138
137
    power_largest = i;
139
 
    slabclass[power_largest].size = POWER_BLOCK;
 
138
    slabclass[power_largest].size = settings.item_size_max;
140
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
    }
141
144
 
142
145
    /* for the test suite:  faking of how much we've already malloc'd */
143
146
    {
193
196
 
194
197
static int do_slabs_newslab(const unsigned int id) {
195
198
    slabclass_t *p = &slabclass[id];
196
 
#ifdef ALLOW_SLABS_REASSIGN
197
 
    int len = POWER_BLOCK;
198
 
#else
199
199
    int len = p->size * p->perslab;
200
 
#endif
201
200
    char *ptr;
202
201
 
203
202
    if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
214
213
 
215
214
    p->slab_list[p->slabs++] = ptr;
216
215
    mem_malloced += len;
217
 
 
218
216
    MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);
 
217
 
219
218
    return 1;
220
219
}
221
220
 
222
221
/*@null@*/
223
 
void *do_slabs_alloc(const size_t size, unsigned int id) {
 
222
static void *do_slabs_alloc(const size_t size, unsigned int id) {
224
223
    slabclass_t *p;
225
224
    void *ret = NULL;
226
225
 
257
256
        assert(p->end_page_ptr != NULL);
258
257
        ret = p->end_page_ptr;
259
258
        if (--p->end_page_free != 0) {
260
 
            p->end_page_ptr += p->size;
 
259
            p->end_page_ptr = ((caddr_t)p->end_page_ptr) + p->size;
261
260
        } else {
262
261
            p->end_page_ptr = 0;
263
262
        }
264
263
    }
265
264
 
266
265
    if (ret) {
 
266
        p->requested += size;
267
267
        MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);
268
268
    } else {
269
269
        MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
272
272
    return ret;
273
273
}
274
274
 
275
 
void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
 
275
static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
276
276
    slabclass_t *p;
277
277
 
278
278
    assert(((item *)ptr)->slabs_clsid == 0);
298
298
        p->sl_total = new_size;
299
299
    }
300
300
    p->slots[p->sl_curr++] = ptr;
 
301
    p->requested -= size;
301
302
    return;
302
303
}
303
304
 
 
305
static int nz_strcmp(int nzlength, const char *nz, const char *z) {
 
306
    int zlength=strlen(z);
 
307
    return (zlength == nzlength) && (strncmp(nz, z, zlength) == 0) ? 0 : -1;
 
308
}
 
309
 
 
310
bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c) {
 
311
    bool ret = true;
 
312
 
 
313
    if (add_stats != NULL) {
 
314
        if (!stat_type) {
 
315
            /* prepare general statistics for the engine */
 
316
            APPEND_STAT("bytes", "%llu", (unsigned long long)stats.curr_bytes);
 
317
            APPEND_STAT("curr_items", "%u", stats.curr_items);
 
318
            APPEND_STAT("total_items", "%u", stats.total_items);
 
319
            APPEND_STAT("evictions", "%llu",
 
320
                        (unsigned long long)stats.evictions);
 
321
        } else if (nz_strcmp(nkey, stat_type, "items") == 0) {
 
322
            item_stats(add_stats, c);
 
323
        } else if (nz_strcmp(nkey, stat_type, "slabs") == 0) {
 
324
            slabs_stats(add_stats, c);
 
325
        } else if (nz_strcmp(nkey, stat_type, "sizes") == 0) {
 
326
            item_stats_sizes(add_stats, c);
 
327
        } else {
 
328
            ret = false;
 
329
        }
 
330
    } else {
 
331
        ret = false;
 
332
    }
 
333
 
 
334
    return ret;
 
335
}
 
336
 
304
337
/*@null@*/
305
 
char* do_slabs_stats(int *buflen) {
 
338
static void do_slabs_stats(ADD_STAT add_stats, void *c) {
306
339
    int i, total;
307
 
    char *buf = (char *)malloc(power_largest * 200 + 100);
308
 
    char *bufcurr = buf;
309
 
 
310
 
    *buflen = 0;
311
 
    if (buf == NULL) return NULL;
 
340
    /* Get the per-thread stats which contain some interesting aggregates */
 
341
    struct thread_stats thread_stats;
 
342
    threadlocal_stats_aggregate(&thread_stats);
312
343
 
313
344
    total = 0;
314
345
    for(i = POWER_SMALLEST; i <= power_largest; i++) {
315
346
        slabclass_t *p = &slabclass[i];
316
347
        if (p->slabs != 0) {
317
 
            unsigned int perslab, slabs;
318
 
 
 
348
            uint32_t perslab, slabs;
319
349
            slabs = p->slabs;
320
350
            perslab = p->perslab;
321
351
 
322
 
            bufcurr += sprintf(bufcurr, "STAT %d:chunk_size %u\r\n", i, p->size);
323
 
            bufcurr += sprintf(bufcurr, "STAT %d:chunks_per_page %u\r\n", i, perslab);
324
 
            bufcurr += sprintf(bufcurr, "STAT %d:total_pages %u\r\n", i, slabs);
325
 
            bufcurr += sprintf(bufcurr, "STAT %d:total_chunks %u\r\n", i, slabs*perslab);
326
 
            bufcurr += sprintf(bufcurr, "STAT %d:used_chunks %u\r\n", i, slabs*perslab - p->sl_curr - p->end_page_free);
327
 
            bufcurr += sprintf(bufcurr, "STAT %d:free_chunks %u\r\n", i, p->sl_curr);
328
 
            bufcurr += sprintf(bufcurr, "STAT %d:free_chunks_end %u\r\n", i, p->end_page_free);
 
352
            char key_str[STAT_KEY_LEN];
 
353
            char val_str[STAT_VAL_LEN];
 
354
            int klen = 0, vlen = 0;
 
355
 
 
356
            APPEND_NUM_STAT(i, "chunk_size", "%u", p->size);
 
357
            APPEND_NUM_STAT(i, "chunks_per_page", "%u", perslab);
 
358
            APPEND_NUM_STAT(i, "total_pages", "%u", slabs);
 
359
            APPEND_NUM_STAT(i, "total_chunks", "%u", slabs * perslab);
 
360
            APPEND_NUM_STAT(i, "used_chunks", "%u",
 
361
                            slabs*perslab - p->sl_curr - p->end_page_free);
 
362
            APPEND_NUM_STAT(i, "free_chunks", "%u", p->sl_curr);
 
363
            APPEND_NUM_STAT(i, "free_chunks_end", "%u", p->end_page_free);
 
364
            APPEND_NUM_STAT(i, "mem_requested", "%llu",
 
365
                            (unsigned long long)p->requested);
 
366
            APPEND_NUM_STAT(i, "get_hits", "%llu",
 
367
                    (unsigned long long)thread_stats.slab_stats[i].get_hits);
 
368
            APPEND_NUM_STAT(i, "cmd_set", "%llu",
 
369
                    (unsigned long long)thread_stats.slab_stats[i].set_cmds);
 
370
            APPEND_NUM_STAT(i, "delete_hits", "%llu",
 
371
                    (unsigned long long)thread_stats.slab_stats[i].delete_hits);
 
372
            APPEND_NUM_STAT(i, "incr_hits", "%llu",
 
373
                    (unsigned long long)thread_stats.slab_stats[i].incr_hits);
 
374
            APPEND_NUM_STAT(i, "decr_hits", "%llu",
 
375
                    (unsigned long long)thread_stats.slab_stats[i].decr_hits);
 
376
            APPEND_NUM_STAT(i, "cas_hits", "%llu",
 
377
                    (unsigned long long)thread_stats.slab_stats[i].cas_hits);
 
378
            APPEND_NUM_STAT(i, "cas_badval", "%llu",
 
379
                    (unsigned long long)thread_stats.slab_stats[i].cas_badval);
 
380
 
329
381
            total++;
330
382
        }
331
383
    }
332
 
    bufcurr += sprintf(bufcurr, "STAT active_slabs %d\r\nSTAT total_malloced %llu\r\n", total, (unsigned long long)mem_malloced);
333
 
    bufcurr += sprintf(bufcurr, "END\r\n");
334
 
    *buflen = bufcurr - buf;
335
 
    return buf;
336
 
}
337
 
 
338
 
#ifdef ALLOW_SLABS_REASSIGN
339
 
/* Blows away all the items in a slab class and moves its slabs to another
340
 
   class. This is only used by the "slabs reassign" command, for manual tweaking
341
 
   of memory allocation. It's disabled by default since it requires that all
342
 
   slabs be the same size (which can waste space for chunk size mantissas of
343
 
   other than 2.0).
344
 
   1 = success
345
 
   0 = fail
346
 
   -1 = tried. busy. send again shortly. */
347
 
int do_slabs_reassign(unsigned char srcid, unsigned char dstid) {
348
 
    void *slab, *slab_end;
349
 
    slabclass_t *p, *dp;
350
 
    void *iter;
351
 
    bool was_busy = false;
352
 
 
353
 
    if (srcid < POWER_SMALLEST || srcid > power_largest ||
354
 
        dstid < POWER_SMALLEST || dstid > power_largest)
355
 
        return 0;
356
 
 
357
 
    p = &slabclass[srcid];
358
 
    dp = &slabclass[dstid];
359
 
 
360
 
    /* fail if src still populating, or no slab to give up in src */
361
 
    if (p->end_page_ptr || ! p->slabs)
362
 
        return 0;
363
 
 
364
 
    /* fail if dst is still growing or we can't make room to hold its new one */
365
 
    if (dp->end_page_ptr || ! grow_slab_list(dstid))
366
 
        return 0;
367
 
 
368
 
    if (p->killing == 0) p->killing = 1;
369
 
 
370
 
    slab = p->slab_list[p->killing - 1];
371
 
    slab_end = (char*)slab + POWER_BLOCK;
372
 
 
373
 
    for (iter = slab; iter < slab_end; (char*)iter += p->size) {
374
 
        item *it = (item *)iter;
375
 
        if (it->slabs_clsid) {
376
 
            if (it->refcount) was_busy = true;
377
 
            item_unlink(it);
378
 
        }
379
 
    }
380
 
 
381
 
    /* go through free list and discard items that are no longer part of this slab */
382
 
    {
383
 
        int fi;
384
 
        for (fi = p->sl_curr - 1; fi >= 0; fi--) {
385
 
            if (p->slots[fi] >= slab && p->slots[fi] < slab_end) {
386
 
                p->sl_curr--;
387
 
                if (p->sl_curr > fi) p->slots[fi] = p->slots[p->sl_curr];
388
 
            }
389
 
        }
390
 
    }
391
 
 
392
 
    if (was_busy) return -1;
393
 
 
394
 
    /* if good, now move it to the dst slab class */
395
 
    p->slab_list[p->killing - 1] = p->slab_list[p->slabs - 1];
396
 
    p->slabs--;
397
 
    p->killing = 0;
398
 
    dp->slab_list[dp->slabs++] = slab;
399
 
    dp->end_page_ptr = slab;
400
 
    dp->end_page_free = dp->perslab;
401
 
    /* this isn't too critical, but other parts of the code do asserts to
402
 
       make sure this field is always 0.  */
403
 
    for (iter = slab; iter < slab_end; (char*)iter += dp->size) {
404
 
        ((item *)iter)->slabs_clsid = 0;
405
 
    }
406
 
    return 1;
407
 
}
408
 
#endif
 
384
 
 
385
    /* add overall slab stats and append terminator */
 
386
 
 
387
    APPEND_STAT("active_slabs", "%d", total);
 
388
    APPEND_STAT("total_malloced", "%llu", (unsigned long long)mem_malloced);
 
389
    add_stats(NULL, 0, NULL, 0, c);
 
390
}
409
391
 
410
392
static void *memory_allocate(size_t size) {
411
393
    void *ret;
425
407
            size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
426
408
        }
427
409
 
428
 
        mem_current += size;
 
410
        mem_current = ((char*)mem_current) + size;
429
411
        if (size < mem_avail) {
430
412
            mem_avail -= size;
431
413
        } else {
435
417
 
436
418
    return ret;
437
419
}
 
420
 
 
421
void *slabs_alloc(size_t size, unsigned int id) {
 
422
    void *ret;
 
423
 
 
424
    pthread_mutex_lock(&slabs_lock);
 
425
    ret = do_slabs_alloc(size, id);
 
426
    pthread_mutex_unlock(&slabs_lock);
 
427
    return ret;
 
428
}
 
429
 
 
430
void slabs_free(void *ptr, size_t size, unsigned int id) {
 
431
    pthread_mutex_lock(&slabs_lock);
 
432
    do_slabs_free(ptr, size, id);
 
433
    pthread_mutex_unlock(&slabs_lock);
 
434
}
 
435
 
 
436
void slabs_stats(ADD_STAT add_stats, void *c) {
 
437
    pthread_mutex_lock(&slabs_lock);
 
438
    do_slabs_stats(add_stats, c);
 
439
    pthread_mutex_unlock(&slabs_lock);
 
440
}