{
  "schemaVersion": 1,
  "slug": "raid-write-hole-state-model",
  "title": "RAID write-hole states: an exhaustive one-bit XOR model",
  "summary": "32 synthetic states distinguish inconsistent parity, recovery of an untouched missing chunk and preservation of an interrupted update. Not hardware measurements or failure probabilities.",
  "published": "2026-10-11",
  "updated": "2026-10-11",
  "generatedAt": "2026-10-11T03:19:58.346Z",
  "evidenceType": "deterministic-algebraic-model",
  "license": "CC0-1.0 for Hesela-generated CSV and numerical rows; model code MIT; cited sources retain their own licenses.",
  "licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
  "sourceUrl": "https://hesela.com/analyses/raid-write-hole-bitmap-ppl/",
  "downloadPath": "/data/datasets/raid-write-hole-state-model.csv",
  "codePath": "/research/raid-write-hole-model.mjs",
  "rowCount": 32,
  "columns": [
    {
      "name": "a_before",
      "type": "integer",
      "description": "Initial bit of updated chunk A. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "b_unchanged",
      "type": "integer",
      "description": "Initial bit of untouched chunk B; B is the missing member during reconstruction. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "c_unchanged",
      "type": "integer",
      "description": "Bit of surviving untouched chunk C. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "a_target",
      "type": "integer",
      "description": "Intended new bit in A, always 1 minus a_before in this enumeration. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "data_persisted",
      "type": "integer",
      "description": "1 if the A update reached nonvolatile storage before the modeled crash. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "parity_persisted",
      "type": "integer",
      "description": "1 if the new parity reached nonvolatile storage before the modeled crash. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "a_after",
      "type": "integer",
      "description": "A bit available after the modeled crash. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "parity_after",
      "type": "integer",
      "description": "Possibly stale parity bit available after the modeled crash. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "parity_consistent",
      "type": "integer",
      "description": "1 if parity_after equals XOR of the three post-crash data bits. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "naive_recovered_b",
      "type": "integer",
      "description": "B reconstructed using a_after, c_unchanged and possibly stale parity. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "naive_b_correct",
      "type": "integer",
      "description": "1 if naive_recovered_b equals b_unchanged. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "ppl_repaired_parity",
      "type": "integer",
      "description": "XOR of a_after and durably recorded XOR of untouched B and C. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "ppl_recovered_b",
      "type": "integer",
      "description": "B reconstructed using the repaired parity. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "ppl_b_correct",
      "type": "integer",
      "description": "1 if ppl_recovered_b equals b_unchanged. Unit: dimensionless bit or 0/1 indicator."
    },
    {
      "name": "a_target_present",
      "type": "integer",
      "description": "1 if A contains the intended new bit; independent of B recovery. Unit: dimensionless bit or 0/1 indicator."
    }
  ],
  "method": "Enumerate eight initial A/B/C bit triples and four independent persistence masks for a single changed A bit and parity. A durable partial-parity record Q=B XOR C is assumed to precede both member updates. Repaired parity is A_after XOR Q. B is absent at reconstruction; A, C and Q survive.",
  "limits": [
    "Synthetic state enumeration, not a sample of devices; state counts are not event probabilities and no confidence interval is applicable.",
    "Atomic one-bit member writes; no torn sectors, firmware, journal corruption, concurrency, RAID6 or multiple failures.",
    "The durable PPL record, its integrity and ordering are assumptions. This is not an implementation or verification of Linux MD recovery.",
    "Missing B was not being written. Missing A is outside the modeled guarantee; PPL does not generally preserve in-flight writes.",
    "No upper-layer I/O acknowledgement is modeled. a_target_present is not an application durability guarantee."
  ],
  "citations": [
    {
      "id": "ppl",
      "title": "Linux v6.12: Partial Parity Log",
      "url": "https://github.com/torvalds/linux/blob/v6.12/Documentation/driver-api/md/raid5-ppl.rst",
      "accessed": "2026-10-11"
    },
    {
      "id": "journal",
      "title": "Linux v6.12: RAID 4/5/6 cache",
      "url": "https://github.com/torvalds/linux/blob/v6.12/Documentation/driver-api/md/raid5-cache.rst",
      "accessed": "2026-10-11"
    },
    {
      "id": "md",
      "title": "Linux v6.12: RAID arrays, dirty/degraded assembly and bitmap metadata",
      "url": "https://github.com/torvalds/linux/blob/v6.12/Documentation/admin-guide/md.rst",
      "accessed": "2026-10-11"
    },
    {
      "id": "flush",
      "title": "Linux v6.12: Explicit volatile write back cache control",
      "url": "https://github.com/torvalds/linux/blob/v6.12/Documentation/block/writeback_cache_control.rst",
      "accessed": "2026-10-11"
    }
  ],
  "artifacts": {
    "modelSha256": "2ce3ffa9b0ca5457c79742158b7a718f5bb50bf1a432e3d519ef95fa98fb27d4",
    "csvSha256": "dd7e302c700db3c9ddca527c2128260f3075385707d678c6ed7279e2ae242089"
  },
  "results": {
    "totalStates": 32,
    "naiveIncorrectB": 16,
    "pplCorrectB": 32,
    "targetAMissing": 16
  },
  "rows": [
    {
      "a_before": 0,
      "b_unchanged": 0,
      "c_unchanged": 0,
      "a_target": 1,
      "data_persisted": 0,
      "parity_persisted": 0,
      "a_after": 0,
      "parity_after": 0,
      "parity_consistent": 1,
      "naive_recovered_b": 0,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 0,
      "b_unchanged": 0,
      "c_unchanged": 0,
      "a_target": 1,
      "data_persisted": 0,
      "parity_persisted": 1,
      "a_after": 0,
      "parity_after": 1,
      "parity_consistent": 0,
      "naive_recovered_b": 1,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 0,
      "b_unchanged": 0,
      "c_unchanged": 0,
      "a_target": 1,
      "data_persisted": 1,
      "parity_persisted": 0,
      "a_after": 1,
      "parity_after": 0,
      "parity_consistent": 0,
      "naive_recovered_b": 1,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 0,
      "b_unchanged": 0,
      "c_unchanged": 0,
      "a_target": 1,
      "data_persisted": 1,
      "parity_persisted": 1,
      "a_after": 1,
      "parity_after": 1,
      "parity_consistent": 1,
      "naive_recovered_b": 0,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 0,
      "b_unchanged": 0,
      "c_unchanged": 1,
      "a_target": 1,
      "data_persisted": 0,
      "parity_persisted": 0,
      "a_after": 0,
      "parity_after": 1,
      "parity_consistent": 1,
      "naive_recovered_b": 0,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 0,
      "b_unchanged": 0,
      "c_unchanged": 1,
      "a_target": 1,
      "data_persisted": 0,
      "parity_persisted": 1,
      "a_after": 0,
      "parity_after": 0,
      "parity_consistent": 0,
      "naive_recovered_b": 1,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 0,
      "b_unchanged": 0,
      "c_unchanged": 1,
      "a_target": 1,
      "data_persisted": 1,
      "parity_persisted": 0,
      "a_after": 1,
      "parity_after": 1,
      "parity_consistent": 0,
      "naive_recovered_b": 1,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 0,
      "b_unchanged": 0,
      "c_unchanged": 1,
      "a_target": 1,
      "data_persisted": 1,
      "parity_persisted": 1,
      "a_after": 1,
      "parity_after": 0,
      "parity_consistent": 1,
      "naive_recovered_b": 0,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 0,
      "b_unchanged": 1,
      "c_unchanged": 0,
      "a_target": 1,
      "data_persisted": 0,
      "parity_persisted": 0,
      "a_after": 0,
      "parity_after": 1,
      "parity_consistent": 1,
      "naive_recovered_b": 1,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 0,
      "b_unchanged": 1,
      "c_unchanged": 0,
      "a_target": 1,
      "data_persisted": 0,
      "parity_persisted": 1,
      "a_after": 0,
      "parity_after": 0,
      "parity_consistent": 0,
      "naive_recovered_b": 0,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 0,
      "b_unchanged": 1,
      "c_unchanged": 0,
      "a_target": 1,
      "data_persisted": 1,
      "parity_persisted": 0,
      "a_after": 1,
      "parity_after": 1,
      "parity_consistent": 0,
      "naive_recovered_b": 0,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 0,
      "b_unchanged": 1,
      "c_unchanged": 0,
      "a_target": 1,
      "data_persisted": 1,
      "parity_persisted": 1,
      "a_after": 1,
      "parity_after": 0,
      "parity_consistent": 1,
      "naive_recovered_b": 1,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 0,
      "b_unchanged": 1,
      "c_unchanged": 1,
      "a_target": 1,
      "data_persisted": 0,
      "parity_persisted": 0,
      "a_after": 0,
      "parity_after": 0,
      "parity_consistent": 1,
      "naive_recovered_b": 1,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 0,
      "b_unchanged": 1,
      "c_unchanged": 1,
      "a_target": 1,
      "data_persisted": 0,
      "parity_persisted": 1,
      "a_after": 0,
      "parity_after": 1,
      "parity_consistent": 0,
      "naive_recovered_b": 0,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 0,
      "b_unchanged": 1,
      "c_unchanged": 1,
      "a_target": 1,
      "data_persisted": 1,
      "parity_persisted": 0,
      "a_after": 1,
      "parity_after": 0,
      "parity_consistent": 0,
      "naive_recovered_b": 0,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 0,
      "b_unchanged": 1,
      "c_unchanged": 1,
      "a_target": 1,
      "data_persisted": 1,
      "parity_persisted": 1,
      "a_after": 1,
      "parity_after": 1,
      "parity_consistent": 1,
      "naive_recovered_b": 1,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 1,
      "b_unchanged": 0,
      "c_unchanged": 0,
      "a_target": 0,
      "data_persisted": 0,
      "parity_persisted": 0,
      "a_after": 1,
      "parity_after": 1,
      "parity_consistent": 1,
      "naive_recovered_b": 0,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 1,
      "b_unchanged": 0,
      "c_unchanged": 0,
      "a_target": 0,
      "data_persisted": 0,
      "parity_persisted": 1,
      "a_after": 1,
      "parity_after": 0,
      "parity_consistent": 0,
      "naive_recovered_b": 1,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 1,
      "b_unchanged": 0,
      "c_unchanged": 0,
      "a_target": 0,
      "data_persisted": 1,
      "parity_persisted": 0,
      "a_after": 0,
      "parity_after": 1,
      "parity_consistent": 0,
      "naive_recovered_b": 1,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 1,
      "b_unchanged": 0,
      "c_unchanged": 0,
      "a_target": 0,
      "data_persisted": 1,
      "parity_persisted": 1,
      "a_after": 0,
      "parity_after": 0,
      "parity_consistent": 1,
      "naive_recovered_b": 0,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 1,
      "b_unchanged": 0,
      "c_unchanged": 1,
      "a_target": 0,
      "data_persisted": 0,
      "parity_persisted": 0,
      "a_after": 1,
      "parity_after": 0,
      "parity_consistent": 1,
      "naive_recovered_b": 0,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 1,
      "b_unchanged": 0,
      "c_unchanged": 1,
      "a_target": 0,
      "data_persisted": 0,
      "parity_persisted": 1,
      "a_after": 1,
      "parity_after": 1,
      "parity_consistent": 0,
      "naive_recovered_b": 1,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 1,
      "b_unchanged": 0,
      "c_unchanged": 1,
      "a_target": 0,
      "data_persisted": 1,
      "parity_persisted": 0,
      "a_after": 0,
      "parity_after": 0,
      "parity_consistent": 0,
      "naive_recovered_b": 1,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 1,
      "b_unchanged": 0,
      "c_unchanged": 1,
      "a_target": 0,
      "data_persisted": 1,
      "parity_persisted": 1,
      "a_after": 0,
      "parity_after": 1,
      "parity_consistent": 1,
      "naive_recovered_b": 0,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 0,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 1,
      "b_unchanged": 1,
      "c_unchanged": 0,
      "a_target": 0,
      "data_persisted": 0,
      "parity_persisted": 0,
      "a_after": 1,
      "parity_after": 0,
      "parity_consistent": 1,
      "naive_recovered_b": 1,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 1,
      "b_unchanged": 1,
      "c_unchanged": 0,
      "a_target": 0,
      "data_persisted": 0,
      "parity_persisted": 1,
      "a_after": 1,
      "parity_after": 1,
      "parity_consistent": 0,
      "naive_recovered_b": 0,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 1,
      "b_unchanged": 1,
      "c_unchanged": 0,
      "a_target": 0,
      "data_persisted": 1,
      "parity_persisted": 0,
      "a_after": 0,
      "parity_after": 0,
      "parity_consistent": 0,
      "naive_recovered_b": 0,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 1,
      "b_unchanged": 1,
      "c_unchanged": 0,
      "a_target": 0,
      "data_persisted": 1,
      "parity_persisted": 1,
      "a_after": 0,
      "parity_after": 1,
      "parity_consistent": 1,
      "naive_recovered_b": 1,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 1,
      "b_unchanged": 1,
      "c_unchanged": 1,
      "a_target": 0,
      "data_persisted": 0,
      "parity_persisted": 0,
      "a_after": 1,
      "parity_after": 1,
      "parity_consistent": 1,
      "naive_recovered_b": 1,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 1,
      "b_unchanged": 1,
      "c_unchanged": 1,
      "a_target": 0,
      "data_persisted": 0,
      "parity_persisted": 1,
      "a_after": 1,
      "parity_after": 0,
      "parity_consistent": 0,
      "naive_recovered_b": 0,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 1,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 0
    },
    {
      "a_before": 1,
      "b_unchanged": 1,
      "c_unchanged": 1,
      "a_target": 0,
      "data_persisted": 1,
      "parity_persisted": 0,
      "a_after": 0,
      "parity_after": 1,
      "parity_consistent": 0,
      "naive_recovered_b": 0,
      "naive_b_correct": 0,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 1
    },
    {
      "a_before": 1,
      "b_unchanged": 1,
      "c_unchanged": 1,
      "a_target": 0,
      "data_persisted": 1,
      "parity_persisted": 1,
      "a_after": 0,
      "parity_after": 0,
      "parity_consistent": 1,
      "naive_recovered_b": 1,
      "naive_b_correct": 1,
      "ppl_repaired_parity": 0,
      "ppl_recovered_b": 1,
      "ppl_b_correct": 1,
      "a_target_present": 1
    }
  ]
}
