How Can a Kernel Module Test kmalloc() Limits? – Best Linux Device Drivers Training in Hyderabad

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Empirically Finding Your Kernel’s kmalloc() Ceiling
Build a real kernel module and measure it yourself — Free Linux Device Drivers Course by EmbeddedPathashala
Difficulty: Intermediate
Reading time: 14 min
Kernel version: 6.x

In Part 1 of this free Linux kernel development course lecture, we worked out on paper why kmalloc() has a size ceiling and how PAGE_SIZE and MAX_ORDER combine to produce it. Theory is useful, but as any experienced kernel developer will tell you, the only way to really trust a number is to measure it on your own running system. In this part, we design and build an original kernel module that empirically discovers exactly how much memory a single kmalloc() call can hand out on your machine.

kmalloc testing kernel module
free linux kernel development course
free linux device drivers course
free embedded systems course
module_param
dmesg debugging

What You Will Learn

How to design a “probe until failure” style kernel module
Using module_param to make the step size configurable at load time
Reading and interpreting the results through dmesg
Why your measured ceiling may differ slightly from the theoretical one
Safe experimentation practices so you don’t destabilize your test machine

Prerequisites

You should be comfortable compiling and loading an out-of-tree kernel module, and you should have read Part 1 of this lecture (linked above) so the numbers here make sense. Run this experiment in a virtual machine or a spare test system, not on anything you rely on for daily work — while safe by design, kernel module experimentation always carries some risk.

Designing the Experiment

The idea is simple: start with a small allocation size, request it with kmalloc(), immediately free it, then increase the requested size by a fixed step and repeat. The moment kmalloc() returns NULL, we know we have found (or nearly found) the ceiling for that specific run. We log every successful size to dmesg so we can watch it climb in real time.

Probe-Until-Failure Flow
Start size = 0
→
kmalloc(size)
→
Success? log & kfree()
→
size += step
NULL returned → stop, report last successful size

A Configurable, Original Test Module

Here is a complete, original kernel module written specifically for this lecture. It uses module_param so you can control the step size from the command line without recompiling, which makes it easy to run the experiment at several different granularities.

// epath_kmalloc_probe.c
// EmbeddedPathashala — kmalloc() ceiling probe module

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>

static unsigned long probe_step = 65536; /* 64 KB default step */
module_param(probe_step, ulong, 0644);
MODULE_PARM_DESC(probe_step, "Byte increment applied on every successful allocation");

static size_t last_good_size;

static int __init epath_probe_init(void)
{
    size_t current_size = probe_step;
    void *buf;

    pr_info("epath_kmalloc_probe: starting, step=%lu bytes\n", probe_step);

    while (true) {
        buf = kmalloc(current_size, GFP_KERNEL);
        if (!buf) {
            pr_alert("epath_kmalloc_probe: FAILED at %zu bytes (%zu KB)\n",
                     current_size, current_size / 1024);
            break;
        }

        last_good_size = current_size;
        pr_info("epath_kmalloc_probe: OK   %8zu bytes (%4zu KB)\n",
                current_size, current_size / 1024);

        kfree(buf);
        current_size += probe_step;
    }

    pr_info("epath_kmalloc_probe: last successful size = %zu bytes (%zu KB)\n",
            last_good_size, last_good_size / 1024);

    /* Returning an error here is intentional: it keeps the module from
     * staying resident, since its only job is to run the probe once. */
    return -ENOMEM;
}

static void __exit epath_probe_exit(void)
{
    /* Nothing to clean up — init never leaves anything allocated. */
}

