Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ obj-m += devicetree.o
obj-m += dma.o
obj-m += blkram.o
obj-m += vnetloop.o
obj-m += kmem_cache.o

KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(CURDIR)
Expand Down
70 changes: 70 additions & 0 deletions examples/kmem_cache.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* kmem_cache.c - Demonstrates the use of the slab allocator.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>

struct my_custom_struct {
int id;
char name[32];
};

static struct kmem_cache *my_cachep;
static struct my_custom_struct *obj1, *obj2;

static int __init kmem_cache_example_init(void)
{
pr_info("kmem_cache_example: Initializing module\n");

my_cachep =
kmem_cache_create("my_custom_cache", sizeof(struct my_custom_struct), 0,
SLAB_HWCACHE_ALIGN, NULL);

if (!my_cachep) {
pr_err("kmem_cache_example: Failed to create cache\n");
return -ENOMEM;
}

obj1 = kmem_cache_alloc(my_cachep, GFP_KERNEL);
obj2 = kmem_cache_alloc(my_cachep, GFP_KERNEL);

if (!obj1 || !obj2) {
pr_err("kmem_cache_example: Failed to allocate objects\n");
if (obj1)
kmem_cache_free(my_cachep, obj1);
if (obj2)
kmem_cache_free(my_cachep, obj2);
kmem_cache_destroy(my_cachep);
return -ENOMEM;
}

obj1->id = 1;
obj2->id = 2;
pr_info("kmem_cache_example: Allocated obj1 at %p (id: %d)\n", obj1,
obj1->id);
pr_info("kmem_cache_example: Allocated obj2 at %p (id: %d)\n", obj2,
obj2->id);

return 0;
}

static void __exit kmem_cache_example_exit(void)
{
if (obj1)
kmem_cache_free(my_cachep, obj1);
if (obj2)
kmem_cache_free(my_cachep, obj2);

if (my_cachep)
kmem_cache_destroy(my_cachep);

pr_info("kmem_cache_example: Cleaned up and exited\n");
}

module_init(kmem_cache_example_init);
module_exit(kmem_cache_example_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kuan-Wei Chiu <visitorckw@gmail.com>");
MODULE_DESCRIPTION("A simple example of kmem_cache");
17 changes: 17 additions & 0 deletions lkmpg.tex
Original file line number Diff line number Diff line change
Expand Up @@ -2480,6 +2480,23 @@ \section{Memory Allocation and Ownership}
Similarly, driver authors should resist the temptation to guess at allocator
internals or to depend on low-level layout details that are not part of the API.

When a module needs to frequently allocate and free objects of the same size---such
as custom data structures, network packet descriptors, or state containers---using
\cpp|kmalloc()| repeatedly can lead to memory fragmentation and unnecessary overhead.
For these highly repetitive allocations, the slab allocator interface is the
correct tool.
Driver authors should use \cpp|kmem_cache_create()| during module initialization
to set up a dedicated cache for their specific structure.
Once the cache is created, \cpp|kmem_cache_alloc()| and \cpp|kmem_cache_free()|
provide extremely fast, fragmentation-resistant memory management for those objects.
It is crucial to symmetrically destroy the cache with \cpp|kmem_cache_destroy()|
during module cleanup to prevent memory leaks.

For a practical demonstration of creating a custom slab cache, allocating objects from
it, and ensuring proper cleanup, see the following example.

\samplec{examples/kmem_cache.c}

Ownership is just as important as allocation.
In Linux 5.10 and later, many drivers benefit from managed-resource helpers such as
\cpp|devm_kzalloc()| and friends when they are written as proper device-model
Expand Down