Anti-Pattern: Locking with Un-Intentional Recursion
- Function f() calls a function g(), while holding a lock
- The chain of calls from function g() ends up calling back f(), via some obscure path, maybe via a function pointer
- Function f() tries to obtaint the same lock
- Can cause obscure deadlocks, due to control flow variations
- Antidotes:
- Try to avoid calling functions while holding locks
- Never hold a lock while calling a function you don't know well
- If unsure, consider explicit checks for recursion
static int i_am_active = 0;
if (i_am_active++) goto handle_error;
mutex_lock(&m);
...code that may call obscure functions...
mutex_unlock(&m);
i_am_active--
Anti-Pattern: Registration Before Ready
- Module registers a structure with embedded function pointers before completing initialization
of data on which the functions depend
- Another thread calls the function, before initialization completes
- Variant: function calls through dependent registrations
- Unpredictable failures
- Antidote:
- Only do registrations after all other initializations are done
- Do multiple registrations bottom-up in order of hierarchical dependence
Anti-Pattern: Unnecessary Memory Allocation
- Programmer allocates temporary dynamic memory, then frees it, unnecessarily
- Memory fragmentation, execution-time overhead
- Antidote:
- Use a different algorithm, that does not require dynamic memory
- Use static memory, if that works
- Design the interface to use memory allocated by the user
Anti-Pattern: Allocating Large Local Objects
int my_function (int my_arg) {
char * my_buffer[some_size];
...maybe call some more functions...
}
- The programmer allocates a large object as local to a kernel function
- The kernel stack overflows (The limit can be as small as 4K bytes!)
- Can cause unpredicatable failures, due to combinations of function calls
- Antidotes:
- Use global static storage (but be careful with locking)
- Use dynamically allocated storage (but be careful about memory leakage)
- Design to avoid needing kernel memory (e.g., rely on user to provide memory)
Anti-Pattern: Intentional Recursion
- The programmer uses a recursive algorithm in the kernel
- The kernel stack overflows (The limit can be as small as 4K bytes.)
- Can cause unpredicatable failures, due to variable depth of recursions
- Antidotes:
- Use iteration
- Impose a hard (small) limit on recursion depth
Anti-Pattern: Failure to Recover Memory
if ((p = kmalloc (sizeof (*p), GFP_KERNEL))) return -ENOMEM;
....
if (some_test(some_object)) return;
...
kfree (p);
- Causes memory leakage
- Antidotes:
- Use reference counts for objects with potentially long lifetimes
- Try to avoid dynamic allocation of temporary objects
Anti-Pattern: Failure to Check Return Status
p = kmalloc (sizeof (*p), GFP_KERNEL);
p->member = some_value;
- Rarely, kmalloc() fails
- When it does, the result will be data segmentation fault (lucky) or silent data corruption (unlucky)
- Another example: del_timer()
- Intermittent system failures, very hard to track down
- Always check status
- Consider using wrapper function or macro if same error recovery makes sense for multiple cases
See also the introductory page on patterns
and the page on locking patterns.