Massive training corpus for AI coding models containing: - 10 JSONL training datasets (641+ examples across coding, reasoning, planning, architecture, communication, debugging, security, workflows, error handling, UI/UX) - 11 agent behavior specifications (explorer, planner, reviewer, debugger, executor, UI designer, Linux admin, kernel engineer, security architect, automation engineer, API architect) - 6 skill definition files (coding, API engineering, kernel, Linux server, security architecture, server automation, UI/UX) - Master README with project origin story and philosophy Built by Pony Alpha 2 to help AI models learn expert-level coding approaches.
30 KiB
30 KiB
Kernel Engineering Expert Skill
Activation Criteria
Activate this skill when the user:
- Requests kernel module development or loading
- Needs device driver implementation (char, block, network)
- Asks for kernel debugging techniques (ftrace, perf, eBPF)
- Requires memory management optimization
- Needs synchronization primitives (spinlocks, mutexes, RCU)
- Asks for filesystem development or VFS integration
- Requires network stack programming
- Needs interrupt handling or bottom half implementation
- Asks for kernel security mechanisms (LSM, SELinux)
- Requires kernel performance optimization
- Needs kernel-space / user-space communication
- Is working on: embedded systems, high-performance computing, custom hardware drivers, real-time systems
Core Methodology
1. Kernel Module Development
Basic Module Structure
// hello_kernel.c - Basic Kernel Module
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#define MODULE_NAME "hello_kernel"
#define MODULE_LICENSE "GPL"
#define MODULE_AUTHOR "Kernel Engineer"
#define MODULE_DESCRIPTION "A simple kernel module"
#define MODULE_VERSION "1.0"
// Module parameters
static int debug_level = 0;
module_param(debug_level, int, 0644);
MODULE_PARM_DESC(debug_level, "Debug level (0-3)");
static char *device_name = "kernel0";
module_param(device_name, charp, 0644);
MODULE_PARM_DESC(device_name, "Device name");
// Module initialization
static int __init hello_init(void)
{
pr_info("%s: Module loaded\n", MODULE_NAME);
pr_info("%s: Device name: %s, debug level: %d\n",
MODULE_NAME, device_name, debug_level);
return 0;
}
// Module cleanup
static void __exit hello_exit(void)
{
pr_info("%s: Module unloaded\n", MODULE_NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_INFO(intree, "Y");
MODULE_LICENSE(MODULE_LICENSE);
MODULE_AUTHOR(MODULE_AUTHOR);
MODULE_DESCRIPTION(MODULE_DESCRIPTION);
MODULE_VERSION(MODULE_VERSION);
Makefile for Kernel Modules
# Makefile for kernel module
obj-m += hello_kernel.o
# Kernel build directory
KDIR := /lib/modules/$(shell uname -r)/build
# Current directory
PWD := $(shell pwd)
# Default target
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
# Clean build artifacts
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
rm -f Module.symvers modules.order
# Load module
load:
insmod hello_kernel.ko
dmesg | tail -10
# Unload module
unload:
rmmod hello_kernel
dmesg | tail -10
# Reload module
reload: unload load
# Show module info
info:
modinfo hello_kernel.ko
.PHONY: all clean load unload reload info
2. Character Device Driver
Complete Character Driver Implementation
// char_dev.c - Character Device Driver
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/mutex.h>
#define DEVICE_NAME "chardev"
#define CLASS_NAME "chardev_class"
#define BUF_LEN 1024
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kernel Engineer");
MODULE_DESCRIPTION("Character device driver");
MODULE_VERSION("1.0");
// Global variables
static int major_number;
static struct class *chardev_class = NULL;
static struct device *chardev_device = NULL;
static struct cdev chardev_cdev;
static char device_buffer[BUF_LEN];
static int buffer_pointer = 0;
static DEFINE_MUTEX(chardev_mutex);
// Function prototypes
static int chardev_open(struct inode *, struct file *);
static int chardev_release(struct inode *, struct file *);
static ssize_t chardev_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t chardev_write(struct file *, const char __user *, size_t, loff_t *);
static loff_t chardev_llseek(struct file *, loff_t, int);
// File operations structure
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = chardev_open,
.release = chardev_release,
.read = chardev_read,
.write = chardev_write,
.llseek = chardev_llseek,
};
// Device open
static int chardev_open(struct inode *inodep, struct file *filep)
{
if (!mutex_trylock(&chardev_mutex)) {
pr_alert("%s: Device busy\n", DEVICE_NAME);
return -EBUSY;
}
buffer_pointer = 0;
pr_info("%s: Device opened\n", DEVICE_NAME);
return 0;
}
// Device release
static int chardev_release(struct inode *inodep, struct file *filep)
{
mutex_unlock(&chardev_mutex);
pr_info("%s: Device closed\n", DEVICE_NAME);
return 0;
}
// Device read
static ssize_t chardev_read(struct file *filep, char __user *buffer,
size_t len, loff_t *offset)
{
int bytes_read = 0;
if (*offset >= buffer_pointer) {
return 0; // EOF
}
if (*offset + len > BUF_LEN) {
len = BUF_LEN - *offset;
}
if (copy_to_user(buffer, device_buffer + *offset, len) != 0) {
return -EFAULT;
}
*offset += len;
bytes_read = len;
pr_info("%s: Sent %d bytes to user\n", DEVICE_NAME, bytes_read);
return bytes_read;
}
// Device write
static ssize_t chardev_write(struct file *filep, const char __user *buffer,
size_t len, loff_t *offset)
{
if (*offset + len > BUF_LEN) {
len = BUF_LEN - *offset;
}
if (copy_from_user(device_buffer + *offset, buffer, len) != 0) {
return -EFAULT;
}
*offset += len;
buffer_pointer = *offset;
pr_info("%s: Received %zu bytes from user\n", DEVICE_NAME, len);
return len;
}
// Device seek
static loff_t chardev_llseek(struct file *filep, loff_t offset, int orig)
{
loff_t new_pos = 0;
switch (orig) {
case 0: // SEEK_SET
new_pos = offset;
break;
case 1: // SEEK_CUR
new_pos = filep->f_pos + offset;
break;
case 2: // SEEK_END
new_pos = buffer_pointer + offset;
break;
default:
return -EINVAL;
}
if (new_pos < 0 || new_pos > BUF_LEN) {
return -EINVAL;
}
filep->f_pos = new_pos;
return new_pos;
}
// Module initialization
static int __init chardev_init(void)
{
int ret = 0;
pr_info("%s: Initializing\n", DEVICE_NAME);
// Allocate dynamic major number
major_number = register_chrdev(0, DEVICE_NAME, &fops);
if (major_number < 0) {
pr_err("%s: Failed to register major number\n", DEVICE_NAME);
return major_number;
}
// Create device class
chardev_class = class_create(CLASS_NAME);
if (IS_ERR(chardev_class)) {
unregister_chrdev(major_number, DEVICE_NAME);
pr_err("%s: Failed to register device class\n", DEVICE_NAME);
return PTR_ERR(chardev_class);
}
// Create device
chardev_device = device_create(chardev_class, NULL,
MKDEV(major_number, 0), NULL, DEVICE_NAME);
if (IS_ERR(chardev_device)) {
class_destroy(chardev_class);
unregister_chrdev(major_number, DEVICE_NAME);
pr_err("%s: Failed to create device\n", DEVICE_NAME);
return PTR_ERR(chardev_device);
}
mutex_init(&chardev_mutex);
pr_info("%s: Device created with major %d\n", DEVICE_NAME, major_number);
return 0;
}
// Module cleanup
static void __exit chardev_exit(void)
{
device_destroy(chardev_class, MKDEV(major_number, 0));
class_unregister(chardev_class);
class_destroy(chardev_class);
unregister_chrdev(major_number, DEVICE_NAME);
mutex_destroy(&chardev_mutex);
pr_info("%s: Exiting\n", DEVICE_NAME);
}
module_init(chardev_init);
module_exit(chardev_exit);
3. Memory Management
Kernel Memory Allocation Patterns
// memory_management.c - Kernel Memory Management
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/gfp.h>
#include <linux/highmem.h>
// Small fixed-size allocation
static void *small_buffer;
// Large allocation example
static void *large_buffer;
// DMA-capable allocation example
static void *dma_buffer;
dma_addr_t dma_handle;
struct page *pages;
void *vaddr;
// Allocation strategies
static int allocate_memory(void)
{
// 1. Small allocations (< 128 bytes) - use kmalloc
small_buffer = kmalloc(64, GFP_KERNEL);
if (!small_buffer) {
pr_err("Failed to allocate small buffer\n");
return -ENOMEM;
}
pr_info("Allocated small buffer: %p\n", small_buffer);
// 2. Medium allocations (128 bytes - 4KB) - use kmalloc
// GFP flags:
// - GFP_KERNEL: Normal kernel allocation, can sleep
// - GFP_ATOMIC: Atomic allocation, cannot sleep (interrupt context)
// - GFP_DMA: DMA-able memory (low 16MB)
// - GFP_HIGHUSER: High memory for userspace pages
large_buffer = kmalloc(8192, GFP_KERNEL);
if (!large_buffer) {
pr_err("Failed to allocate large buffer\n");
kfree(small_buffer);
return -ENOMEM;
}
pr_info("Allocated large buffer: %p\n", large_buffer);
// 3. Very large allocations (> 128KB) - use vmalloc
// Note: vmalloc memory is not contiguous in physical memory
void *very_large = vmalloc(1024 * 1024); // 1 MB
if (!very_large) {
pr_err("Failed to allocate very large buffer\n");
kfree(small_buffer);
kfree(large_buffer);
return -ENOMEM;
}
pr_info("Allocated very large buffer: %p\n", very_large);
// 4. DMA-coherent allocation
// For device DMA, must use physically contiguous memory
dma_buffer = dma_alloc_coherent(NULL, 4096, &dma_handle, GFP_KERNEL);
if (!dma_buffer) {
pr_err("Failed to allocate DMA buffer\n");
vfree(very_large);
kfree(small_buffer);
kfree(large_buffer);
return -ENOMEM;
}
pr_info("Allocated DMA buffer: %p (phys: %pad)\n", dma_buffer, &dma_handle);
// 5. Page-based allocation
pages = alloc_pages(GFP_KERNEL, 2); // 2^2 = 4 pages
if (!pages) {
pr_err("Failed to allocate pages\n");
dma_free_coherent(NULL, 4096, dma_buffer, dma_handle);
vfree(very_large);
kfree(small_buffer);
kfree(large_buffer);
return -ENOMEM;
}
vaddr = page_address(pages);
pr_info("Allocated pages: %p\n", vaddr);
return 0;
}
static void free_memory(void)
{
// Free in reverse order
if (pages) {
__free_pages(pages, 2);
}
if (dma_buffer) {
dma_free_coherent(NULL, 4096, dma_buffer, dma_handle);
}
if (large_buffer) {
kfree(large_buffer);
}
if (small_buffer) {
kfree(small_buffer);
}
}
// Memory copy example
static void memory_copy_example(void)
{
char *src, *dst;
size_t size = 1024;
src = kmalloc(size, GFP_KERNEL);
dst = kmalloc(size, GFP_KERNEL);
if (src && dst) {
// Kernel-to-kernel copy
memcpy(dst, src, size);
// User-kernel copy (in read/write operations)
// copy_from_user(to, from, n);
// copy_to_user(to, from, n);
// Optimized copy for large data
// __memcpy(dst, src, size);
kfree(src);
kfree(dst);
}
}
// Memory mapping example (mmap support)
static void *mmap_buffer;
static size_t mmap_size = PAGE_SIZE;
static int mmap_allocate(void)
{
// Allocate memory that will be mapped to userspace
mmap_buffer = kmalloc(mmap_size, GFP_KERNEL);
if (!mmap_buffer) {
return -ENOMEM;
}
memset(mmap_buffer, 0, mmap_size);
return 0;
}
static void mmap_free(void)
{
kfree(mmap_buffer);
}
static int chardev_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long size = vma->vm_end - vma->vm_start;
if (size > mmap_size) {
return -EINVAL;
}
// Set page attributes
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
// Map memory to userspace
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys(mmap_buffer) >> PAGE_SHIFT,
size, vma->vm_page_prot)) {
return -EAGAIN;
}
return 0;
}
4. Synchronization and Concurrency
Synchronization Primitives
// synchronization.c - Kernel Synchronization
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/rwlock.h>
#include <linux/semaphore.h>
#include <linux/completion.h>
#include <linux/atomic.h>
#include <linux/wait.h>
// 1. Spinlock - for short critical sections, cannot sleep
static DEFINE_SPINLOCK(my_spinlock);
void spinlock_example(void)
{
unsigned long flags;
// Spinlock variant: can be used in interrupt context
spin_lock_irqsave(&my_spinlock, flags);
// Critical section - cannot sleep, no blocking calls
pr_info("In spinlock critical section\n");
spin_unlock_irqrestore(&my_spinlock, flags);
// Regular spinlock (not in interrupt context)
spin_lock(&my_spinlock);
// Critical section
spin_unlock(&my_spinlock);
// Try lock - non-blocking
if (spin_trylock(&my_spinlock)) {
// Got the lock
spin_unlock(&my_spinlock);
}
}
// 2. Mutex - for longer critical sections, can sleep
static DEFINE_MUTEX(my_mutex);
void mutex_example(void)
{
// Lock mutex - can sleep
mutex_lock(&my_mutex);
// Critical section - can sleep, blocking calls allowed
msleep(100); // OK to sleep
mutex_unlock(&my_mutex);
// Try lock - non-blocking
if (mutex_trylock(&my_mutex)) {
// Got the lock
mutex_unlock(&my_mutex);
}
}
// 3. Read-Write Semaphore
static DECLARE_RWSEM(rwsem);
void rwsem_example(void)
{
// Reader lock - multiple readers allowed
down_read(&rwsem);
// Read critical section
up_read(&rwsem);
// Writer lock - exclusive access
down_write(&rwsem);
// Write critical section
up_write(&rwsem);
// Try variants
if (down_read_trylock(&rwsem)) {
up_read(&rwsem);
}
if (down_write_trylock(&rwsem)) {
up_write(&rwsem);
}
}
// 4. Atomic Operations - lock-free synchronization
static atomic_t counter = ATOMIC_INIT(0);
void atomic_example(void)
{
int old, new;
// Atomic increment
atomic_inc(&counter);
// Atomic decrement
atomic_dec(&counter);
// Atomic add
atomic_add(5, &counter);
// Atomic read
old = atomic_read(&counter);
// Atomic exchange
new = atomic_xchg(&counter, 100);
// Atomic compare and exchange
old = atomic_read(&counter);
atomic_cmpxchg(&counter, old, 200);
// Atomic add and return old value
old = atomic_fetch_add(&counter, 10);
}
// 5. Completion - one-to-one synchronization
static struct completion my_completion;
static int wait_thread(void *data)
{
pr_info("Worker: Waiting for signal\n");
wait_for_completion(&my_completion);
pr_info("Worker: Received signal, proceeding\n");
return 0;
}
void completion_example(void)
{
init_completion(&my_completion);
// Start thread that waits
// kthread_run(wait_thread, NULL, "waiter");
msleep(1000);
// Signal completion
complete(&my_completion);
}
// 6. Wait Queue - producer-consumer pattern
static DECLARE_WAIT_QUEUE_HEAD(my_wait_queue);
static int data_ready = 0;
void wait_queue_example(void)
{
// Consumer - wait for data
wait_event_interruptible(my_wait_queue, data_ready != 0);
pr_info("Consumer: Data is ready\n");
data_ready = 0;
// Producer - signal data ready
data_ready = 1;
wake_up_interruptible(&my_wait_queue);
}
// 7. RCU (Read-Copy-Update) - read-mostly data structures
#include <linux/rcupdate.h>
struct rcu_data {
int value;
struct rcu_head rcu;
};
static struct rcu_data *global_data;
static void rcu_reclaim(struct rcu_head *rp)
{
struct rcu_data *data = container_of(rp, struct rcu_data, rcu);
kfree(data);
}
void rcu_example(void)
{
struct rcu_data *new_data, *old_data;
// Reader side - very fast, no locks
rcu_read_lock();
if (global_data) {
pr_info("RCU data: %d\n", global_data->value);
}
rcu_read_unlock();
// Writer side - update with grace period
new_data = kmalloc(sizeof(*new_data), GFP_KERNEL);
if (new_data) {
new_data->value = 42;
// Swap pointers atomically
old_data = global_data;
rcu_assign_pointer(global_data, new_data);
// Wait for readers to finish
call_rcu(&old_data->rcu, rcu_reclaim);
}
}
// 8. Seqlock - for data with many readers and few writers
static DEFINE_SEQLOCK(seqlock_data);
static unsigned long seqlock_timestamp;
void seqlock_example(void)
{
unsigned int seq;
unsigned long timestamp;
// Reader
do {
seq = read_seqbegin(&seqlock_data);
timestamp = seqlock_timestamp;
} while (read_seqretry(&seqlock_data, seq));
// Writer
write_seqlock(&seqlock_data);
seqlock_timestamp = jiffies;
write_sequnlock(&seqlock_data);
}
// Per-CPU variables - avoid locking entirely
#include <linux/percpu.h>
static DEFINE_PER_CPU(unsigned long, per_cpu_counter);
void percpu_example(void)
{
unsigned long *counter;
unsigned long sum = 0;
int cpu;
// Access per-CPU variable
preempt_disable(); // Prevent CPU migration
counter = this_cpu_ptr(&per_cpu_counter);
(*counter)++;
preempt_enable();
// Sum all per-CPU counters
for_each_possible_cpu(cpu) {
counter = per_cpu_ptr(&per_cpu_counter, cpu);
sum += *counter;
}
pr_info("Total count: %lu\n", sum);
}
5. Interrupt Handling
Interrupt Handler Implementation
// interrupt_handler.c - Interrupt Handling
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/delay.h>
#define IRQ_NUMBER 42 // Example IRQ number
// Shared data between interrupt handler and process context
static atomic_t irq_count = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(irq_wait_queue);
// Top-half: Interrupt Service Routine (ISR)
static irqreturn_t my_isr(int irq, void *dev_id)
{
atomic_inc(&irq_count);
wake_up_interruptible(&irq_wait_queue);
pr_info("IRQ %d handled\n", irq);
// Return IRQ_HANDLED if we handled this interrupt
// Return IRQ_NONE if we didn't handle it
return IRQ_HANDLED;
}
// Alternative: Threaded interrupt handler
static irqreturn_t my_isr_thread(int irq, void *dev_id)
{
pr_info("Threaded IRQ handler running\n");
msleep(100); // Can sleep in threaded handler
return IRQ_HANDLED;
}
// Request interrupt
static int request_interrupt(void)
{
int ret;
// Request standard IRQ
ret = request_irq(IRQ_NUMBER,
my_isr,
IRQF_TRIGGER_RISING | IRQF_SHARED,
"my_interrupt",
(void *)my_isr);
if (ret) {
pr_err("Failed to request IRQ %d\n", IRQ_NUMBER);
return ret;
}
// Alternative: Request threaded IRQ
// ret = request_threaded_irq(IRQ_NUMBER,
// my_isr, // Top half
// my_isr_thread, // Bottom half (thread)
// IRQF_TRIGGER_RISING | IRQF_ONESHOT,
// "my_interrupt",
// (void *)my_isr);
return 0;
}
// Free interrupt
static void free_interrupt(void)
{
free_irq(IRQ_NUMBER, (void *)my_isr);
}
// Bottom half: Softirq (not commonly used directly)
static void softirq_handler(struct softirq_action *h)
{
pr_info("Softirq handler\n");
}
// Bottom half: Tasklet
static void my_tasklet_handler(unsigned long data);
static DECLARE_TASKLET(my_tasklet, my_tasklet_handler, 0);
static void my_tasklet_handler(unsigned long data)
{
pr_info("Tasklet handler\n");
}
// Schedule tasklet
static void schedule_tasklet(void)
{
tasklet_schedule(&my_tasklet);
}
// Bottom half: Workqueue
#include <linux/workqueue.h>
static struct workqueue_struct *my_workqueue;
static void my_work_handler(struct work_struct *work);
static DECLARE_WORK(my_work, my_work_handler);
static DECLARE_DELAYED_WORK(my_delayed_work, my_work_handler);
static void my_work_handler(struct work_struct *work)
{
pr_info("Workqueue handler\n");
// Can sleep here
msleep(100);
}
// Initialize workqueue
static int init_workqueue(void)
{
// Create dedicated workqueue
my_workqueue = create_singlethread_workqueue("my_workqueue");
if (!my_workqueue) {
return -ENOMEM;
}
// Queue work on system workqueue
schedule_work(&my_work);
// Queue delayed work
schedule_delayed_work(&my_delayed_work, msecs_to_jiffies(1000));
// Queue work on dedicated workqueue
// queue_work(my_workqueue, &my_work);
return 0;
}
// Cleanup workqueue
static void cleanup_workqueue(void)
{
// Wait for work to complete
cancel_work_sync(&my_work);
cancel_delayed_work_sync(&my_delayed_work);
// Destroy workqueue
if (my_workqueue) {
destroy_workqueue(my_workqueue);
}
}
// GPIO Interrupt example
#define GPIO_PIN 17
static irqreturn_t gpio_isr(int irq, void *data)
{
int state = gpio_get_value(GPIO_PIN);
pr_info("GPIO %d changed to %d\n", GPIO_PIN, state);
return IRQ_HANDLED;
}
static int setup_gpio_interrupt(void)
{
int ret, irq;
// Request GPIO
ret = gpio_request(GPIO_PIN, "my_gpio");
if (ret) {
pr_err("Failed to request GPIO %d\n", GPIO_PIN);
return ret;
}
// Set direction
gpio_direction_input(GPIO_PIN);
// Get IRQ number for GPIO
irq = gpio_to_irq(GPIO_PIN);
// Request IRQ
ret = request_irq(irq, gpio_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"gpio_irq", NULL);
if (ret) {
gpio_free(GPIO_PIN);
return ret;
}
return 0;
}
static void cleanup_gpio_interrupt(void)
{
int irq = gpio_to_irq(GPIO_PIN);
free_irq(irq, NULL);
gpio_free(GPIO_PIN);
}
6. Kernel Debugging
Ftrace Usage
#!/bin/bash
# Kernel Tracing with Ftrace
# Enable ftrace
echo 1 > /proc/sys/kernel/ftrace_enabled
# Available tracers
cat /sys/kernel/debug/tracing/available_tracers
# Use function tracer
echo function > /sys/kernel/debug/tracing/current_tracer
# Filter functions
echo '*sched*' > /sys/kernel/debug/tracing/set_ftrace_filter
# View trace
cat /sys/kernel/debug/tracing/trace
# Clear trace
echo > /sys/kernel/debug/tracing/trace
# Function graph tracer
echo function_graph > /sys/kernel/debug/tracing/current_tracer
# Set graph depth
echo 3 > /sys/kernel/debug/tracing/max_graph_depth
# View graph trace
cat /sys/kernel/debug/tracing/trace_graph
# Trace specific function
echo do_sys_open > /sys/kernel/debug/tracing/set_ftrace_pid
Kernel Code with Tracepoints
// tracing_example.c - Kernel Tracing
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ftrace.h>
// Tracepoint example
#define CREATE_TRACE_POINTS
#include <trace/events/sample.h>
// Trace events
static void trace_events_example(void)
{
int value = 42;
// Custom trace event
trace_sample_event(value, "example data");
pr_info("Generated trace event\n");
}
// Using trace_printk() (for debugging only)
static void trace_printk_example(void)
{
// Only works when tracing is enabled
trace_printk("Debug message: value=%d\n", 42);
}
// Function tracing
static noinline void traced_function(void)
{
pr_info("This function will be traced\n");
}
// Performance counters
static void perf_counter_example(void)
{
// Use perf events to count occurrences
trace_printk("Performance marker\n");
}
eBPF Programs
// eBPF program example (requires bpf() syscall)
// This would be loaded via bpftool or libbpf
// Example: Socket filter eBPF program
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
// BPF program for packet counting
SEC("socket")
int bpf_prog1(struct __sk_buff *skb)
{
void *data_end = (void *)(long)skb->data_end;
void *data = (void *)(long)skb->data;
__u32 nh_off;
nh_off = sizeof(struct ethhdr);
if (data + nh_off > data_end)
return 0;
// Count packets
__u64 *counter = bpf_map_lookup_elem(&my_map, &key);
if (counter)
__sync_fetch_and_add(counter, 1);
return 0;
}
// Maps definition
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.max_entries = 256,
};
char _license[] SEC("license") = "GPL";
7. Kernel Security
Linux Security Module (LSM) Hook
// lsm_example.c - Simple LSM Hook
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/security.h>
#include <linux/binfmts.h>
// Security hook for file execution
static int my_security_bprm_check(struct linux_binprm *bprm)
{
const char *filename = bprm->filename;
pr_info("LSM: Executing %s\n", filename);
// Security check logic here
// Return 0 to allow, negative error to deny
return 0;
}
// Security operations list
static struct security_hook_list my_hooks[] = {
LSM_HOOK_INIT(bprm_check, my_security_bprm_check),
};
// Initialize LSM
static int __init my_lsm_init(void)
{
pr_info("LSM: Initializing\n");
security_add_hooks(my_hooks, ARRAY_SIZE(my_hooks), "my_lsm");
return 0;
}
// Cleanup LSM
static void __exit my_lsm_exit(void)
{
pr_info("LSM: Exiting\n");
}
module_init(my_lsm_init);
module_exit(my_lsm_exit);
MODULE_LICENSE("GPL");
8. Network Programming
Kernel Socket Programming
// kernel_socket.c - Kernel Network Programming
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/net.h>
#include <net/sock.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <asm/uaccess.h>
#include <linux/socket.h>
// Create socket in kernel
static int create_kernel_socket(void)
{
struct socket *sock;
struct sockaddr_in addr;
int ret;
// Create TCP socket
ret = sock_create_kern(&init_net, AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
if (ret < 0) {
pr_err("Failed to create socket: %d\n", ret);
return ret;
}
// Set address
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
// Bind socket
ret = kernel_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
pr_err("Failed to bind socket: %d\n", ret);
sock_release(sock);
return ret;
}
// Listen
ret = kernel_listen(sock, 10);
if (ret < 0) {
pr_err("Failed to listen: %d\n", ret);
sock_release(sock);
return ret;
}
pr_info("Socket created and bound\n");
return 0;
}
// Send data from kernel
static int send_kernel_data(struct socket *sock, const char *data, size_t len)
{
struct msghdr msg;
struct kvec iov;
int ret;
memset(&msg, 0, sizeof(msg));
iov.iov_base = (void *)data;
iov.iov_len = len;
ret = kernel_sendmsg(sock, &msg, &iov, 1, len);
if (ret < 0) {
pr_err("Failed to send data: %d\n", ret);
return ret;
}
return ret;
}
// Receive data in kernel
static int recv_kernel_data(struct socket *sock, char *data, size_t len)
{
struct msghdr msg;
struct kvec iov;
int ret;
memset(&msg, 0, sizeof(msg));
iov.iov_base = data;
iov.iov_len = len;
ret = kernel_recvmsg(sock, &msg, &iov, 1, len, 0);
if (ret < 0) {
pr_err("Failed to receive data: %d\n", ret);
return ret;
}
return ret;
}
// Netfilter hook example
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
static unsigned int nf_hook_func(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct iphdr *iph;
if (!skb)
return NF_ACCEPT;
iph = ip_hdr(skb);
pr_info("Packet: protocol=%d, saddr=%pI4, daddr=%pI4\n",
iph->protocol, &iph->saddr, &iph->daddr);
return NF_ACCEPT;
}
static struct nf_hook_ops nf_hook_ops = {
.hook = nf_hook_func,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
};
static int __init netfilter_init(void)
{
return nf_register_net_hook(&init_net, &nf_hook_ops);
}
static void __exit netfilter_exit(void)
{
nf_unregister_net_hook(&init_net, &nf_hook_ops);
}
9. Decision Trees
Synchronization Primitive Selection
Critical section characteristics?
│
├─ Very short (< microsecond) → Spinlock
├─ Can sleep, exclusive access → Mutex
├─ Many readers, few writers → RCU or Seqlock
├─ Readers and writers → RW Semaphore
├─ Simple signal → Completion
└─ Producer-consumer → Wait queue
Memory Allocation Strategy
Allocation size and context?
│
├─ < 128 bytes → kmalloc with appropriate GFP flags
├─ 128 bytes - 128 KB → kmalloc
├─ > 128 KB → vmalloc (or alloc_pages if contiguous)
├─ DMA required → dma_alloc_coherent
├─ Highmem → __get_free_pages with GFP_HIGHUSER
└─ Interrupt context → GFP_ATOMIC (no sleep)
10. Anti-Patterns to Avoid
- Sleeping in atomic context: Never use sleeping functions while holding spinlock
- Race conditions: Always use proper synchronization
- Memory leaks: Track and free all allocations
- Use after free: Be careful with RCU and freeing
- Integer overflow: Check arithmetic operations
- Buffer overflows: Validate all user input
- Deadlocks: Acquire locks in consistent order
- Priority inversion: Use proper priority inheritance
- Ignoring return values: Always check error codes
- Missing module_put: Match get/put operations
11. Quality Checklist
Before considering kernel code production-ready:
- All error paths properly handled
- Memory allocations checked for failure
- Synchronization primitives correctly used
- No sleeping in atomic context
- No use of deprecated APIs
- Module metadata complete
- Coding style follows kernel standards
- Sparse checking passes
- Tested with lockdep
- Memory leak testing performed
- Performance testing completed
- Security review conducted
- Documentation complete
- Backward compatibility considered
- API stability maintained
- Tested on multiple architectures
- Kernel version compatibility verified
- Static analysis performed
- Stress testing completed
- Integration testing done
This comprehensive skill definition provides complete guidance for kernel engineering across Linux environments.