OBJCACHE(9)

NAME
objcache_create, objcache_create_mbacked, objcache_create_simple, objcache_destroy, objcache_dtor, objcache_get, objcache_malloc_alloc, objcache_malloc_free, objcache_nop_alloc, objcache_nop_free, objcache_put, objcache_reclaimlist - object caching facility

SYNOPSIS
#include <sys/objcache.h>

    typedef boolean_t (objcache_ctor_fn)(void *obj, void *privdata, int ocflags);
    typedef void (objcache_dtor_fn)(void *obj, void *privdata);
    typedef void *(objcache_alloc_fn)(void *allocator_args, int ocflags);
    typedef void (objcache_free_fn)(void *obj, void *allocator_args);
    struct objcache_malloc_args {
    	size_t		objsize;
    	malloc_type_t	mtype;
    };

struct objcache *
objcache_create(const char *name, int cluster_limit, int mag_capacity, objcache_ctor_fn *ctor, objcache_dtor_fn *dtor, void *privdata, objcache_alloc_fn *alloc, objcache_free_fn *free, void *allocator_args);
struct objcache *
objcache_create_mbacked(malloc_type_t mtype, size_t objsize, int cluster_limit, int mag_capacity, objcache_ctor_fn *ctor, objcache_dtor_fn *dtor, void *privdata);
struct objcache *
objcache_create_simple(malloc_type_t mtype, size_t objsize);
void
objcache_destroy(struct objcache *oc);
void
objcache_dtor(struct objcache *oc, void *obj);
void *
objcache_get(struct objcache *oc, int ocflags);
void *
objcache_malloc_alloc(void *allocator_args, int ocflags);
void
objcache_malloc_free(void *obj, void *allocator_args);
void *
objcache_nop_alloc(void *allocator_args, int ocflags);
void
objcache_nop_free(void *obj, void *allocator_args);
void
objcache_put(struct objcache *oc, void *obj);
boolean_t
objcache_reclaimlist(struct objcache *oc[], int nlist);

DESCRIPTION
Object caching is a technique for manipulating objects that are frequently allocated and freed. The idea behind caching is to preserve the invariant portion of an object's initial state between uses, so it does not have to be destroyed and reborn every time the object is used.

objcache_create() creates a new object cache. It is identified by name, which is used to distinguish the object in diagnostic output. The cluster_limit determines the number of available magazines in the depot layer. It must be at least (mag_capacity * ncpus * 8). If 0 is given, then there is no limit to the number of magazines the depot can have (aside from the inherent limitation imposed by the restricted nature of the back end allocator). The mag_capacity describes the capacity of the magazine, that is the largest number of objects it can hold. If set to 0, the default value is used as defined in sys/kern/kern_objcache.c. Currently, the default value is 64. The object caching system itself may adjust the cluster limit and/or magazines' capacity based on the number of available CPUs. ctor specifies a function that constructs (i.e., performs the one-time initialization of) an object in the cache. It is defined as:

    boolean_t foo_ctor(void *obj, void *privdata, int ocflags);

If no constructor is needed, it must be set to NULL. dtor specifies a destructor function that destroys the cached object, before it is released to the back end that manages the flow of real memory. It is defined as:

    void foo_dtor(void *obj, void *privdata);

If no destructor is needed, it must be set to NULL. The interface to underlying allocator is provided by alloc, free and allocator_args. It must adhere to the following form:

    void *foo_alloc(void *allocator_args, int ocflags);
    void foo_free(void *obj, void *allocator_args);

objcache_malloc_alloc() and objcache_malloc_free() are wrappers for kmalloc(9) allocation functions. Whereas, objcache_nop_alloc() and objcache_nop_free() are wrappers for allocation policies that pre-allocate at initialization time instead of doing run-time allocation.

objcache_create_mbacked() creates a new object cache of size objsize, backed with a malloc_type_t argument. The latter is used to perform statistics in memory usage and for basic sanity checks. For the underlying allocator, kmalloc(9) functions are employed.

objcache_create_simple() creates a new object cache of size objsize, backed with a malloc_type_t argument. The cluster_limit is set to 0 and the default value for magazines' capacity is used. ctor and dtor are set to NULL. privdata is set to NULL as well. For the underlying allocator, kmalloc(9) functions are employed.

objcache_get() returns an object from the oc object cache. The object is in its initialized state. Newly allocated objects are subjected to the object cache's constructor function, if not NULL, prior to being returned. ocflags is only used when the depot does not have any non-empty magazines and a new object needs to be allocated using the back end allocator. In this case we cannot depend on flags such as M_ZERO. If the back end allocator fails, or if the depot's object limit has been reached and M_WAITOK is not specified, NULL is returned.

