Context and Atomicity
Read this chapter twice.
Everything in it answers one question — may this code sleep? — and getting that answer wrong is the most common serious mistake a new kernel developer makes. It is not a mistake in the sense of a typo. It is a mistake in the sense of a category: you write code that is correct in the place you were thinking about and catastrophic in the place it actually runs.
The four concepts here, in the six-part treatment: context, preempt_count, atomic
context, and the machinery that catches you.
Concept 1: Context
1. What problem it solves
To sleep means to call schedule() and give up the CPU until something wakes you. That requires
a task whose state can be saved and later restored — and, crucially, a task that it is legitimate
to suspend.
In a syscall handler, that task is yours: you asked, so you can wait. In an interrupt handler, there is no such task. The interrupt borrowed whatever happened to be running on that CPU. Suspending that task to wait for your I/O would block an innocent process, on a CPU that may be holding locks, with interrupts possibly masked, for an unbounded time. The scheduler would have nothing coherent to switch back to.
So the kernel does not attempt to make it work. It forbids it, and it tells you when you try.
2. Where it exists in the kernel
Context is not a variable you can read; it is a property of how you got here, tracked in a per-CPU counter and in which stack you are on.
rg -n "define in_task|define in_hardirq|define in_serving_softirq|define in_nmi" include/linux/preempt.h
rg -n "define might_sleep\b" include/linux/kernel.h include/linux/sched.h
rg -n "__might_sleep|__might_resched" kernel/sched/core.c
3. Who owns or interacts with it
| Actor | Interaction |
|---|---|
The entry code (arch/*/entry/) | Establishes the context by how it entered — trap, interrupt, or NMI |
preempt_count | The running record of "how atomic am I right now" |
| Every locking primitive | Changes the context by incrementing it (spinlocks) or not (mutexes) |
might_sleep() | Annotations sprinkled through every sleeping function, as tripwires |
| lockdep | Cross-checks that a lock is never taken in two incompatible contexts |
PREEMPT_RT | Changes the answers. Most spinlock_ts become sleepable there. |
4. The taxonomy
| Context | You get here by | May sleep | Mutex | Spinlock | GFP_KERNEL | copy_to_user |
|---|---|---|---|---|---|---|
| Process (task) | A syscall, a fault, a kernel thread, a workqueue item, a threaded IRQ | ✅ | ✅ | ✅ | ✅ | ✅ |
| …with preemption disabled | preempt_disable(), get_cpu_ptr(), rcu_read_lock() (non-preemptible RCU) | ❌ | ❌ | ✅ | ❌ | ❌ |
| …holding a spinlock | spin_lock() — still process context, now atomic | ❌ | ❌ | ✅ (another) | ❌ | ❌ |
| …with IRQs disabled | local_irq_save(), spin_lock_irqsave() | ❌ | ❌ | ✅ | ❌ | ❌ |
| Softirq / tasklet / timer | A bottom half running | ❌ | ❌ | ✅ | ❌ | ❌ |
| Hardware IRQ | The top half of an interrupt | ❌ | ❌ | ✅ (IRQ-safe) | ❌ | ❌ |
| NMI | Watchdog, perf, machine check | ❌ | ❌ | ❌ | ❌ | ❌ |
Read the third row again, because it is the one that catches people:
Warning: taking a spinlock puts you in atomic context even inside a syscall. The overwhelming majority of "sleeping in atomic context" bugs are not written in interrupt handlers. They are written in perfectly ordinary process-context code, between a
spin_lock()and aspin_unlock(), by someone who called a function three levels deep that allocates.
The decision, as a flowchart
Can this code sleep?
│
┌─────────────────┴──────────────────┐
│ │
Am I in an interrupt? Am I in a task?
(hardirq / softirq / NMI) │
│ │
NO ─────────────────────────▶ Is preempt_count > 0?
(spinlock held? preempt_disable?
rcu_read_lock? bh disabled?)
│
┌──────────┴──────────┐
YES NO
│ │
❌ ATOMIC Are IRQs disabled?
cannot sleep │
┌──────────┴──────────┐
YES NO
│ │
❌ ATOMIC ✅ YOU MAY SLEEP
Every ❌ leaf means the same set of prohibitions: no mutex_lock, no GFP_KERNEL, no
copy_*_user, no wait_event, no msleep, no synchronize_rcu, no calling a function you have
not read.
The functions that sleep and do not look like it
This is the list to internalize. Every one of these has bitten someone.
| Call | Why it can sleep |
|---|---|
kmalloc(n, GFP_KERNEL) and every allocator with GFP_KERNEL | May enter reclaim, which does I/O |
copy_to_user / copy_from_user / get_user / put_user | The user page may not be resident; faulting it in can do I/O |
mutex_lock, down, down_read, wait_event, wait_for_completion | That is their purpose |
msleep, schedule_timeout, usleep_range | Same. (udelay/ndelay busy-wait and are the atomic-safe alternative.) |
synchronize_rcu, synchronize_irq, flush_work, cancel_work_sync | They wait for something else to finish |
request_firmware | Talks to user space |
clk_prepare, regulator_enable, most I²C and SPI transfers | Bus transactions that wait on hardware |
printk — sometimes | Console drivers have historically been able to block; the multi-year printk rework exists because of this |
Anything ending in _interruptible, _killable, or _timeout | The name is telling you |
| Any function you have not read | Assume yes until you check |
Tip: The reliable way to check is not to guess but to look for the tripwire:
rg -n "might_sleep\(\)|might_fault\(\)" mm/ kernel/ | head rg -n -B 5 "might_sleep" mm/page_alloc.c | head -30A function that calls
might_sleep()is telling you, in machine-checkable form, that it can. That annotation is the kernel's answer to "how am I supposed to know?"
5. Experiment
CLAIM. kmalloc(GFP_KERNEL) under a spinlock is detected immediately by the debug kernel —
before it deadlocks, and regardless of whether memory is actually tight.
METHOD. In a scratch module, on a lab-fast kernel (which has CONFIG_DEBUG_ATOMIC_SLEEP):
static DEFINE_SPINLOCK(lab_lock);
static int __init lab_init(void)
{
void *p;
spin_lock(&lab_lock);
/* WRONG ON PURPOSE: GFP_KERNEL may sleep, and we are atomic. */
p = kmalloc(128, GFP_KERNEL);
spin_unlock(&lab_lock);
kfree(p);
return 0;
}
PREDICT FIRST, in writing:
- Does this hang, oops, print a BUG and continue, or appear to work?
- Does the answer change if memory is plentiful?
- Does the answer change with
CONFIG_DEBUG_ATOMIC_SLEEP=n? - What if you change
GFP_KERNELtoGFP_ATOMIC?
Then run it and read the output. It looks like this, and every field is useful:
BUG: sleeping function called from invalid context at mm/page_alloc.c:NNNN
in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 412, name: insmod
preempt_count: 1, expected: 0
CPU: 2 PID: 412 Comm: insmod Tainted: G O
Call Trace:
dump_stack_lvl+0x...
__might_resched.cold+0x...
__kmalloc_noprof+0x...
lab_init+0x2c/0x1000 [lab]
do_one_initcall+0x...
Preemption disabled at:
lab_init+0x14/0x1000 [lab]
| Field | What it tells you |
|---|---|
at mm/page_alloc.c:NNNN | Where the might_sleep() fired — the callee, not your bug |
in_atomic(): 1 | You are atomic |
irqs_disabled(): 0 | …because of preemption, not because IRQs are off — so it is a spinlock or preempt_disable, not irqsave |
preempt_count: 1, expected: 0 | Exactly one level of nesting to find |
Preemption disabled at: | This is your bug. The line that made you atomic. |
That last line is the whole reason to turn the option on. It names the spin_lock(), not the
kmalloc().
RESULT. Record what happened for all four predictions. Then answer: with
CONFIG_DEBUG_ATOMIC_SLEEP=n and plenty of free memory, does the bug reproduce at all?
6. Failure mode
| Mistake | Symptom |
|---|---|
GFP_KERNEL under a spinlock | BUG: sleeping function called from invalid context — or nothing at all, until the machine is under memory pressure and then a deadlock |
mutex_lock in an interrupt handler | Same BUG, or a hang; a mutex must be released by its owner and an interrupt has no owner |
copy_to_user under a spinlock | Works whenever the page happens to be resident; BUGs the rest of the time |
msleep in a timer callback | scheduling while atomic, then usually a panic |
| Calling an unread helper from atomic context | Whatever that helper does, three levels down |
| "It worked in testing" | You tested with free memory, one CPU, and no pressure — i.e. none of the conditions under which it fails |
Concept 2: preempt_count
1. What problem it solves
"Am I atomic?" has to be answerable in O(1) at any point, from any code, without knowing how you got there. A single per-task counter provides that, and packing several different reasons into different bit-fields of the same word means one comparison answers "am I atomic at all" while the individual fields answer "why".
2. Where it exists in the kernel
Per-task (and on some architectures per-CPU for speed), in thread_info.
rg -n -A 30 "PREEMPT_BITS|SOFTIRQ_BITS|HARDIRQ_BITS|NMI_BITS" include/linux/preempt.h | head -50
rg -n "define preemptible\(\)|define in_atomic\(\)" include/linux/preempt.h
3. Who owns or interacts with it
| Actor | Effect on it |
|---|---|
preempt_disable() / preempt_enable() | ± the preemption field |
spin_lock() / spin_unlock() | Calls preempt_disable() internally — this is why a spinlock makes you atomic |
local_bh_disable() / local_bh_enable() | ± the softirq field |
| Entering a softirq | + the softirq field |
| Entering a hardirq | + the hardirq field |
| Entering an NMI | + the NMI field |
rcu_read_lock() | + preemption, on non-preemptible RCU configurations |
| The scheduler | Refuses to preempt when the counter is nonzero |
4. The layout and the queries
preempt_count (one word, several fields)
┌──────┬──────────┬──────────┬──────────────────┐
│ NMI │ HARDIRQ │ SOFTIRQ │ PREEMPT │
└──────┴──────────┴──────────┴──────────────────┘
│ │ │ │
│ │ │ └─ preempt_disable(), spin_lock(),
│ │ │ rcu_read_lock() on non-preempt RCU
│ │ └─ in a softirq, or local_bh_disable()
│ └─ in a hardware interrupt handler
└─ in an NMI
in_atomic() → any field nonzero
preemptible() → preempt_count == 0 AND interrupts enabled
| Query | True when | Note |
|---|---|---|
in_task() | Not in hardirq, softirq, or NMI | Does not mean you may sleep — you may still hold a spinlock |
in_hardirq() | In a hardware interrupt handler | Replaced the older in_irq() |
in_serving_softirq() | Actually running a softirq | The narrow one |
in_softirq() | Serving a softirq or bottom halves are disabled | Broader than people expect; usually not what you want |
in_interrupt() | Hardirq, softirq, or NMI | Historic, still common |
in_nmi() | In an NMI | Almost nothing is safe here |
in_atomic() | Any field nonzero | Only meaningful with CONFIG_PREEMPT_COUNT |
preemptible() | Safe to be preempted | The closest thing to "may I sleep" |
Warning:
in_atomic()cannot see a held spinlock unlessCONFIG_PREEMPT_COUNTis enabled, because without itpreempt_disable()compiles to nothing. Several options selectCONFIG_PREEMPT_COUNT, includingCONFIG_DEBUG_ATOMIC_SLEEP— which is precisely why the debug option is what makes the check meaningful. Confirm on your build:grep -E 'CONFIG_(PREEMPT_COUNT|PREEMPTION|DEBUG_ATOMIC_SLEEP|PREEMPT_RT)=' ~/kernel/build/.config rg -n "select PREEMPT_COUNT" lib/Kconfig.debug kernel/Kconfig.preemptDo not write code that branches on
in_atomic(). It is a debugging aid, not an API. A function that behaves differently depending on its caller's context is a function whose callers cannot reason about it; pass the context in explicitly, or provide two functions.
5. Experiment
CLAIM. preempt_count is observable, and every primitive you use changes it in a way you can
predict.
METHOD. A module that prints the counter at several points:
static void report(const char *where)
{
pr_info("%-22s preempt_count=%08x in_task=%d in_atomic=%d irqs_off=%d\n",
where, preempt_count(), !!in_task(), !!in_atomic(),
irqs_disabled());
}
static DEFINE_SPINLOCK(l);
static int __init ctx_init(void)
{
unsigned long flags;
report("module init");
preempt_disable();
report("preempt_disable");
preempt_enable();
spin_lock(&l);
report("spin_lock");
spin_unlock(&l);
spin_lock_irqsave(&l, flags);
report("spin_lock_irqsave");
spin_unlock_irqrestore(&l, flags);
local_bh_disable();
report("local_bh_disable");
local_bh_enable();
rcu_read_lock();
report("rcu_read_lock");
rcu_read_unlock();
return 0;
}
PREDICT FIRST, filling in this table before you build it:
| At | preempt_count (hex) | in_task | in_atomic | irqs_off |
|---|---|---|---|---|
| module init | ? | ? | ? | ? |
preempt_disable | ? | ? | ? | ? |
spin_lock | ? | ? | ? | ? |
spin_lock_irqsave | ? | ? | ? | ? |
local_bh_disable | ? | ? | ? | ? |
rcu_read_lock | ? | ? | ? | ? |
The two rows most people get wrong are spin_lock (people expect it to leave the counter alone) and
rcu_read_lock (whose answer depends on your CONFIG_PREEMPT_RCU setting — check it, and explain
the result you get rather than the one you expected).
6. Failure mode
| Mistake | Symptom |
|---|---|
Unbalanced preempt_disable/enable | BUG: scheduling while atomic somewhere unrelated, later; the machine eventually wedges |
Unbalanced local_irq_save/restore | Interrupts stay off; the machine hangs or the watchdog fires |
Branching on in_atomic() | A function whose behavior depends on its caller — unreviewable, and wrong under PREEMPT_RT |
| Returning from a function with a lock still held | The counter never comes back down; the next schedule() BUGs |
Assuming in_task() means "may sleep" | It does not. You can be in a task and holding three spinlocks. |
Concept 3: What Creates Atomic Context
1. What problem it solves
You cannot avoid what you cannot enumerate. There are exactly five ways to become atomic, and knowing all five turns "am I atomic?" from a judgement call into a checklist.
2. Where it exists in the kernel
Everywhere — which is the point. Each of the five is a common, correct thing to do.
3. The five, and how to get out of each
| # | You became atomic by | Because | To sleep, you must |
|---|---|---|---|
| 1 | Being in an interrupt (hardirq/softirq/NMI/timer) | There is no task to suspend | Defer to a workqueue or a threaded IRQ (Deferred Work) |
| 2 | Holding a spinlock | spin_lock() disables preemption | Drop the lock, do the sleeping thing, retake and re-validate |
| 3 | preempt_disable() — including via get_cpu_ptr(), this_cpu_* sequences | You asked the scheduler to leave you alone | preempt_enable() first |
| 4 | IRQs disabled — local_irq_save, spin_lock_irqsave | You cannot be woken; the timer tick is off | Restore them first |
| 5 | Inside rcu_read_lock() (non-preemptible RCU) | The grace period is waiting on you | End the read-side section, or use SRCU, which can sleep |
The restructuring for case 2 is the pattern you will write most often:
/* WRONG: allocating under the lock. */
spin_lock(&s->lock);
node = kmalloc(sizeof(*node), GFP_KERNEL); /* ← BUG */
list_add(&node->list, &s->items);
spin_unlock(&s->lock);
/* RIGHT: allocate first, outside the lock, and hold it only for the
* data-structure manipulation it actually protects. */
node = kmalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return -ENOMEM;
spin_lock(&s->lock);
/* RE-VALIDATE. The world changed while the lock was not held: another
* CPU may have added this key, or torn the structure down. Whatever you
* checked before the allocation, check again here. */
if (s->dying) {
spin_unlock(&s->lock);
kfree(node);
return -ESHUTDOWN;
}
list_add(&node->list, &s->items);
spin_unlock(&s->lock);
Warning: The "re-validate" comment is not boilerplate; it is the entire difficulty. Dropping a lock to sleep is easy. Correctly re-establishing the invariants you were relying on, after arbitrary other work has happened, is the hard part — and the bug that survives review is always the missing re-validation, never the missing lock.
The escape hatch that is not one: switching GFP_KERNEL to GFP_ATOMIC silences the BUG. It is
occasionally right and usually a mistake, because GFP_ATOMIC cannot reclaim, is much more likely
to fail, and draws on emergency reserves that exist for paths with no alternative. If your first
instinct on seeing this BUG is to change the GFP flag, the second thought should be "or should this
allocation move outside the lock?" — which it usually should. See Memory.
4. PREEMPT_RT changes the answers
On a PREEMPT_RT kernel, most of the above is different, and code that assumes otherwise breaks:
| Normal kernel | PREEMPT_RT | |
|---|---|---|
spinlock_t | Spins; disables preemption; atomic | A sleeping lock. You may sleep while holding it. |
raw_spinlock_t | Same as spinlock_t | Still a true spinlock; still atomic |
| Most IRQ handlers | Run in hardirq context | Run in threads, so they may sleep |
| Softirqs | Run in softirq context | Run in threads |
local_irq_disable() in generic code | Disables interrupts | Often does not, in the way you expect |
This has two practical consequences you should adopt now, before you ever touch an RT kernel:
- Use
spinlock_tunless you genuinely needraw_spinlock_t.raw_means "this must be atomic even on RT", which is a strong claim about a very short critical section. - Never reason from "the scheduler probably will not preempt here." Code that is accidentally correct because of a preemption model is code that breaks on a different one.
grep -E 'CONFIG_PREEMPT_RT' ~/kernel/build/.config
ls Documentation/locking/ # locktypes.rst covers exactly this
$EDITOR Documentation/locking/locktypes.rst
5. Experiment
CLAIM. The same function can be correct or catastrophic depending only on who calls it — and the kernel will not warn you until the bad caller actually runs.
METHOD. Write one helper that allocates with GFP_KERNEL, and call it from two places:
static int lab_helper(void) /* looks innocent */
{
void *p = kmalloc(64, GFP_KERNEL);
if (!p)
return -ENOMEM;
kfree(p);
return 0;
}
/* Caller A: process context, no locks. */
static int __init lab_init(void)
{
lab_helper(); /* fine */
timer_setup(&lab_timer, lab_timer_fn, 0);
mod_timer(&lab_timer, jiffies + HZ);
return 0;
}
/* Caller B: a timer callback — SOFTIRQ context. */
static void lab_timer_fn(struct timer_list *t)
{
lab_helper(); /* BUG, one second later */
}
PREDICT FIRST: does insmod succeed? How long after insmod does the BUG appear? Does the
module still load successfully? Does rmmod work afterwards?
RESULT. The load succeeds and looks fine. One second later the machine prints a BUG from a completely different call stack. Write down the lesson in one sentence — it is the reason kernel review asks "what context is this called from?" about every new helper.
6. Failure mode
| Mistake | Symptom |
|---|---|
| A helper that sleeps, called from two contexts | Works from one caller, BUGs from the other, possibly much later |
Adding a mutex_lock to an existing function | Every caller in atomic context is now broken, and there may be forty |
GFP_ATOMIC used to silence a BUG | Allocation failures under pressure, and reserve exhaustion that hurts unrelated code |
| Assuming your code is not on an RT kernel | It is, on somebody's machine |
Relying on PREEMPT_NONE semantics | Breaks the day someone builds with full preemption |
Concept 4: The Machinery That Catches You
1. What problem it solves
None of the above is reliably detectable by reading code, because the offending call is usually several frames away and behind a function pointer. The kernel therefore instruments itself: sleeping functions announce that they sleep, and a debug build checks the announcement against the current context every single time.
2. Where it exists in the kernel
rg -n "might_sleep\(\)" include/linux/ | head
rg -n "might_sleep|might_fault|cant_sleep" mm/page_alloc.c mm/slub.c kernel/locking/mutex.c | head
rg -n -A 6 "config DEBUG_ATOMIC_SLEEP" lib/Kconfig.debug
3. The annotations, and what each one asserts
| Annotation | Asserts | Put it in |
|---|---|---|
might_sleep() | "This function may sleep; you had better be able to" | Any function of yours that can sleep, at the top |
might_sleep_if(cond) | Conditionally — e.g. only when a GFP_KERNEL flag was passed | Allocator-like wrappers |
might_fault() | "This may take a page fault, which may sleep" | Anything touching user memory |
cant_sleep() | The inverse: "it is a bug if this is called where sleeping is allowed to matter" | Rarely; atomic-only helpers |
lockdep_assert_held(&lock) | "The caller must hold this lock" | Any function with a documented locking precondition |
lockdep_assert_irqs_disabled() | "IRQs must be off here" | Low-level paths |
WARN_ON_ONCE(in_hardirq()) | A blunt instrument for a specific case | Last resort |
Put might_sleep() in your own sleeping functions. It costs nothing in a production build and it
converts "someone will discover this in three years" into "the debug kernel says so on the first
call". This is a real, valued thing to add to existing code, and it makes a good early patch.
4. The options to turn on
| Option | Catches | Cost |
|---|---|---|
CONFIG_DEBUG_ATOMIC_SLEEP | Sleeping in atomic context, with the Preemption disabled at: line | Small; selects PREEMPT_COUNT |
CONFIG_PROVE_LOCKING (lockdep) | Lock-order inversions, IRQ-unsafe/safe mixing, and sleeping-in-atomic — before the deadlock | Noticeable, worth it |
CONFIG_DEBUG_SPINLOCK / DEBUG_MUTEXES | Unbalanced, uninitialized, or wrongly-freed locks | Small |
CONFIG_DEBUG_PREEMPT | Unbalanced preempt counts and per-CPU misuse | Small |
CONFIG_KASAN | The use-after-free your broken teardown caused | 2–3× slower |
All of these are in lab-fast.config or lab-paranoid.config.
Verify rather than assume:
grep -E 'CONFIG_(DEBUG_ATOMIC_SLEEP|PROVE_LOCKING|DEBUG_PREEMPT|DEBUG_SPINLOCK)=' ~/kernel/build/.config
5. Experiment
CLAIM. Without CONFIG_DEBUG_ATOMIC_SLEEP, a sleeping-in-atomic bug is invisible under normal
conditions — it is not "usually detected", it is not detected at all.
METHOD. Build the kmalloc-under-spinlock module from Concept 1 twice: once against lab-fast,
once against a kernel with CONFIG_DEBUG_ATOMIC_SLEEP=n. Load each twenty times.
PREDICT FIRST: how many of the twenty loads on the non-debug kernel produce any visible symptom?
Then make memory tight and repeat on the non-debug kernel:
# In the guest, with a small -m: apply memory pressure while loading.
stress-ng --vm 2 --vm-bytes 80% -t 30s & # or a simple malloc loop
for i in $(seq 20); do insmod ./lab.ko; rmmod lab; done
RESULT. The bug that produced nothing twenty times in a row now produces a hang or a splat. Write down the sentence this teaches about testing kernel code on an idle machine.
6. Failure mode
| Mistake | Symptom |
|---|---|
| Developing without the debug options | Bugs found by users, in production, months later |
Ignoring a BUG: that "does not seem to break anything" | It broke something; you have not found it yet |
Not adding might_sleep() to your own sleeping helper | Your caller's bug is undetectable until it deadlocks |
| Turning off lockdep because it is slow | The class of bug it catches is the class you cannot find by hand |
| Reading only the top line of the splat | The Preemption disabled at: line is the bug; the top line is the victim |
The One-Page Summary
Pin this somewhere.
QUESTION 1: Am I in an interrupt?
hardirq / softirq / tasklet / timer callback / NMI → ATOMIC
QUESTION 2: Is preempt_count nonzero?
spin_lock held → ATOMIC
preempt_disable() → ATOMIC
local_bh_disable() → ATOMIC
rcu_read_lock() → ATOMIC (non-preemptible RCU)
QUESTION 3: Are IRQs disabled?
local_irq_save / spin_lock_irqsave → ATOMIC
IF ATOMIC, THESE ARE ALL FORBIDDEN:
mutex_lock / down / wait_event / wait_for_completion
kmalloc(GFP_KERNEL) and every GFP_KERNEL allocation
copy_to_user / copy_from_user / get_user / put_user
msleep / schedule_timeout / synchronize_rcu / flush_work
anything you have not read
IF YOU MUST SLEEP AND YOU ARE ATOMIC:
→ defer the work (workqueue / threaded IRQ)
→ or drop the lock, sleep, retake it, AND RE-VALIDATE
AND ALWAYS:
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_PROVE_LOCKING=y
-smp 4 minimum, -smp 8 for concurrency work
Validation / Self-check
- Define "sleep" in kernel terms, and explain why it is impossible in interrupt context using the word "task".
- List the five ways to become atomic. For each, name the call that gets you out.
- Why does taking a spinlock make you atomic? What does
spin_lock()call internally? - Name six functions that can sleep but do not look like it.
- Read this splat aloud and say which line is the bug: the
at mm/…line, thein_atomic()line, or thePreemption disabled at:line. Why? in_task()returns true. May you callmutex_lock()? Justify the answer.- What is the difference between
in_softirq()andin_serving_softirq(), and which do you almost never want? - Why is
in_atomic()unreliable withoutCONFIG_PREEMPT_COUNT, and which common debug option selects it? - Why should code never branch on
in_atomic()? What should it do instead? - You hit "sleeping function called from invalid context" on a
kmalloc. Give two fixes, and say which is usually right and why. - When you drop a lock to sleep and retake it, what is the hard part? Give a concrete example of an invariant that could have changed.
- Under
PREEMPT_RT, may you sleep while holding aspinlock_t? Araw_spinlock_t? What does that imply about which one you should reach for by default? - Your module loads cleanly and BUGs one second later from an unrelated stack. What is the most likely structure of the bug?
- Why is a sleeping-in-atomic bug invisible on an idle development machine with the debug options off?
Next: Concurrency — the other question you must always be able to answer: who else is touching this data right now?