Blame libglusterfs/src/glusterfs/mem-pool.h

Packit Service e080da
/*
Packit Service e080da
  Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
Packit Service e080da
  This file is part of GlusterFS.
Packit Service e080da
Packit Service e080da
  This file is licensed to you under your choice of the GNU Lesser
Packit Service e080da
  General Public License, version 3 or any later version (LGPLv3 or
Packit Service e080da
  later), or the GNU General Public License, version 2 (GPLv2), in all
Packit Service e080da
  cases as published by the Free Software Foundation.
Packit Service e080da
*/
Packit Service e080da
Packit Service e080da
#ifndef _MEM_POOL_H_
Packit Service e080da
#define _MEM_POOL_H_
Packit Service e080da
Packit Service e080da
#include "glusterfs/list.h"
Packit Service e080da
#include "glusterfs/locking.h"
Packit Service e080da
#include "glusterfs/atomic.h"
Packit Service e080da
#include "glusterfs/logging.h"
Packit Service e080da
#include "glusterfs/mem-types.h"
Packit Service e080da
#include "glusterfs/glusterfs.h" /* for glusterfs_ctx_t */
Packit Service e080da
#include <stdlib.h>
Packit Service e080da
#include <inttypes.h>
Packit Service e080da
#include <string.h>
Packit Service e080da
#include <stdarg.h>
Packit Service e080da
Packit Service e080da
/*
Packit Service e080da
 * Need this for unit tests since inline functions
Packit Service e080da
 * access memory allocation and need to use the
Packit Service e080da
 * unit test versions
Packit Service e080da
 */
Packit Service e080da
#ifdef UNIT_TESTING
Packit Service e080da
#include <stddef.h>
Packit Service e080da
#include <setjmp.h>
Packit Service e080da
#include <cmocka.h>
Packit Service e080da
#endif
Packit Service e080da
Packit Service e080da
#define GF_MEM_TRAILER_SIZE 8
Packit Service e080da
#define GF_MEM_HEADER_MAGIC 0xCAFEBABE
Packit Service e080da
#define GF_MEM_TRAILER_MAGIC 0xBAADF00D
Packit Service e080da
#define GF_MEM_INVALID_MAGIC 0xDEADC0DE
Packit Service e080da
Packit Service e080da
#define POOL_SMALLEST 7 /* i.e. 128 */
Packit Service e080da
#define POOL_LARGEST 20 /* i.e. 1048576 */
Packit Service e080da
#define NPOOLS (POOL_LARGEST - POOL_SMALLEST + 1)
Packit Service e080da
Packit Service e080da
struct mem_acct_rec {
Packit Service e080da
    const char *typestr;
Packit Service e080da
    uint64_t size;
Packit Service e080da
    uint64_t max_size;
Packit Service e080da
    uint64_t total_allocs;
Packit Service e080da
    uint32_t num_allocs;
Packit Service e080da
    uint32_t max_num_allocs;
Packit Service e080da
    gf_lock_t lock;
Packit Service e080da
#ifdef DEBUG
Packit Service e080da
    struct list_head obj_list;
Packit Service e080da
#endif
Packit Service e080da
};
Packit Service e080da
Packit Service e080da
struct mem_acct {
Packit Service e080da
    uint32_t num_types;
Packit Service e080da
    gf_atomic_t refcnt;
Packit Service e080da
    struct mem_acct_rec rec[0];
Packit Service e080da
};
Packit Service e080da
Packit Service e080da
struct mem_header {
Packit Service e080da
    uint32_t type;
Packit Service e080da
    size_t size;
Packit Service e080da
    struct mem_acct *mem_acct;
Packit Service e080da
    uint32_t magic;
Packit Service e080da
#ifdef DEBUG
Packit Service e080da
    struct list_head acct_list;
Packit Service e080da
#endif
Packit Service e080da
    int padding[8];
Packit Service e080da
};
Packit Service e080da
Packit Service e080da
#define GF_MEM_HEADER_SIZE (sizeof(struct mem_header))
Packit Service e080da
Packit Service e080da
#ifdef DEBUG
Packit Service e080da
struct mem_invalid {
Packit Service e080da
    uint32_t magic;
Packit Service e080da
    void *mem_acct;
Packit Service e080da
    uint32_t type;
Packit Service e080da
    size_t size;
Packit Service e080da
    void *baseaddr;
Packit Service e080da
};
Packit Service e080da
#endif
Packit Service e080da
Packit Service e080da
void *
Packit Service e080da
__gf_calloc(size_t cnt, size_t size, uint32_t type, const char *typestr);
Packit Service e080da
Packit Service e080da
void *
Packit Service e080da
__gf_malloc(size_t size, uint32_t type, const char *typestr);
Packit Service e080da
Packit Service e080da
void *
Packit Service e080da
__gf_realloc(void *ptr, size_t size);
Packit Service e080da
Packit Service e080da
int
Packit Service e080da
gf_vasprintf(char **string_ptr, const char *format, va_list arg);
Packit Service e080da
Packit Service e080da
int
Packit Service e080da
gf_asprintf(char **string_ptr, const char *format, ...)
Packit Service e080da
    __attribute__((__format__(__printf__, 2, 3)));
