A device driver often ends up allocating many objects of the same size, over and over. Given that the kernel already maintains a set of memory pools of objects that are all the same size, why not add some special pools for these high-volume objects? In fact, the kernel does implement this sort of lookaside cache. Device drivers normally do not exhibit the sort of memory behavior that justifies using a lookaside cache, but there can be exceptions; the USB and ISDN drivers in Linux 2.4 use caches.
Linux memory caches have a type of kmem_cache_t
and
are created with a call to kmem_cache_create:
kmem_cache_t * kmem_cache_create(const char *name, size_t size, size_t offset, unsigned long flags, void (*constructor)(void *, kmem_cache_t *, unsigned long flags), void (*destructor)(void *, kmem_cache_t *, unsigned long flags) );
The function creates a new cache object that can host any number of
memory areas all of the same size, specified by the
size
argument. The name
argument is associated with this cache and functions as housekeeping
information usable in tracking problems; usually, it is set to the
name of the type of structure that will be cached. The maximum length
for the name is 20 characters, including the trailing terminator.
The offset
is the offset of the first object in the
page; it can be used to ensure a particular alignment for the
allocated objects, but you most likely will use 0 to request the
default value. flags
controls how allocation is
done, and is a bit mask of the following flags:
-
SLAB_NO_REAP
Setting this flag protects the cache from being reduced when the system is looking for memory. You would not usually need to set this flag.
-
SLAB_HWCACHE_ALIGN
This flag requires each data object to be aligned to a cache line; actual alignment depends on the cache layout of the host platform. This is usually a good choice.
-
SLAB_CACHE_DMA
This flag requires each data object to be allocated in DMA-capable memory.
The constructor
and destructor
arguments to the function are optional functions (but there can be no
destructor without a constructor); the former can be used to
initialize newly allocated objects and the latter can be used to
“clean up” objects prior to their memory being released back to the
system as a whole.
Constructors and destructors can be useful, but there are a few
constraints that you should keep in mind. A constructor is called
when the memory for a set of objects is allocated; because that memory
may hold several objects, the constructor may be called multiple
times. You cannot assume that the constructor will be called as an
immediate effect of allocating an object. Similarly, destructors can
be called at some unknown future time, not immediately after an object
has been freed. Constructors and destructors may or may not be
allowed to sleep, according to whether they are passed the
SLAB_CTOR_ATOMIC
flag (where
CTOR
is short for
constructor).
For convenience, a programmer can use the same function for both the
constructor and destructor; the slab allocator always passes the
SLAB_CTOR_CONSTRUCTOR
flag when the callee is a
constructor.
Once a cache of objects is created, you can allocate objects from it by calling kmem_cache_alloc:
void *kmem_cache_alloc(kmem_cache_t *cache, int flags);
Here, the cache
argument is the cache you have
created previously; the flags are the same as you would pass to
kmalloc, and are consulted if
kmem_cache_alloc needs to go out and allocate
more memory itself.
To free an object, use kmem_cache_free:
void kmem_cache_free(kmem_cache_t *cache, const void *obj);
When driver code is finished with the cache, typically when the module is unloaded, it should free its cache as follows:
int kmem_cache_destroy(kmem_cache_t *cache);
The destroy option will succeed only if all objects allocated from the cache have been returned to it. A module should thus check the return status from kmem_cache_destroy; a failure indicates some sort of memory leak within the module (since some of the objects have been dropped).
One side benefit to using lookaside caches is that the kernel
maintains statistics on cache usage. There is even a kernel
configuration option that enables the collection of extra statistical
information, but at a noticeable runtime cost. Cache statistics may be
obtained from /proc/slabinfo
.
Time for an example. scullc is a cut-down version of the scull module that implements only the bare device—the persistent memory region. Unlike scull, which uses kmalloc, scullc uses memory caches. The size of the quantum can be modified at compile time and at load time, but not at runtime—that would require creating a new memory cache, and we didn’t want to deal with these unneeded details. The sample module refuses to compile with version 2.0 of the kernel because memory caches were not there, as explained in Section 7.6 later in the chapter.
scullc is a complete example that can be used to make tests. It differs from scull only in a few lines of code. This is how it allocates memory quanta:
/* Allocate a quantum using the memory cache */ if (!dptr->data[s_pos]) { dptr->data[s_pos] = kmem_cache_alloc(scullc_cache, GFP_KERNEL); if (!dptr->data[s_pos]) goto nomem; memset(dptr->data[s_pos], 0, scullc_quantum); }
And these lines release memory:
for (i = 0; i < qset; i++) if (dptr->data[i]) kmem_cache_free(scullc_cache, dptr->data[i]); kfree(dptr->data);
To support use of scullc_cache
, these few lines are
included in the file at proper places:
/* declare one cache pointer: use it for all devices */ kmem_cache_t *scullc_cache; /* init_module: create a cache for our quanta */ scullc_cache = kmem_cache_create("scullc", scullc_quantum, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); /* no ctor/dtor */ if (!scullc_cache) { result = -ENOMEM; goto fail_malloc2; } /* cleanup_module: release the cache of our quanta */ kmem_cache_destroy(scullc_cache);
The main differences in passing from scull to scullc are a slight speed improvement and better memory use. Since quanta are allocated from a pool of memory fragments of exactly the right size, their placement in memory is as dense as possible, as opposed to scull quanta, which bring in an unpredictable memory fragmentation.
Get Linux Device Drivers, Second Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.