Technical Breakdown of the Root Cause
1. Involved Entities
- Source file:
mm/compaction.c (struct definition in include/linux/mmzone.h)
- Functions:
compaction_deferred(), try_to_compact_pages(), and kcompactd_do_work()
- Struct and field:
struct zone::compact_considered (at byte offset 0x430 / 1072 bytes within struct zone)
2. Compaction Deferral Mechanism
In the Linux page compaction subsystem, when compaction in a memory zone fails to yield an allocation of the requested order, future compaction attempts for that zone are deferred to avoid wasting CPU cycles.
- When compaction fails,
defer_compaction() resets zone->compact_considered = 0 and increments zone->compact_defer_shift up to COMPACT_MAX_DEFER_SHIFT (6).
- The limit on how many compaction attempts should be skipped is calculated as:
unsigned long defer_limit = 1UL << zone->compact_defer_shift;
- Before attempting compaction in a zone, callers invoke
compaction_deferred(zone, order). The function determines whether compaction should be deferred or attempted:
static bool compaction_deferred(struct zone *zone, int order)
{
unsigned long defer_limit = 1UL << zone->compact_defer_shift;
if (order < zone->compact_order_failed)
return false;
/* Avoid possible overflow */
if (++zone->compact_considered >= defer_limit) {
zone->compact_considered = defer_limit;
return false;
}
trace_mm_compaction_deferred(zone, order);
return true;
}
3. Root Cause and Trigger Condition
compaction_deferred() performs an unsynchronized read-modify-write (++zone->compact_considered) followed by a potential cap assignment (zone->compact_considered = defer_limit) on plain unsigned int field zone->compact_considered.
- No locks (such as
zone->lock or pgdat locks) are acquired around compaction_deferred().
- Multiple tasks can execute compaction paths simultaneously on the same zone:
- User/kernel tasks in the direct compaction path (
__alloc_pages_direct_compact() -> try_to_compact_pages()).
- The background compaction kernel thread (
kcompactd() -> kcompactd_do_work()).
- When task 7236 (in
try_to_compact_pages()) and task 34 (kcompactd0 in kcompactd_do_work()) evaluate compaction_deferred(zone, order) concurrently on CPU 1 and CPU 0 for the same zone, both execute ++zone->compact_considered concurrently at line 150 of mm/compaction.c.
- Because these are non-atomic, unprotected accesses, KCSAN detects and reports a data race on
zone->compact_considered.