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

ActorInteraction
The entry code (arch/*/entry/)Establishes the context by how it entered — trap, interrupt, or NMI
preempt_countThe running record of "how atomic am I right now"
Every locking primitiveChanges the context by incrementing it (spinlocks) or not (mutexes)
might_sleep()Annotations sprinkled through every sleeping function, as tripwires
lockdepCross-checks that a lock is never taken in two incompatible contexts
PREEMPT_RTChanges the answers. Most spinlock_ts become sleepable there.

4. The taxonomy

ContextYou get here byMay sleepMutexSpinlockGFP_KERNELcopy_to_user
Process (task)A syscall, a fault, a kernel thread, a workqueue item, a threaded IRQ✅✅✅✅✅
…with preemption disabledpreempt_disable(), get_cpu_ptr(), rcu_read_lock() (non-preemptible RCU)❌❌✅❌❌
…holding a spinlockspin_lock() — still process context, now atomic❌❌✅ (another)❌❌
…with IRQs disabledlocal_irq_save(), spin_lock_irqsave()❌❌✅❌❌
Softirq / tasklet / timerA bottom half running❌❌✅❌❌
Hardware IRQThe top half of an interrupt❌❌✅ (IRQ-safe)❌❌
NMIWatchdog, 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 a spin_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.

CallWhy it can sleep
kmalloc(n, GFP_KERNEL) and every allocator with GFP_KERNELMay enter reclaim, which does I/O
copy_to_user / copy_from_user / get_user / put_userThe user page may not be resident; faulting it in can do I/O
mutex_lock, down, down_read, wait_event, wait_for_completionThat is their purpose
msleep, schedule_timeout, usleep_rangeSame. (udelay/ndelay busy-wait and are the atomic-safe alternative.)
synchronize_rcu, synchronize_irq, flush_work, cancel_work_syncThey wait for something else to finish
request_firmwareTalks to user space
clk_prepare, regulator_enable, most I²C and SPI transfersBus transactions that wait on hardware
printk — sometimesConsole drivers have historically been able to block; the multi-year printk rework exists because of this
Anything ending in _interruptible, _killable, or _timeoutThe name is telling you
Any function you have not readAssume 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 -30

A 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:

  1. Does this hang, oops, print a BUG and continue, or appear to work?
  2. Does the answer change if memory is plentiful?
  3. Does the answer change with CONFIG_DEBUG_ATOMIC_SLEEP=n?
  4. What if you change GFP_KERNEL to GFP_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]
FieldWhat it tells you
at mm/page_alloc.c:NNNNWhere the might_sleep() fired — the callee, not your bug
in_atomic(): 1You 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: 0Exactly 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

MistakeSymptom
GFP_KERNEL under a spinlockBUG: 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 handlerSame BUG, or a hang; a mutex must be released by its owner and an interrupt has no owner
copy_to_user under a spinlockWorks whenever the page happens to be resident; BUGs the rest of the time
msleep in a timer callbackscheduling while atomic, then usually a panic
Calling an unread helper from atomic contextWhatever 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

ActorEffect 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 schedulerRefuses 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
QueryTrue whenNote
in_task()Not in hardirq, softirq, or NMIDoes not mean you may sleep — you may still hold a spinlock
in_hardirq()In a hardware interrupt handlerReplaced the older in_irq()
in_serving_softirq()Actually running a softirqThe narrow one
in_softirq()Serving a softirq or bottom halves are disabledBroader than people expect; usually not what you want
in_interrupt()Hardirq, softirq, or NMIHistoric, still common
in_nmi()In an NMIAlmost nothing is safe here
in_atomic()Any field nonzeroOnly meaningful with CONFIG_PREEMPT_COUNT
preemptible()Safe to be preemptedThe closest thing to "may I sleep"

Warning: in_atomic() cannot see a held spinlock unless CONFIG_PREEMPT_COUNT is enabled, because without it preempt_disable() compiles to nothing. Several options select CONFIG_PREEMPT_COUNT, including CONFIG_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.preempt

Do 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:

Atpreempt_count (hex)in_taskin_atomicirqs_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

MistakeSymptom
Unbalanced preempt_disable/enableBUG: scheduling while atomic somewhere unrelated, later; the machine eventually wedges
Unbalanced local_irq_save/restoreInterrupts 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 heldThe 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 byBecauseTo sleep, you must
1Being in an interrupt (hardirq/softirq/NMI/timer)There is no task to suspendDefer to a workqueue or a threaded IRQ (Deferred Work)
2Holding a spinlockspin_lock() disables preemptionDrop the lock, do the sleeping thing, retake and re-validate
3preempt_disable() — including via get_cpu_ptr(), this_cpu_* sequencesYou asked the scheduler to leave you alonepreempt_enable() first
4IRQs disabled — local_irq_save, spin_lock_irqsaveYou cannot be woken; the timer tick is offRestore them first
5Inside rcu_read_lock() (non-preemptible RCU)The grace period is waiting on youEnd 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 kernelPREEMPT_RT
spinlock_tSpins; disables preemption; atomicA sleeping lock. You may sleep while holding it.
raw_spinlock_tSame as spinlock_tStill a true spinlock; still atomic
Most IRQ handlersRun in hardirq contextRun in threads, so they may sleep
SoftirqsRun in softirq contextRun in threads
local_irq_disable() in generic codeDisables interruptsOften does not, in the way you expect

This has two practical consequences you should adopt now, before you ever touch an RT kernel:

  1. Use spinlock_t unless you genuinely need raw_spinlock_t. raw_ means "this must be atomic even on RT", which is a strong claim about a very short critical section.
  2. 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

MistakeSymptom
A helper that sleeps, called from two contextsWorks from one caller, BUGs from the other, possibly much later
Adding a mutex_lock to an existing functionEvery caller in atomic context is now broken, and there may be forty
GFP_ATOMIC used to silence a BUGAllocation failures under pressure, and reserve exhaustion that hurts unrelated code
Assuming your code is not on an RT kernelIt is, on somebody's machine
Relying on PREEMPT_NONE semanticsBreaks 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

AnnotationAssertsPut 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 passedAllocator-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 caseLast 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

OptionCatchesCost
CONFIG_DEBUG_ATOMIC_SLEEPSleeping in atomic context, with the Preemption disabled at: lineSmall; selects PREEMPT_COUNT
CONFIG_PROVE_LOCKING (lockdep)Lock-order inversions, IRQ-unsafe/safe mixing, and sleeping-in-atomic — before the deadlockNoticeable, worth it
CONFIG_DEBUG_SPINLOCK / DEBUG_MUTEXESUnbalanced, uninitialized, or wrongly-freed locksSmall
CONFIG_DEBUG_PREEMPTUnbalanced preempt counts and per-CPU misuseSmall
CONFIG_KASANThe use-after-free your broken teardown caused2–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

MistakeSymptom
Developing without the debug optionsBugs 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 helperYour caller's bug is undetectable until it deadlocks
Turning off lockdep because it is slowThe class of bug it catches is the class you cannot find by hand
Reading only the top line of the splatThe 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

  1. Define "sleep" in kernel terms, and explain why it is impossible in interrupt context using the word "task".
  2. List the five ways to become atomic. For each, name the call that gets you out.
  3. Why does taking a spinlock make you atomic? What does spin_lock() call internally?
  4. Name six functions that can sleep but do not look like it.
  5. Read this splat aloud and say which line is the bug: the at mm/… line, the in_atomic() line, or the Preemption disabled at: line. Why?
  6. in_task() returns true. May you call mutex_lock()? Justify the answer.
  7. What is the difference between in_softirq() and in_serving_softirq(), and which do you almost never want?
  8. Why is in_atomic() unreliable without CONFIG_PREEMPT_COUNT, and which common debug option selects it?
  9. Why should code never branch on in_atomic()? What should it do instead?
  10. You hit "sleeping function called from invalid context" on a kmalloc. Give two fixes, and say which is usually right and why.
  11. 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.
  12. Under PREEMPT_RT, may you sleep while holding a spinlock_t? A raw_spinlock_t? What does that imply about which one you should reach for by default?
  13. Your module loads cleanly and BUGs one second later from an unrelated stack. What is the most likely structure of the bug?
  14. 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?