Chapter 4: Memory Management
Table of Contents
- Kernel Memory Architecture
- Memory Allocation Functions
- kmalloc and kfree
- vmalloc and vfree
- Memory Pools and Slabs
- Page Allocation
- DMA-Capable Memory
- Per-CPU Variables
- Memory Mapping Internals
Kernel Memory Architecture
Virtual Memory Layout
User Space (0x0000000000000000 - 0x00007fffffffffff) [128 TB on x86-64]
├── User process memory
├── Heap
├── Stack
└── Shared libraries
Kernel Space (0xffff800000000000 - 0xffffffffffffffff) [128 TB on x86-64]
├── Direct mapping of all physical memory
├── vmalloc area
├── Kernel code and data
├── Module memory
└── Per-CPU data
Memory Zones
The kernel divides physical memory into zones:
/*
* Memory Zones:
*
* ZONE_DMA - Low memory for DMA (0-16MB on x86)
* ZONE_DMA32 - DMA memory accessible by 32-bit devices (0-4GB)
* ZONE_NORMAL - Normal memory (directly mapped)
* ZONE_HIGHMEM - High memory (>896MB on 32-bit systems, not on 64-bit)
* ZONE_MOVABLE - Movable memory for hot-plug
*/
Physical vs Virtual Memory
Virtual Address (used by driver)
↓
Page Table (MMU translation)
↓
Physical Address (actual RAM)
Important:
- Kernel virtual addresses are NOT physical addresses
- Use virt_to_phys() / phys_to_virt() for conversion
- DMA requires physical addresses
Memory Allocation Functions
Overview of Allocation Functions
/*
* Choosing the right allocation function:
*
* Small allocations (<PAGE_SIZE):
* → Use kmalloc() - Fast, physically contiguous
*
* Large allocations (>PAGE_SIZE):
* → Use vmalloc() - May not be physically contiguous
* → Or use __get_free_pages() for physically contiguous
*
* DMA buffers:
* → Use dma_alloc_coherent() - DMA-safe memory
*
* Frequent same-size allocations:
* → Use kmem_cache (slab allocator) - Efficient
*
* Very large allocations:
* → Consider using vmalloc() or allocating pages directly
*/
GFP Flags (Get Free Pages)
#include <linux/gfp.h>
/*
* GFP flags control allocation behavior
*
* Most commonly used:
*/
/* GFP_KERNEL - Normal allocation, can sleep */
/* Use in: Process context, when sleeping is OK */
void *buffer = kmalloc(size, GFP_KERNEL);
/* GFP_ATOMIC - Allocation that cannot sleep */
/* Use in: Interrupt handlers, atomic context */
void *buffer = kmalloc(size, GFP_ATOMIC);
/* GFP_DMA - Allocate from DMA zone */
void *dma_buffer = kmalloc(size, GFP_DMA);
/* GFP_USER - Allocate user space memory */
void *user_mem = kmalloc(size, GFP_USER);
/*
* Flag modifiers (can be OR'd):
*
* __GFP_ZERO - Zero the allocated memory
* __GFP_NOWARN - Suppress allocation failure warnings
* __GFP_NORETRY - Don't retry if allocation fails
* __GFP_RETRY_MAYFAIL - May retry but can fail
* __GFP_HIGH - High priority allocation
* __GFP_IO - Can perform I/O operations
* __GFP_FS - Can call filesystem operations
*/
/* Common combinations: */
GFP_KERNEL = __GFP_RECLAIM | __GFP_IO | __GFP_FS
GFP_ATOMIC = __GFP_HIGH | __GFP_ATOMIC
GFP_NOIO = __GFP_RECLAIM /* Can sleep but no I/O */
GFP_NOFS = __GFP_RECLAIM | __GFP_IO /* Can sleep & I/O but no FS */
/*
* Example: Allocate and zero memory
*/
void *buffer = kmalloc(size, GFP_KERNEL | __GFP_ZERO);
/* Or use kzalloc() which is equivalent */
void *buffer = kzalloc(size, GFP_KERNEL);
kmalloc and kfree
kmalloc - Kernel Memory Allocator
#include <linux/slab.h>
/*
* kmalloc - Allocate kernel memory
*
* @size: Number of bytes to allocate
* @flags: GFP allocation flags
*
* Return: Pointer to allocated memory, or NULL on failure
*
* Properties:
* - Returns physically contiguous memory
* - Fast (uses slab allocator underneath)
* - Limited by physically contiguous memory availability
* - Typical maximum: 4MB (architecture dependent)
* - Memory is from low memory (directly mapped)
*/
void *kmalloc(size_t size, gfp_t flags);
/*
* kzalloc - Allocate and zero memory
*
* Equivalent to: kmalloc(size, flags | __GFP_ZERO)
*/
void *kzalloc(size_t size, gfp_t flags);
/*
* kfree - Free memory allocated by kmalloc/kzalloc
*
* @ptr: Pointer returned by kmalloc/kzalloc
*
* Safe to call with NULL pointer (does nothing)
*/
void kfree(const void *ptr);
/*
* kcalloc - Allocate array and zero
*
* @n: Number of elements
* @size: Size of each element
* @flags: GFP flags
*
* Checks for overflow: n * size
*/
void *kcalloc(size_t n, size_t size, gfp_t flags);
/*
* krealloc - Resize previously allocated memory
*
* @p: Previously allocated memory
* @new_size: New size
* @flags: GFP flags
*
* Return: Pointer to resized memory (may have moved)
*/
void *krealloc(const void *p, size_t new_size, gfp_t flags);
/*
* ksize - Get actual size of allocated memory
*
* @ptr: Pointer to allocated memory
*
* Return: Actual allocated size (may be larger than requested)
*/
size_t ksize(const void *ptr);
kmalloc Examples
/*
* Example 1: Simple allocation
*/
static int simple_alloc_example(void)
{
char *buffer;
/* Allocate 1024 bytes */
buffer = kmalloc(1024, GFP_KERNEL);
if (!buffer) {
pr_err("Failed to allocate memory\n");
return -ENOMEM;
}
/* Use the buffer */
strcpy(buffer, "Hello, kernel!");
pr_info("Buffer contains: %s\n", buffer);
/* Free the buffer */
kfree(buffer);
return 0;
}
/*
* Example 2: Allocating structures
*/
struct my_device {
int id;
char name[32];
void *data;
};
static struct my_device *alloc_device(int id, const char *name)
{
struct my_device *dev;
/* Allocate and zero structure */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
/* Initialize fields */
dev->id = id;
strncpy(dev->name, name, sizeof(dev->name) - 1);
/* Allocate sub-buffer */
dev->data = kmalloc(4096, GFP_KERNEL);
if (!dev->data) {
kfree(dev);
return NULL;
}
return dev;
}
static void free_device(struct my_device *dev)
{
if (dev) {
kfree(dev->data);
kfree(dev);
}
}
/*
* Example 3: Array allocation
*/
static int array_alloc_example(void)
{
int *array;
int i;
int count = 100;
/* Allocate array of integers */
array = kcalloc(count, sizeof(int), GFP_KERNEL);
if (!array)
return -ENOMEM;
/* Initialize array */
for (i = 0; i < count; i++)
array[i] = i * i;
/* Use array */
pr_info("array[10] = %d\n", array[10]);
/* Free array */
kfree(array);
return 0;
}
/*
* Example 4: Resizing allocation
*/
static int resize_example(void)
{
char *buffer;
char *new_buffer;
/* Initial allocation */
buffer = kmalloc(1024, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
strcpy(buffer, "Initial data");
/* Resize to 2048 bytes */
new_buffer = krealloc(buffer, 2048, GFP_KERNEL);
if (!new_buffer) {
kfree(buffer); /* Original buffer still valid */
return -ENOMEM;
}
/* Use resized buffer (original data preserved) */
pr_info("Resized buffer: %s\n", new_buffer);
/* Free (new_buffer is now the valid pointer) */
kfree(new_buffer);
return 0;
}
/*
* Example 5: Error handling best practices
*/
static int proper_error_handling(void)
{
void *buf1 = NULL, *buf2 = NULL, *buf3 = NULL;
int ret = 0;
buf1 = kmalloc(1024, GFP_KERNEL);
if (!buf1) {
ret = -ENOMEM;
goto out;
}
buf2 = kmalloc(2048, GFP_KERNEL);
if (!buf2) {
ret = -ENOMEM;
goto out;
}
buf3 = kmalloc(4096, GFP_KERNEL);
if (!buf3) {
ret = -ENOMEM;
goto out;
}
/* Use buffers... */
out:
/* Clean up (kfree handles NULL) */
kfree(buf3);
kfree(buf2);
kfree(buf1);
return ret;
}
vmalloc and vfree
vmalloc - Virtual Memory Allocator
#include <linux/vmalloc.h>
/*
* vmalloc - Allocate virtually contiguous memory
*
* @size: Number of bytes to allocate
*
* Return: Pointer to allocated memory, or NULL on failure
*
* Properties:
* - Returns virtually contiguous memory (physically may be scattered)
* - Slower than kmalloc (page table manipulation required)
* - Can allocate much larger regions
* - Memory is from high memory (not directly mapped)
* - Cannot be used for DMA (not physically contiguous)
* - Can sleep (uses GFP_KERNEL internally)
*/
void *vmalloc(unsigned long size);
/*
* vzalloc - Allocate and zero virtually contiguous memory
*/
void *vzalloc(unsigned long size);
/*
* vmalloc_user - Allocate memory for mapping to userspace
*/
void *vmalloc_user(unsigned long size);
/*
* vfree - Free memory allocated by vmalloc
*
* @addr: Address returned by vmalloc
*/
void vfree(const void *addr);
/*
* virt_to_phys - Convert virtual address to physical (for kmalloc)
* Cannot be used reliably for vmalloc addresses!
*/
unsigned long virt_to_phys(volatile void *address);
/*
* phys_to_virt - Convert physical address to virtual
*/
void *phys_to_virt(unsigned long address);
vmalloc vs kmalloc
/*
* Comparison: kmalloc vs vmalloc
*/
/* kmalloc:
* + Physically contiguous (can be used for DMA)
* + Fast allocation
* + Direct mapping (no page table overhead)
* - Limited size (typically max 4MB)
* - Can fail if physical memory is fragmented
*/
void *kmalloc_example(void)
{
void *buf;
/* Good: Small allocation */
buf = kmalloc(4096, GFP_KERNEL); /* 1 page */
/* Risky: Large allocation might fail */
buf = kmalloc(16 * 1024 * 1024, GFP_KERNEL); /* 16 MB */
return buf;
}
/* vmalloc:
* + Can allocate very large regions
* + Won't fail due to fragmentation
* + Useful for large buffers
* - NOT physically contiguous (cannot use for DMA)
* - Slower (requires page table setup)
* - More memory overhead (page tables)
*/
void *vmalloc_example(void)
{
void *buf;
/* Good: Large allocation */
buf = vmalloc(100 * 1024 * 1024); /* 100 MB */
/* Bad: Small allocation (overhead not worth it) */
buf = vmalloc(64); /* Wasteful */
return buf;
}
/*
* Decision flowchart:
*
* Need DMA? ──Yes──> Use kmalloc or dma_alloc_coherent
* │
* No
* │
* ↓
* Size < 1 page? ──Yes──> Use kmalloc
* │
* No
* │
* ↓
* Size < 128KB? ──Yes──> Try kmalloc, fallback to vmalloc
* │
* No
* │
* ↓
* Use vmalloc
*/
vmalloc Examples
/*
* Example 1: Large buffer allocation
*/
static int large_buffer_example(void)
{
char *buffer;
size_t size = 10 * 1024 * 1024; /* 10 MB */
/* vmalloc for large buffer */
buffer = vmalloc(size);
if (!buffer) {
pr_err("Failed to allocate %zu bytes\n", size);
return -ENOMEM;
}
pr_info("Allocated %zu bytes at %p\n", size, buffer);
/* Use buffer */
memset(buffer, 0, size);
/* Free buffer */
vfree(buffer);
return 0;
}
/*
* Example 2: Fallback allocation
*/
static void *alloc_buffer_smart(size_t size)
{
void *buffer;
/* Try kmalloc first (faster if available) */
buffer = kmalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (buffer) {
pr_info("Allocated %zu bytes with kmalloc\n", size);
return buffer;
}
/* Fallback to vmalloc for large allocation */
buffer = vmalloc(size);
if (buffer) {
pr_info("Allocated %zu bytes with vmalloc\n", size);
}
return buffer;
}
static void free_buffer_smart(void *buffer, size_t size)
{
/* Determine allocation type and free appropriately */
if (is_vmalloc_addr(buffer)) {
vfree(buffer);
} else {
kfree(buffer);
}
}
/*
* Example 3: User-mappable buffer
*/
static void *alloc_user_buffer(size_t size)
{
void *buffer;
/* Allocate buffer that can be mapped to userspace */
buffer = vmalloc_user(size);
if (!buffer)
return NULL;
/* Initialize */
memset(buffer, 0, size);
return buffer;
}
Memory Pools and Slabs
Slab Allocator (kmem_cache)
The slab allocator is efficient for frequently allocating objects of the same size.
#include <linux/slab.h>
/*
* Memory cache (slab) - For frequent same-size allocations
*
* Benefits:
* - Very fast allocation/deallocation
* - Reduces fragmentation
* - Can have constructor/destructor
* - Cache-friendly (objects stay "warm")
*/
struct kmem_cache {
/* Opaque structure managed by kernel */
};
/*
* kmem_cache_create - Create a memory cache
*
* @name: Cache name (for debugging, appears in /proc/slabinfo)
* @size: Size of each object
* @align: Alignment requirement (0 for default)
* @flags: Cache flags
* @ctor: Constructor function (called when object is allocated)
*
* Return: Pointer to cache, or NULL on failure
*/
struct kmem_cache *kmem_cache_create(const char *name,
unsigned int size,
unsigned int align,
slab_flags_t flags,
void (*ctor)(void *));
/*
* Common flags:
*
* SLAB_HWCACHE_ALIGN - Align objects to cache line
* SLAB_POISON - Fill with poison pattern (debugging)
* SLAB_RED_ZONE - Add red zones for overflow detection
* SLAB_PANIC - Panic if creation fails
* SLAB_RECLAIM_ACCOUNT - Track reclaimable objects
*/
/*
* kmem_cache_alloc - Allocate object from cache
*
* @cachep: Cache to allocate from
* @flags: GFP allocation flags
*
* Return: Pointer to allocated object, or NULL
*/
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);
/*
* kmem_cache_free - Free object back to cache
*
* @cachep: Cache object was allocated from
* @objp: Object to free
*/
void kmem_cache_free(struct kmem_cache *cachep, void *objp);
/*
* kmem_cache_destroy - Destroy cache
*
* @cachep: Cache to destroy
*
* Must free all objects before destroying cache!
*/
void kmem_cache_destroy(struct kmem_cache *cachep);
Slab Allocator Examples
/*
* Example: Using kmem_cache for device structures
*/
struct my_packet {
unsigned int id;
unsigned int length;
unsigned char data[128];
struct list_head list;
};
static struct kmem_cache *packet_cache;
/*
* Initialize packet cache
*/
static int packet_cache_init(void)
{
/* Create cache for packet structures */
packet_cache = kmem_cache_create("my_packet_cache",
sizeof(struct my_packet),
0, /* Default alignment */
SLAB_HWCACHE_ALIGN, /* Cache-line aligned */
NULL); /* No constructor */
if (!packet_cache) {
pr_err("Failed to create packet cache\n");
return -ENOMEM;
}
pr_info("Packet cache created\n");
return 0;
}
/*
* Allocate packet
*/
static struct my_packet *alloc_packet(void)
{
struct my_packet *pkt;
/* Allocate from cache */
pkt = kmem_cache_alloc(packet_cache, GFP_KERNEL);
if (!pkt)
return NULL;
/* Initialize packet */
memset(pkt, 0, sizeof(*pkt));
INIT_LIST_HEAD(&pkt->list);
return pkt;
}
/*
* Free packet
*/
static void free_packet(struct my_packet *pkt)
{
if (pkt)
kmem_cache_free(packet_cache, pkt);
}
/*
* Cleanup packet cache
*/
static void packet_cache_destroy(void)
{
/*
* Important: Free all allocated packets before destroying cache!
*/
kmem_cache_destroy(packet_cache);
pr_info("Packet cache destroyed\n");
}
/*
* Example: Using constructor
*/
struct my_object {
spinlock_t lock;
struct list_head list;
int ref_count;
void *data;
};
/* Constructor - called when object is first allocated */
static void my_object_ctor(void *obj)
{
struct my_object *o = obj;
/* Initialize fields that should always be initialized */
spin_lock_init(&o->lock);
INIT_LIST_HEAD(&o->list);
o->ref_count = 0;
o->data = NULL;
}
static struct kmem_cache *object_cache;
static int object_cache_init(void)
{
object_cache = kmem_cache_create("my_object_cache",
sizeof(struct my_object),
0,
SLAB_HWCACHE_ALIGN,
my_object_ctor); /* Use constructor */
return object_cache ? 0 : -ENOMEM;
}
/*
* Complete example with module
*/
static struct kmem_cache *my_cache;
static struct my_packet *packets[10];
static int __init cache_example_init(void)
{
int i;
/* Create cache */
if (packet_cache_init() < 0)
return -ENOMEM;
/* Allocate some packets */
for (i = 0; i < 10; i++) {
packets[i] = alloc_packet();
if (!packets[i]) {
pr_err("Failed to allocate packet %d\n", i);
goto cleanup;
}
packets[i]->id = i;
pr_info("Allocated packet %d\n", i);
}
return 0;
cleanup:
for (i = 0; i < 10; i++) {
if (packets[i])
free_packet(packets[i]);
}
packet_cache_destroy();
return -ENOMEM;
}
static void __exit cache_example_exit(void)
{
int i;
/* Free all packets */
for (i = 0; i < 10; i++) {
if (packets[i]) {
free_packet(packets[i]);
pr_info("Freed packet %d\n", i);
}
}
/* Destroy cache */
packet_cache_destroy();
}
module_init(cache_example_init);
module_exit(cache_example_exit);
Memory Pools (mempool)
Memory pools guarantee minimum number of preallocated objects.
#include <linux/mempool.h>
/*
* mempool - Guaranteed memory allocation
*
* Use when you need to guarantee allocation success
* Example: In critical paths where allocation failure is unacceptable
*/
typedef struct mempool_s mempool_t;
/*
* mempool_create - Create memory pool
*
* @min_nr: Minimum number of elements to preallocate
* @alloc_fn: Allocation function
* @free_fn: Free function
* @pool_data: Data for allocation function
*/
mempool_t *mempool_create(int min_nr,
mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn,
void *pool_data);
/*
* mempool_create_slab_pool - Create pool using slab cache
*/
mempool_t *mempool_create_slab_pool(int min_nr, struct kmem_cache *cache);
/*
* mempool_alloc - Allocate from pool
*
* Guaranteed to succeed if called with GFP_WAIT
*/
void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask);
/*
* mempool_free - Return object to pool
*/
void mempool_free(void *element, mempool_t *pool);
/*
* mempool_destroy - Destroy pool
*/
void mempool_destroy(mempool_t *pool);
/*
* Example: Creating mempool with slab cache
*/
static struct kmem_cache *my_cache;
static mempool_t *my_pool;
static int mempool_example_init(void)
{
/* Create slab cache */
my_cache = kmem_cache_create("my_cache",
sizeof(struct my_packet),
0, 0, NULL);
if (!my_cache)
return -ENOMEM;
/* Create mempool with 32 preallocated objects */
my_pool = mempool_create_slab_pool(32, my_cache);
if (!my_pool) {
kmem_cache_destroy(my_cache);
return -ENOMEM;
}
pr_info("Mempool created with 32 objects\n");
return 0;
}
static void mempool_example_exit(void)
{
mempool_destroy(my_pool);
kmem_cache_destroy(my_cache);
}
Page Allocation
Direct Page Allocation
#include <linux/gfp.h>
/*
* __get_free_pages - Allocate 2^order pages
*
* @gfp_mask: GFP flags
* @order: Order of allocation (0 = 1 page, 1 = 2 pages, 2 = 4 pages, etc.)
*
* Return: Address of first page, or 0 on failure
*
* Maximum order is typically 10 or 11 (4MB or 8MB)
*/
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
/*
* __get_free_page - Allocate single page
*/
#define __get_free_page(gfp_mask) \
__get_free_pages((gfp_mask), 0)
/*
* get_zeroed_page - Allocate and zero single page
*/
unsigned long get_zeroed_page(gfp_t gfp_mask);
/*
* free_pages - Free pages allocated by __get_free_pages
*/
void free_pages(unsigned long addr, unsigned int order);
/*
* free_page - Free single page
*/
#define free_page(addr) free_pages((addr), 0)
/*
* alloc_pages - Allocate pages (returns struct page *)
*/
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);
/*
* alloc_page - Allocate single page (returns struct page *)
*/
#define alloc_page(gfp_mask) alloc_pages((gfp_mask), 0)
/*
* __free_pages - Free pages (struct page version)
*/
void __free_pages(struct page *page, unsigned int order);
/*
* __free_page - Free single page
*/
#define __free_page(page) __free_pages((page), 0)
/*
* Page address conversion
*/
void *page_address(struct page *page); /* Get virtual address */
struct page *virt_to_page(void *addr); /* Get page struct */
Page Allocation Examples
/*
* Example 1: Allocate single page
*/
static int single_page_example(void)
{
unsigned long page_addr;
/* Allocate one page */
page_addr = __get_free_page(GFP_KERNEL);
if (!page_addr) {
pr_err("Failed to allocate page\n");
return -ENOMEM;
}
pr_info("Allocated page at 0x%lx\n", page_addr);
/* Use page (page_addr is virtual address) */
memset((void *)page_addr, 0, PAGE_SIZE);
/* Free page */
free_page(page_addr);
return 0;
}
/*
* Example 2: Allocate multiple contiguous pages
*/
static int multi_page_example(void)
{
unsigned long pages;
unsigned int order = 2; /* 2^2 = 4 pages */
size_t size = PAGE_SIZE << order; /* 4 * PAGE_SIZE */
/* Allocate 4 contiguous pages */
pages = __get_free_pages(GFP_KERNEL, order);
if (!pages) {
pr_err("Failed to allocate %u pages\n", 1 << order);
return -ENOMEM;
}
pr_info("Allocated %zu bytes (%u pages) at 0x%lx\n",
size, 1 << order, pages);
/* Use pages */
memset((void *)pages, 0, size);
/* Free pages (must use same order!) */
free_pages(pages, order);
return 0;
}
/*
* Example 3: Using struct page
*/
static int page_struct_example(void)
{
struct page *page;
void *virt_addr;
/* Allocate page (returns struct page *) */
page = alloc_page(GFP_KERNEL);
if (!page) {
pr_err("Failed to allocate page\n");
return -ENOMEM;
}
/* Get virtual address */
virt_addr = page_address(page);
pr_info("Allocated page, virt_addr = %p\n", virt_addr);
/* Use page */
memset(virt_addr, 0, PAGE_SIZE);
/* Free page */
__free_page(page);
return 0;
}
DMA-Capable Memory
DMA Memory Allocation
#include <linux/dma-mapping.h>
/*
* dma_alloc_coherent - Allocate DMA-coherent memory
*
* @dev: Device structure
* @size: Size to allocate
* @dma_handle: Output - DMA address for device
* @gfp: GFP flags
*
* Return: Virtual address, or NULL on failure
*
* Properties:
* - Memory is accessible by both CPU and device
* - Coherent (no cache management needed)
* - Expensive (may disable caching)
* - Use for: Control structures, descriptors
*/
void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp);
/*
* dma_free_coherent - Free DMA-coherent memory
*/
void dma_free_coherent(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_handle);
/*
* dma_map_single - Map buffer for DMA
*
* For streaming DMA (single buffer)
* Use for: Large data transfers
*/
dma_addr_t dma_map_single(struct device *dev, void *ptr,
size_t size, enum dma_data_direction dir);
void dma_unmap_single(struct device *dev, dma_addr_t addr,
size_t size, enum dma_data_direction dir);
/*
* DMA directions:
*
* DMA_TO_DEVICE - Data is sent to device
* DMA_FROM_DEVICE - Data is received from device
* DMA_BIDIRECTIONAL - Data moves both ways
* DMA_NONE - No DMA transfer
*/
DMA Examples
/*
* Example: Allocating DMA buffer
*/
struct my_dma_buffer {
void *virt_addr; /* CPU virtual address */
dma_addr_t dma_addr; /* Device DMA address */
size_t size;
};
static int alloc_dma_buffer(struct device *dev,
struct my_dma_buffer *buf,
size_t size)
{
/* Allocate DMA-coherent memory */
buf->virt_addr = dma_alloc_coherent(dev, size,
&buf->dma_addr,
GFP_KERNEL);
if (!buf->virt_addr) {
dev_err(dev, "Failed to allocate DMA buffer\n");
return -ENOMEM;
}
buf->size = size;
dev_info(dev, "DMA buffer: virt=%p, dma=%pad, size=%zu\n",
buf->virt_addr, &buf->dma_addr, buf->size);
return 0;
}
static void free_dma_buffer(struct device *dev,
struct my_dma_buffer *buf)
{
if (buf->virt_addr) {
dma_free_coherent(dev, buf->size,
buf->virt_addr, buf->dma_addr);
buf->virt_addr = NULL;
}
}
/*
* DMA will be covered in more detail in Chapter 7
*/
Per-CPU Variables
Per-CPU Memory
#include <linux/percpu.h>
/*
* Per-CPU variables - Separate copy for each CPU
*
* Benefits:
* - No locking needed (each CPU has own copy)
* - Cache-friendly (no cache line bouncing)
* - Fast access
*
* Use for: Statistics, per-CPU caches, temporary buffers
*/
/*
* DEFINE_PER_CPU - Define per-CPU variable
*/
DEFINE_PER_CPU(int, my_percpu_var);
/*
* get_cpu_var - Access per-CPU variable (disables preemption)
* put_cpu_var - Release per-CPU variable (enables preemption)
*/
int value = get_cpu_var(my_percpu_var);
/* ... use value ... */
put_cpu_var(my_percpu_var);
/*
* per_cpu - Access per-CPU variable for specific CPU
*/
int value = per_cpu(my_percpu_var, cpu_id);
/*
* alloc_percpu - Dynamically allocate per-CPU data
*/
void __percpu *alloc_percpu(type);
/*
* free_percpu - Free per-CPU data
*/
void free_percpu(void __percpu *ptr);
/*
* Example: Per-CPU statistics
*/
struct stats {
unsigned long packets;
unsigned long bytes;
unsigned long errors;
};
static DEFINE_PER_CPU(struct stats, device_stats);
static void update_stats(unsigned long bytes)
{
struct stats *s;
/* Get per-CPU stats (disables preemption) */
s = &get_cpu_var(device_stats);
s->packets++;
s->bytes += bytes;
/* Release (enables preemption) */
put_cpu_var(device_stats);
}
static void print_total_stats(void)
{
struct stats total = {0};
int cpu;
/* Sum stats from all CPUs */
for_each_possible_cpu(cpu) {
struct stats *s = &per_cpu(device_stats, cpu);
total.packets += s->packets;
total.bytes += s->bytes;
total.errors += s->errors;
}
pr_info("Total: packets=%lu, bytes=%lu, errors=%lu\n",
total.packets, total.bytes, total.errors);
}
Memory Mapping Internals
Understanding Virtual Memory Areas
/*
* struct vm_area_struct - Represents a virtual memory region
*/
struct vm_area_struct {
unsigned long vm_start; /* Start address */
unsigned long vm_end; /* End address (exclusive) */
unsigned long vm_flags; /* Flags (VM_READ, VM_WRITE, etc.) */
struct file *vm_file; /* File being mapped (if any) */
void *vm_private_data; /* Driver private data */
const struct vm_operations_struct *vm_ops;
/* ... more fields ... */
};
/*
* Common VM flags:
*
* VM_READ, VM_WRITE, VM_EXEC - Access permissions
* VM_SHARED - Modifications are shared
* VM_IO - Memory mapped I/O
* VM_RESERVED - Don't swap out
* VM_DONTCOPY - Don't copy on fork
* VM_DONTEXPAND - Cannot grow with mremap
*/
Summary
In this chapter, you learned:
✅ Memory Architecture: Zones, virtual vs physical memory
✅ kmalloc/kfree: Fast physically contiguous allocation
✅ vmalloc/vfree: Large virtually contiguous allocation
✅ Slab Allocator: Efficient same-size object caching
✅ Page Allocation: Direct page-level allocation
✅ DMA Memory: Device-accessible memory
✅ Per-CPU Variables: Lock-free per-processor data
Key Takeaways
- Choose appropriate allocator based on size and requirements
- Always check return values - allocation can fail
- Free all allocated memory in cleanup paths
- Use GFP_KERNEL when you can sleep, GFP_ATOMIC in atomic context
- DMA requires special memory allocation functions
Next Steps
Proceed to 05-concurrency.md to learn about synchronization, locking, and concurrent access to shared data.
Quick Reference
Allocation Functions
/* Small allocations */
void *kmalloc(size, GFP_KERNEL);
void *kzalloc(size, GFP_KERNEL);
void kfree(ptr);
/* Large allocations */
void *vmalloc(size);
void vfree(ptr);
/* Page allocations */
unsigned long __get_free_pages(GFP_KERNEL, order);
void free_pages(addr, order);
/* DMA allocations */
void *dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
void dma_free_coherent(dev, size, cpu_addr, dma_handle);
/* Slab allocator */
struct kmem_cache *kmem_cache_create(name, size, align, flags, ctor);
void *kmem_cache_alloc(cache, GFP_KERNEL);
void kmem_cache_free(cache, obj);
void kmem_cache_destroy(cache);
GFP Flags
GFP_KERNEL /* Can sleep */
GFP_ATOMIC /* Cannot sleep */
GFP_DMA /* DMA-capable memory */
__GFP_ZERO /* Zero memory */