objcache_put() returns obj to the oc object cache. The object must be in its initialized state prior to this call. If there is no empty magazine, the object destructor is called and the object is freed.

objcache_dtor() puts obj back into the oc object cache, indicating that the object is not in any shape to be reused and should be destructed and freed immediately.

objcache_reclaimlist() iterates over the oclist[] list with nlist elements and tries to free up some memory. For each object cache in the reclaim list, the current per-CPU cache is tried first and then the full magazine depot. The function returns TRUE as soon as some free memory is found and FALSE otherwise.

objcache_destroy() destroys the oc object cache. The object must have no existing references.

IMPLEMENTATION NOTES

  Magazine
A magazine is the very basic functional unit of the object caching scheme. The number of objects it can hold is fixed and determined by its capacity. The term magazine is used as an analogy with automatic weapon (a firearm that can fire several rounds without reloading).

  Per-CPU object cache
The reasoning behind per-CPU caches is to allow CPUs to perform their transactions (i.e., allocations, frees) in a parallel, yet lockless manner.

Each CPU is given two magazines, an active and a backup. This is done in order to avoid a situation where a tight loop of two allocations followed by two frees can cause thrashing at the magazine boundary.

If we need to add an object to the cache and the active magazine is full, room is searched in the backup magazine. If the backup has room, we swap active with backup and add the object. If both magazines are full, we get an empty magazine from the depot and move a fully loaded magazine to the depot.

  Magazine depot
Each object cache manages a global supply of magazines, the depot, that is available across all CPUs. The depot maintains two lists of magazines. One for completely full and one for completely free magazines. The per-CPU object caches only exchange completely full or completely empty magazines with the depot layer.

EXAMPLES

    /* This is the data structure we are going to cache. */
    struct foo {
            int x;
            char str[32];
    };
    MALLOC_DEFINE(M_FOOBUF, "foobuf", "Buffer to my little precious data");
    struct objcache_malloc_args foo_malloc_args = {
            sizeof(struct foo), M_FOOBUF };
    struct objcache *foo_cache;
    /*
     * Object cache constructor.
     */
    static boolean_t
    foo_cache_ctor(void *obj, void *privdata, int ocflags)
    {
            struct foo *myfoo = obj;
            /*
             * Do any initialization of the object here. Let's just zero out
             * the data structure for the fun of it.
             */
            bzero(myfoo, sizeof(*myfoo));
            return (TRUE);
    }
    /*
     * Object cache destructor.
     */
    static void
    foo_cache_dtor(void *obj, void *privdata)
    {
            struct foo *myfoo = obj;
            /*
             * Do any clean up here. E.g., if you have kmalloc'ed() inside
             * the constructor, this is the right place and time to kfree().
             */
    }
    /*
     * Initialize our subsystem.
     */
    static void
    foo_init(void)
    {
            /* Create the object cache. */
            foo_cache = objcache_create("foo",
                0,                          /* infinite depot's capacity */
                0,                          /* default magazine's capacity */
                foo_ctor, foo_dtor, NULL,
                objcache_malloc_alloc,
                objcache_malloc_free,
                &foo_malloc_args);
    }
    /*
     * Random function.
     */
    static void
    foo_random(...)
    {
            struct foo *myfoo;
            /* Get a `foo' object from the object cache. */
            myfoo = objcache_get(foo_cache, M_WAITOK);
            /* Do stuff with it. */
            /* ... */
            /* We don't need it anymore. Put it back in object cache. */
            objcache_put(foo_cache, myfoo);
    }
    /*
     * Shutdown our subsystem.
     */
    static void
    foo_uninit(void)
    {
            /* Destroy the object cache. */
            objcache_destroy(foo_cache);
    }

SEE ALSO
memory(9) Jeff Bonwick, The Slab Allocator: An Object-Caching Kernel Memory Allocator, USENIX Summer 1994 Technical Conference. Jeff Bonwick, Jonathan Adams, Magazines and Vmem: Extending the Slab Allocator to Many CPUs and Arbitrary Resources, USENIX 2001 Technical Conference.

HISTORY
The object caching system appeared in DragonFly 1.3.

AUTHORS
The object caching system was written by -nosplit Jeffrey M. Hsu <hsu@freebsd.org>. This manual page was written by Stathis Kamperis <ekamperi@gmail.com>.