An eviction can remove the line from the cache array before H has taken the dirty data. A later forwarded request must therefore search the writeback (WB) entries, not only the array. Review Writeback Buffers.
Both traces start from reset followed by C0 acquiring M and storing 1. When C0 evicts X, it moves 1 into a WB entry, sends PutM W and enters MI^A; H still records exclusive(C0), memory = 0. Select MSI. W labels that writeback; R labels C1's GetS.
Eviction state (Primer names) | Meaning | Loads and stores to X |
|---|---|---|
MI^A | Was M, wants I, waits for Put-Ack. WB entry keeps the dirty data. | Stall |
SI^A, EI^A | Was S or E, wants I, waits for Put-Ack. Clean. An SI^A reached from MI^A by a Fwd-GetS keeps the WB entry until Put-Ack. | Stall |
II^A | Lost the line to a Fwd-GetM or Inv while waiting for Put-Ack. | Stall |
A Fwd that hits a line in MI^A is answered from the WB entry. Fwd-GetS: send Data to H, go to SI^A. Fwd-GetM: send Data to H, go to II^A. In every case the WB entry is freed only on Put-Ack.
Writeback W wins H's order. H writes memory, then sends Put-Ack, and C0 frees its WB entry. R waits until the memory write, then receives S / 1. The final reader is C1 only; H must not invent a retained S copy at evicted C0.
Step | C0 state / value | C1 state / value | H directory | Memory | Newest data |
|---|---|---|---|---|---|
Initial | M / 1 | I / - | exclusive(C0) | 0 | C0: 1 |
C0 evicts | MI^A / 1 (in WB) | I / - | exclusive(C0) | 0 | C0 WB: 1, PutM(1) in flight to H |
1. PutM W at H | MI^A / 1 (in WB) | I / - | exclusive(C0), pend PutM C0 | 0 | C0 WB and H buffer: 1 |
2. GetS R at H | MI^A / 1 (in WB) | IS^D / - | exclusive(C0), pend PutM C0, queued GetS C1 | 0 | C0 WB and H buffer: 1 |
H writes memory | MI^A / 1 (in WB) | IS^D / - | U, pend GetS C1 | 1 | C0 WB, H buffer and memory: 1, Data(1) in flight to C1 |
3. Put-Ack at C0 | I / - | IS^D / - | U, pend GetS C1 | 1 | H buffer and memory: 1, Data(1) in flight to C1 |
4. Data at C1 | I / - | S / 1 | U, pend GetS C1 | 1 | C1 and memory: 1 |
(assumed) grant delivered | I / - | S / 1 | S{C1} | 1 | C1 and memory: 1 |
H selects R and forwards to C0 while W's PutM is still in flight. C0 is in MI^A, so it answers from its WB entry and goes to SI^A. Its Data says PutM W outstanding: C0 keeps no readable copy, so H does not record it as a sharer. H writes memory and completes R, independent of W. When W arrives, C0 is no longer a recorded holder, so this is a PutM from a non-holder: H sends Put-Ack without changing the directory or memory. This course marks the Data so that H never records C0. The Primer instead records C0 as a sharer and removes it when the late PutM from a non-owner arrives, then sends Put-Ack. Either way the late PutM writes nothing. Some designs also tag each directory entry with a generation, a per-entry counter that tells an old message from a new transaction on the same line, but the rule does not need one.
Step | C0 state / value | C1 state / value | H directory | Memory | Newest data |
|---|---|---|---|---|---|
Initial | M / 1 | I / - | exclusive(C0) | 0 | C0: 1 |
C0 evicts | MI^A / 1 (in WB) | I / - | exclusive(C0) | 0 | C0 WB: 1, PutM(1) in flight to H |
1. GetS R at H | MI^A / 1 (in WB) | IS^D / - | exclusive(C0), pend GetS C1 | 0 | C0 WB: 1, PutM(1) in flight to H |
2. Fwd-GetS at C0 (handled from WB) | SI^A / 1 (in WB) | IS^D / - | exclusive(C0), pend GetS C1 | 0 | C0 WB: 1, Data(1) and PutM(1) in flight to H |
3. Data at H | SI^A / 1 (in WB) | IS^D / - | exclusive(C0), pend GetS C1 | 0 | C0 WB and H buffer: 1, PutM(1) in flight to H |
H writes memory | SI^A / 1 (in WB) | IS^D / - | U, pend GetS C1 | 1 | C0 WB, H buffer and memory: 1, Data(1) in flight to C1, PutM(1) in flight to H |
4. Data at C1 | SI^A / 1 (in WB) | S / 1 | U, pend GetS C1 | 1 | C1, C0 WB and memory: 1, PutM(1) in flight to H |
(assumed) grant delivered | SI^A / 1 (in WB) | S / 1 | S{C1} | 1 | C1, C0 WB and memory: 1, PutM(1) in flight to H |
5. PutM W at H (non-holder) | SI^A / 1 (in WB) | S / 1 | S{C1} | 1 | C1, C0 WB and memory: 1 |
6. Put-Ack at C0 | I / - | S / 1 | S{C1} | 1 | C1 and memory: 1 |
H writes memory and leaves the entry U, pend GetS C1: in MSI an S record claims memory is current, and C0, answering from its WB entry, is not recorded as a sharer. If W reaches H before C0's Data, H holds W in X's pending entry until the forward is answered; Part 10's A Power-Down Race traces that order.
H never makes R wait for W. If it did, and W were queued behind R, R would wait for W while W waited for R. In SI^A C0's WB entry still holds 1 but serves no loads, and H does not count it as a sharer, so the final sharer set is {C1}. If H instead treated the late W as a normal writeback, it could overwrite newer data in memory or remove C1's record; acknowledging it without change avoids both.
C0's line was already evicted. It answers from the WB entry and goes to SI^A, which serves no loads and only waits for Put-Ack. Its Data says the PutM is outstanding, so H records S{C1}, not {C0,C1}.