summaryrefslogtreecommitdiff
path: root/spin
diff options
context:
space:
mode:
authorMike Haertel2023-03-21 08:35:13 -0700
committerMike Haertel2023-03-21 08:35:13 -0700
commitfa364103127162af6656950c56be2e8953010d25 (patch)
treec8afd4a3556880eafdfb09cd3991974d29014193 /spin
parentNefariously add our nefarious plan. (diff)
downloadbiggolf-bug-skip-introduces-failure.tar.xz
Messy spin snapshot with bug demobug-skip-introduces-failure
Diffstat (limited to '')
-rw-r--r--spin/Makefile2
-rw-r--r--spin/snoopy.pml66
2 files changed, 41 insertions, 27 deletions
diff --git a/spin/Makefile b/spin/Makefile
index 73669f9..cb862cf 100644
--- a/spin/Makefile
+++ b/spin/Makefile
@@ -5,7 +5,7 @@ clean:
5 5
6%.pml.trail: %.pml 6%.pml.trail: %.pml
7 @rm -f $@ 7 @rm -f $@
8 spin -search -bitstate -l $< | egrep '^(pan|spin):' 8 spin -search -bit $< | egrep '^(pan|spin):'
9 [ ! -e $@ ] || spin -c -g -l -p -replay $< 9 [ ! -e $@ ] || spin -c -g -l -p -replay $<
10 10
11.SECONDARY: 11.SECONDARY:
diff --git a/spin/snoopy.pml b/spin/snoopy.pml
index 4774b1a..0b4de5d 100644
--- a/spin/snoopy.pml
+++ b/spin/snoopy.pml
@@ -20,70 +20,84 @@ proctype cache(int recvqbase; int sendqbase; int npeers)
20#define ALLPEERS ((1 << npeers) - 1) 20#define ALLPEERS ((1 << npeers) - 1)
21#define RQ(i) qs[recvqbase+i] 21#define RQ(i) qs[recvqbase+i]
22#define SQ(i) qs[sendqbase+i] 22#define SQ(i) qs[sendqbase+i]
23#define ERESET() atomic { ebcmd = I; ebstate = I; enotsent = 0; enotrcvd = 0; ereverse = 0; esource = -1; }
24#define P() noprogress = 0
23 25
24 mtype cstate = I; // I, M, or Busy 26 mtype cstate = I; // I, M, or Busy
25 mtype ebcmd = I; // I, ItoM, or Busy 27 mtype ebcmd = I; // I, ItoM, or Busy
26 int ebstate = I; // I, M, or U 28 mtype ebstate = I; // I, M, or U
27 int enotsent = 0; // bitmask of snoops remaining to send 29 int enotsent = 0; // bitmask of snoops remaining to send
28 int enotrcvd = 0; // bitmask of replies remaining to receive 30 int enotrcvd = 0; // bitmask of replies remaining to receive
29 int ereverse = 0; // count of reverse progress 31 int ereverse = 0; // count of reverse progress
30 int esource = -1; // source of command (-1 is originated locally) 32 int esource = -1; // source of command (-1 is originated locally)
31 int i = 0; // current peer 33 int i = 0; // current peer
34 int fairmask = 0; // fairness mask to ensure servicing all peers
35 int noprogress = 0; // increment loop breaker
32 36
33 do 37 do
34 // Receive fill request from peer 38 // Receive fill request from peer
35 :: atomic { RQ(i)?ItoM -> if 39 :: atomic { RQ(i)?ItoM -> if
36 :: cstate == M -> cstate = I -> SQ(i)!M 40 :: cstate == M -> cstate = I -> SQ(i)!M
37 :: cstate == I && ebcmd == I -> ebcmd = ItoM -> enotsent = ALLPEERS & ~(1 << i) -> esource = i 41 :: cstate != M && ebcmd == I -> ebcmd = ItoM -> enotsent = ALLPEERS & ~(1 << i) -> esource = i
38 :: else -> SQ(i)!X 42 :: else -> SQ(i)!X
39 fi } 43 fi -> P() }
40 44
41 // Receive fill responses from peers 45 // Receive fill responses from peers
42 :: atomic { RQ(i)?I -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) } 46 :: atomic { RQ(i)?I -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> P() }
43 :: atomic { RQ(i)?M -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> ebstate = M -> if 47 :: atomic { RQ(i)?M -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> ebstate = M -> if
44 :: esource != -1 -> SQ(esource)!M 48 :: esource != -1 -> SQ(esource)!M
45 :: else /* ideally put it into the cache right away */ 49 :: else /* ideally put it into the cache right away */
46 fi } 50 fi -> P() }
47 :: atomic { RQ(i)?U -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> if :: ebstate == I -> ebstate = U :: else fi } 51 :: atomic { RQ(i)?U -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> if :: ebstate == I -> ebstate = U :: else fi -> P() }
48 :: atomic { RQ(i)?X -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> enotsent = enotsent | (1 << i) -> ereverse = ereverse + 1 -> if 52 :: atomic { RQ(i)?X -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> enotsent = enotsent | (1 << i) -> ereverse = ereverse + 1 -> if
49 // To prevent deadlock, if we get X'd too many times abandon the transaction and send X to its source 53 // To prevent deadlock, if we get X'd too many times abandon the transaction and send X to its source
50 :: ereverse > npeers && esource == -1 && enotsent == ALLPEERS -> ebcmd = I -> ebstate = I -> enotsent = 0 -> ereverse = 0 -> esource = -1 54 :: ereverse > npeers && esource == -1 && enotsent == ALLPEERS -> ERESET()
51 :: ereverse > npeers && esource != -1 && enotsent == ALLPEERS & ~(1 << esource) -> SQ(esource)!X -> ebcmd = I -> ebstate = I -> enotsent = 0 -> ereverse = 0 -> esource = -1 55 :: ereverse > npeers && esource != -1 && enotsent == ALLPEERS & ~(1 << esource) -> SQ(esource)!X -> ERESET()
52 :: else 56 :: else
53 fi } 57 fi -> P() }
54 58
55 // Send not-yet-sent snoops 59 // Send not-yet-sent snoops
56 :: atomic { ebcmd == ItoM && (enotsent & (1 << i)) -> enotsent = enotsent & ~(1 << i) -> enotrcvd = enotrcvd | (1 << i) -> SQ(i)!ItoM } 60 :: atomic { ebcmd == ItoM && (enotsent & (1 << i)) -> enotsent = enotsent & ~(1 << i) -> enotrcvd = enotrcvd | (1 << i) -> SQ(i)!ItoM -> P() }
57 61
58 // Complete requests from peers (sending response if not sent earlier) 62 // Complete requests from peers (sending response to source if not already sent earlier)
59 :: atomic { ebcmd == ItoM && enotsent == 0 && enotrcvd == 0 && esource != -1 -> if 63 :: atomic { ebcmd == ItoM && enotsent == 0 && enotrcvd == 0 && esource != -1 -> if
60 :: ebstate == I -> SQ(esource)!I 64 :: ebstate == I -> SQ(esource)!I
61 :: ebstate == M 65 :: ebstate == M -> skip
62 :: ebstate == U -> SQ(esource)!U 66 :: ebstate == U -> SQ(esource)!U
63 :: else -> assert(false) 67 :: else -> assert(false)
64 fi -> ebcmd = I -> ebstate = I -> ereverse = 0 -> esource = -1 } 68 fi -> ERESET() -> P() }
65 69
66 // Complete locally-originated request when cache space is available to hold the data 70 // Complete locally-originated request when cache space is available to hold the data
67 :: atomic { ebcmd == ItoM && enotsent == 0 && enotrcvd == 0 && esource == -1 && cstate == I -> if 71 :: atomic { ebcmd == ItoM && enotsent == 0 && enotrcvd == 0 && esource == -1 && cstate == I -> if
68 :: (ebstate == M | ebstate == U) -> cstate = M 72 :: (ebstate == M | ebstate == U) -> cstate = M
69 :: else -> assert(false) 73 :: else -> assert(false)
70 fi -> ebcmd = I -> ebstate = I } 74 fi -> ERESET() -> P() } -> skip
71 75
72 :: else -> atomic { if 76#if 0
73 // Simulate local cache being occupied by other address 77 // Simulate local resources in use due to activity at other addresses
74 :: atomic { cstate == I -> cstate = Busy } 78 :: atomic { if :: cstate == I -> cstate = Busy :: cstate == Busy -> cstate = I fi }
75 :: atomic { cstate == Busy -> cstate = I } 79 :: atomic { if :: ebcmd == I -> ebcmd = Busy :: ebcmd == Busy -> ebcmd = I }
80#endif
76 81
77 // Simulate evilbuffers conducting transaction for other address 82#if 0 // these assertions further explode the states
78 :: atomic { ebcmd == I -> ebcmd = Busy } 83 // If we're not in a transaction assert that the evil buffer is fully reset (just to make sure we've limited reachable states)
79 :: atomic { ebcmd == Busy -> ebcmd = I } 84 :: atomic { ebcmd == Busy || ebcmd == I -> assert(ebstate == I && enotsent == 0 && enotrcvd == 0 && ereverse == 0 && esource == -1) }
85 // If we're in ItoM transaction assert that it's not in the cache
86 :: atomic { ebcmd == ItoM -> assert(cstate == I || cstate == Busy) }
87#endif
80 88
81 // Launch locally-requested fills; rely on deadlock avoidance above to protect from multiple peers simultaneously deciding to do this 89 // Launch locally-requested fills; rely on deadlock avoidance above to protect from multiple peers simultaneously deciding to do this
82 :: atomic { ebcmd == I -> ebcmd = ItoM -> enotsent = ALLPEERS -> enotrcvd = 0 -> ereverse = 0 -> esource = -1 } 90 :: atomic { cstate != M && ebcmd == I -> ebcmd = ItoM -> enotsent = ALLPEERS -> esource = -1 -> P() }
83 91
84 // Cycle through connections to all peers to ensure fairness 92#if 1
85 :: atomic { i = (i + 1) % npeers } 93 // Cycle through connections to all peers to ensure fairness
86 fi } 94 :: atomic { noprogress < 2 -> i = (i + 1) % npeers -> if :: i == 0 -> noprogress = noprogress + 1 :: else fi }
95#else
96 :: atomic { noprogress < 2 && npeers >= 1 && (fairmask & 1) == 0 -> i = 0 -> fairmask = fairmask | 1 }
97 :: atomic { noprogress < 2 && npeers >= 2 && (fairmask & 2) == 0 -> i = 1 -> fairmask = fairmask | 2 }
98 :: atomic { noprogress < 2 && npeers >= 3 && (fairmask & 4) == 0 -> i = 2 -> fairmask = fairmask | 4 }
99 :: atomic { fairmask == (1 << npeers) - 1 -> fairmask = 0 -> noprogress = noprogress + 1 }
100#endif
87 od 101 od
88 102
89#undef ALLPEERS 103#undef ALLPEERS