Packit Service e080da
Packit Service e080da
void
Packit Service e080da
__gf_free(void *ptr);
Packit Service e080da
Packit Service e080da
static inline void *
Packit Service e080da
__gf_default_malloc(size_t size)
Packit Service e080da
{
Packit Service e080da
    void *ptr = NULL;
Packit Service e080da
Packit Service e080da
    ptr = malloc(size);
Packit Service e080da
    if (!ptr)
Packit Service e080da
        gf_msg_nomem("", GF_LOG_ALERT, size);
Packit Service e080da
Packit Service e080da
    return ptr;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
static inline void *
Packit Service e080da
__gf_default_calloc(int cnt, size_t size)
Packit Service e080da
{
Packit Service e080da
    void *ptr = NULL;
Packit Service e080da
Packit Service e080da
    ptr = calloc(cnt, size);
Packit Service e080da
    if (!ptr)
Packit Service e080da
        gf_msg_nomem("", GF_LOG_ALERT, (cnt * size));
Packit Service e080da
Packit Service e080da
    return ptr;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
static inline void *
Packit Service e080da
__gf_default_realloc(void *oldptr, size_t size)
Packit Service e080da
{
Packit Service e080da
    void *ptr = NULL;
Packit Service e080da
Packit Service e080da
    ptr = realloc(oldptr, size);
Packit Service e080da
    if (!ptr)
Packit Service e080da
        gf_msg_nomem("", GF_LOG_ALERT, size);
Packit Service e080da
Packit Service e080da
    return ptr;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
#define MALLOC(size) __gf_default_malloc(size)
Packit Service e080da
#define CALLOC(cnt, size) __gf_default_calloc(cnt, size)
Packit Service e080da
#define REALLOC(ptr, size) __gf_default_realloc(ptr, size)
Packit Service e080da
Packit Service e080da
#define FREE(ptr)                                                              \
Packit Service e080da
    do {                                                                       \
Packit Service e080da
        if (ptr != NULL) {                                                     \
Packit Service e080da
            free((void *)ptr);                                                 \
Packit Service e080da
            ptr = (void *)0xeeeeeeee;                                          \
Packit Service e080da
        }                                                                      \
Packit Service e080da
    } while (0)
Packit Service e080da
Packit Service e080da
#define GF_CALLOC(nmemb, size, type) __gf_calloc(nmemb, size, type, #type)
Packit Service e080da
Packit Service e080da
#define GF_MALLOC(size, type) __gf_malloc(size, type, #type)
Packit Service e080da
Packit Service e080da
#define GF_REALLOC(ptr, size) __gf_realloc(ptr, size)
Packit Service e080da
Packit Service e080da
#define GF_FREE(free_ptr) __gf_free(free_ptr)
Packit Service e080da
Packit Service e080da
static inline char *
Packit Service e080da
gf_strndup(const char *src, size_t len)
Packit Service e080da
{
Packit Service e080da
    char *dup_str = NULL;
Packit Service e080da
Packit Service e080da
    if (!src) {
Packit Service e080da
        goto out;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    dup_str = GF_MALLOC(len + 1, gf_common_mt_strdup);
Packit Service e080da
    if (!dup_str) {
Packit Service e080da
        goto out;
Packit Service e080da
    }
Packit Service e080da
Packit Service e080da
    memcpy(dup_str, src, len);
Packit Service e080da
    dup_str[len] = '\0';
Packit Service e080da
out:
Packit Service e080da
    return dup_str;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
static inline char *
Packit Service e080da
gf_strdup(const char *src)
Packit Service e080da
{
Packit Service e080da
    if (!src)
Packit Service e080da
        return NULL;
Packit Service e080da
Packit Service e080da
    return gf_strndup(src, strlen(src));
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
static inline void *
Packit Service e080da
gf_memdup(const void *src, size_t size)
Packit Service e080da
{
Packit Service e080da
    void *dup_mem = NULL;
Packit Service e080da
Packit Service e080da
    dup_mem = GF_MALLOC(size, gf_common_mt_strdup);
Packit Service e080da
    if (!dup_mem)
Packit Service e080da
        goto out;
Packit Service e080da
Packit Service e080da
    memcpy(dup_mem, src, size);
Packit Service e080da
Packit Service e080da
out:
Packit Service e080da
    return dup_mem;
Packit Service e080da
}
Packit Service e080da
Packit Service e080da
/* kind of 'header' for the actual mem_pool_shared structure, this might make
Packit Service e080da
 * it possible to dump some more details in a statedump */
Packit Service e080da
struct mem_pool {
Packit Service e080da
    /* object size, without pooled_obj_hdr_t */
Packit Service e080da
    unsigned long sizeof_type;
Packit Service e080da
    unsigned long count; /* requested pool size (unused) */
Packit Service e080da
    char *name;
Packit Service e080da
    gf_atomic_t active; /* current allocations */
Packit Service e080da
#ifdef DEBUG
Packit Service e080da
    gf_atomic_t hit;  /* number of allocations served from pt_pool */
Packit Service e080da
    gf_atomic_t miss; /* number of std allocs due to miss */
Packit Service e080da
#endif
Packit Service e080da
    struct list_head owner; /* glusterfs_ctx_t->mempool_list */
Packit Service e080da
    glusterfs_ctx_t *ctx;   /* take ctx->lock when updating owner */
Packit Service e080da
Packit Service e080da
    struct mem_pool_shared *pool; /* the initial pool that was returned */
Packit Service e080da
};
Packit Service e080da
Packit Service e080da
typedef struct pooled_obj_hdr {
Packit Service e080da
    unsigned long magic;
Packit Service e080da
    struct pooled_obj_hdr *next;
Packit Service e080da
    struct per_thread_pool_list *pool_list;
Packit Service e080da
    unsigned int power_of_two;
Packit Service e080da
Packit Service e080da
    /* track the pool that was used to request this object */
Packit Service e080da
    struct mem_pool *pool;
Packit Service e080da
} pooled_obj_hdr_t;
Packit Service e080da
Packit Service 173fb3
/* Each memory block inside a pool has a fixed size that is a power of two.
Packit Service 173fb3
 * However each object will have a header that will reduce the available
Packit Service 173fb3
 * space. */
Packit Service 173fb3
#define AVAILABLE_SIZE(p2) ((1UL << (p2)) - sizeof(pooled_obj_hdr_t))
Packit Service e080da
Packit Service e080da
typedef struct per_thread_pool {
Packit Service e080da
    /* the pool that was used to request this allocation */
Packit Service e080da
    struct mem_pool_shared *parent;
Packit Service e080da
    /* Everything else is protected by our own lock. */
Packit Service e080da
    pooled_obj_hdr_t *hot_list;
Packit Service e080da
    pooled_obj_hdr_t *cold_list;
Packit Service e080da
} per_thread_pool_t;
Packit Service e080da
Packit Service e080da
typedef struct per_thread_pool_list {
Packit Service 173fb3
    /* thr_list is used to place the TLS pool_list into the active global list
Packit Service 173fb3
     * (pool_threads) or the inactive global list (pool_free_threads). It's
Packit Service 173fb3
     * protected by the global pool_lock. */
Packit Service e080da
    struct list_head thr_list;
Packit Service 173fb3
Packit Service 173fb3
    /* This lock is used to update poison and the hot/cold lists of members
Packit Service 173fb3
     * of 'pools' array. */
Packit Service 173fb3
    pthread_spinlock_t lock;
Packit Service 173fb3
Packit Service 173fb3
    /* This field is used to mark a pool_list as not being owned by any thread.
Packit Service 173fb3
     * This means that the sweeper thread won't be cleaning objects stored in
Packit Service 173fb3
     * its pools. mem_put() uses it to decide if the object being released is
Packit Service 173fb3
     * placed into its original pool_list or directly destroyed. */
Packit Service 173fb3
    bool poison;
Packit Service 173fb3
Packit Service e080da
    /*
Packit Service e080da
     * There's really more than one pool, but the actual number is hidden
Packit Service e080da
     * in the implementation code so we just make it a single-element array
Packit Service e080da
     * here.
Packit Service e080da
     */
Packit Service e080da
    per_thread_pool_t pools[1];
Packit Service e080da
} per_thread_pool_list_t;
Packit Service e080da
Packit Service e080da
/* actual pool structure, shared between different mem_pools */
Packit Service e080da
struct mem_pool_shared {
Packit Service e080da
    unsigned int power_of_two;
Packit Service e080da
    /*
Packit Service e080da
     * Updates to these are *not* protected by a global lock, so races
Packit Service e080da
     * could occur and the numbers might be slightly off.  Don't expect
Packit Service e080da
     * them to line up exactly.  It's the general trends that matter, and
Packit Service e080da
     * it's not worth the locked-bus-cycle overhead to make these precise.
Packit Service e080da
     */
Packit Service e080da
    gf_atomic_t allocs_hot;
Packit Service e080da
    gf_atomic_t allocs_cold;
Packit Service e080da
    gf_atomic_t allocs_stdc;
Packit Service e080da
    gf_atomic_t frees_to_list;
Packit Service e080da
};
Packit Service e080da
Packit Service e080da
void
Packit Service 173fb3
mem_pools_init(void); /* start the pool_sweeper thread */
Packit Service e080da
void
Packit Service e080da
mem_pools_fini(void); /* cleanup memory pools */
Packit Service e080da
Packit Service e080da
struct mem_pool *
Packit Service e080da
mem_pool_new_fn(glusterfs_ctx_t *ctx, unsigned long sizeof_type,
Packit Service e080da
                unsigned long count, char *name);
Packit Service e080da
Packit Service e080da
#define mem_pool_new(type, count)                                              \
Packit Service e080da
    mem_pool_new_fn(THIS->ctx, sizeof(type), count, #type)
Packit Service e080da
Packit Service e080da
#define mem_pool_new_ctx(ctx, type, count)                                     \
Packit Service e080da
    mem_pool_new_fn(ctx, sizeof(type), count, #type)
Packit Service e080da
Packit Service e080da
void
Packit Service e080da
mem_put(void *ptr);
Packit Service e080da
void *
Packit Service e080da
mem_get(struct mem_pool *pool);
Packit Service e080da
void *
Packit Service e080da
mem_get0(struct mem_pool *pool);
Packit Service e080da
Packit Service e080da
void
Packit Service e080da
mem_pool_destroy(struct mem_pool *pool);
Packit Service e080da
Packit Service e080da
void
Packit Service 173fb3
mem_pool_thread_destructor(per_thread_pool_list_t *pool_list);
Packit Service e080da
Packit Service 173fb3
void
Packit Service 173fb3
gf_mem_acct_enable_set(void *ctx);
Packit Service e080da
Packit Service e080da
#endif /* _MEM_POOL_H */