module_init(epath_probe_init);
module_exit(epath_probe_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Empirically probes the kmalloc() maximum single-call allocation size");

Building and Running the Probe

Build the module against your running kernel’s headers, then load it and read the results from the kernel log:

$ make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
$ sudo insmod epath_kmalloc_probe.ko probe_step=131072
$ dmesg | tail -30
$ sudo rmmod epath_kmalloc_probe 2>/dev/null   # module refuses to stay loaded, this is expected

Why does insmod report an error? Because epath_probe_init() deliberately returns -ENOMEM once the probe completes. This is a normal and expected part of this specific module’s design — the whole point is to run the probe once and report through dmesg, not to stay resident. You do not need to run rmmod afterward, though it is harmless to try.

Interpreting the Output

Observation What it tells you
Last successful size lands close to a few MB Matches the theoretical KMALLOC_MAX_SIZE ceiling from Part 1
Failure happens earlier than expected The system is fragmented, or under memory pressure from other workloads
Very small probe_step values Gives a more precise ceiling, but produces a much longer dmesg log

Real-World Use Cases

  • Board bring-up: confirming actual allocator behavior on new embedded hardware before committing a driver design to it.
  • Regression testing: catching an unexpected kmalloc ceiling change after a kernel version upgrade in your product.
  • Teaching and learning: exactly the use case for this free embedded systems course — turning an abstract macro into something you watch happen live in dmesg.

Common Mistakes and Troubleshooting

  • Using a step size that is too small: the loop will run for a very long time and flood dmesg; start with a larger step, then narrow it down.
  • Running this on a production or shared system: the repeated allocate/free cycle briefly stresses the allocator; always use a disposable test VM.
  • Forgetting the module intentionally fails to load: seeing “Cannot allocate memory” from insmod after the probe runs is expected behavior here, not a bug.
  • Comparing results across different machines directly: the ceiling depends on PAGE_SIZE, MAX_ORDER, and current fragmentation, all of which vary by hardware and uptime.

Best Practices

  1. Always test in a disposable VM or spare board, never on a system you depend on.
  2. Run the probe more than once and on a freshly booted system for the most representative ceiling.
  3. Pair a coarse first pass with a fine-grained second pass once you know roughly where the ceiling sits.
  4. Log both the requested size and the returned pointer value if you want to additionally observe allocation locality.

Performance and Security Considerations

Performance: the repeated allocate-then-immediately-free pattern used here is intentionally simple and is not representative of real driver workloads — it exists purely to map the ceiling, not to benchmark throughput.

Security: never adapt a “probe until failure” style loop into production driver code driven by user-controlled input; an attacker-influenced step size or starting size could be used to intentionally stress the allocator.

Summary / Key Takeaways

A simple allocate-log-free loop can empirically discover your kmalloc ceiling
module_param lets you tune the step size without recompiling
The measured result should land close to the theoretical KMALLOC_MAX_SIZE from Part 1
Always experiment on disposable systems, never production machines

Conclusion

Pairing theory with hands-on measurement is what separates guessing from genuinely understanding the Linux kernel’s memory allocator. With this two-part lecture, you now know both why the kmalloc() ceiling exists and how to independently verify it on any kernel you have access to — a skill that will serve you well throughout the rest of this free Linux kernel development course and free Linux device drivers course.

FAQ

Q1. Why does the probe module return -ENOMEM from init even though it worked correctly?
It is deliberate — the module’s job is to run once, log the results, and not stay resident, so returning an error is the cleanest way to prevent it from loading permanently.

Q2. Will I get the exact same ceiling every time I run the probe?
Not necessarily. A freshly booted system typically gives the closest result to the theoretical maximum; a long-running, fragmented system may show a lower ceiling.

Q3. Can I adapt this module to test vmalloc() instead?
Yes, conceptually — swap kmalloc()/kfree() for vmalloc()/vfree(), though you should expect a much higher ceiling since physical contiguity is no longer required.

Q4. Is it safe to run this probe on a Raspberry Pi or other embedded board?
Yes, as long as it is a test board and not something running a production workload — the module frees every allocation immediately after use.

Q5. What GFP flag should I use if I want to test allocation behavior from interrupt context?
Replace GFP_KERNEL with GFP_ATOMIC, but be aware the ceiling and failure behavior can differ, since atomic allocations cannot sleep or wait for reclaim.

Q6. Why use module_param instead of a hardcoded step size?
It lets you re-run the experiment at different granularities from the command line, without rebuilding the module each time.

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Leave a Reply

Your email address will not be published. Required fields are marked *