Arc I · Linux Driver Lab
Chapter 04

Kernel Timer Driver

Real camera pipelines generate frames on a schedule. This driver does the same — and hits a wall.

timer_list softirq context spinlock_irqsave del_timer_sync

Why this driver?

The previous drivers were reactive — they responded to userspace writes. A real camera generates frames autonomously on a hardware timer. I needed a driver that produces events independently, on a schedule, and wakes up readers when data is ready.

That sounds simple. But it introduced the constraint that changes everything about synchronization in this driver.

The constraint: softirq context

Kernel timer callbacks run in softirq context — an interrupt context where the CPU cannot sleep. That means:

PrimitiveCan sleep?Safe in softirq?
mutexYes❌ Illegal
spinlock_tNo✅ Required

Everything I learned about mutexes in drivers 01–03 was suddenly wrong. I had to switch to spinlocks with irqsave/irqrestore.

unsigned long flags;
spin_lock_irqsave(&kernel_logger.lock, flags);
/* critical section */
spin_unlock_irqrestore(&kernel_logger.lock, flags);

What cannot run under a spinlock

FunctionWhy not
copy_to_userMay page-fault (sleep)
wake_up_interruptibleTouches scheduler
kmalloc(GFP_KERNEL)May sleep
mod_timerHas internal locks

The correct copy_to_user pattern

uint8_t tmp[MEM_SIZE];
spin_lock_irqsave(&lock, flags);
memcpy(tmp, buffer[idx], copy_len);  /* safe inside lock */
count--;
spin_unlock_irqrestore(&lock, flags);
if (copy_to_user(user_buf, tmp, copy_len)) /* outside lock */
    return -EFAULT;

Bugs I hit and fixed

BugFix
copy_to_user under spinlock → kernel warningMoved to after unlock, using local tmp buffer
wake_up under spinlock → deadlock riskMoved outside critical section
Timer not stopping on ioctl STOPAdded timer_active flag checked in callback
Used del_timer on exitReplaced with del_timer_sync — waits for in-progress callback
del_timer vs del_timer_sync: del_timer returns immediately. If the callback is running on another CPU, your module can unload while the callback is still executing. del_timer_sync waits. Always use del_timer_sync on exit.