xref: /openbmc/qemu/docs/spin/tcg-exclusive.promela (revision bbfa326fc8028e275eddf8c9965c2a1b59405b2e)
1*ac06724aSPaolo Bonzini/*
2*ac06724aSPaolo Bonzini * This model describes the implementation of exclusive sections in
3*ac06724aSPaolo Bonzini * cpus-common.c (start_exclusive, end_exclusive, cpu_exec_start,
4*ac06724aSPaolo Bonzini * cpu_exec_end).
5*ac06724aSPaolo Bonzini *
6*ac06724aSPaolo Bonzini * Author: Paolo Bonzini <pbonzini@redhat.com>
7*ac06724aSPaolo Bonzini *
8*ac06724aSPaolo Bonzini * This file is in the public domain.  If you really want a license,
9*ac06724aSPaolo Bonzini * the WTFPL will do.
10*ac06724aSPaolo Bonzini *
11*ac06724aSPaolo Bonzini * To verify it:
12*ac06724aSPaolo Bonzini *     spin -a docs/tcg-exclusive.promela
13*ac06724aSPaolo Bonzini *     gcc pan.c -O2
14*ac06724aSPaolo Bonzini *     ./a.out -a
15*ac06724aSPaolo Bonzini *
16*ac06724aSPaolo Bonzini * Tunable processor macros: N_CPUS, N_EXCLUSIVE, N_CYCLES, USE_MUTEX,
17*ac06724aSPaolo Bonzini *                           TEST_EXPENSIVE.
18*ac06724aSPaolo Bonzini */
19*ac06724aSPaolo Bonzini
20*ac06724aSPaolo Bonzini// Define the missing parameters for the model
21*ac06724aSPaolo Bonzini#ifndef N_CPUS
22*ac06724aSPaolo Bonzini#define N_CPUS 2
23*ac06724aSPaolo Bonzini#warning defaulting to 2 CPU processes
24*ac06724aSPaolo Bonzini#endif
25*ac06724aSPaolo Bonzini
26*ac06724aSPaolo Bonzini// the expensive test is not so expensive for <= 2 CPUs
27*ac06724aSPaolo Bonzini// If the mutex is used, it's also cheap (300 MB / 4 seconds) for 3 CPUs
28*ac06724aSPaolo Bonzini// For 3 CPUs and the lock-free option it needs 1.5 GB of RAM
29*ac06724aSPaolo Bonzini#if N_CPUS <= 2 || (N_CPUS <= 3 && defined USE_MUTEX)
30*ac06724aSPaolo Bonzini#define TEST_EXPENSIVE
31*ac06724aSPaolo Bonzini#endif
32*ac06724aSPaolo Bonzini
33*ac06724aSPaolo Bonzini#ifndef N_EXCLUSIVE
34*ac06724aSPaolo Bonzini# if !defined N_CYCLES || N_CYCLES <= 1 || defined TEST_EXPENSIVE
35*ac06724aSPaolo Bonzini#  define N_EXCLUSIVE     2
36*ac06724aSPaolo Bonzini#  warning defaulting to 2 concurrent exclusive sections
37*ac06724aSPaolo Bonzini# else
38*ac06724aSPaolo Bonzini#  define N_EXCLUSIVE     1
39*ac06724aSPaolo Bonzini#  warning defaulting to 1 concurrent exclusive sections
40*ac06724aSPaolo Bonzini# endif
41*ac06724aSPaolo Bonzini#endif
42*ac06724aSPaolo Bonzini#ifndef N_CYCLES
43*ac06724aSPaolo Bonzini# if N_EXCLUSIVE <= 1 || defined TEST_EXPENSIVE
44*ac06724aSPaolo Bonzini#  define N_CYCLES        2
45*ac06724aSPaolo Bonzini#  warning defaulting to 2 CPU cycles
46*ac06724aSPaolo Bonzini# else
47*ac06724aSPaolo Bonzini#  define N_CYCLES        1
48*ac06724aSPaolo Bonzini#  warning defaulting to 1 CPU cycles
49*ac06724aSPaolo Bonzini# endif
50*ac06724aSPaolo Bonzini#endif
51*ac06724aSPaolo Bonzini
52*ac06724aSPaolo Bonzini
53*ac06724aSPaolo Bonzini// synchronization primitives.  condition variables require a
54*ac06724aSPaolo Bonzini// process-local "cond_t saved;" variable.
55*ac06724aSPaolo Bonzini
56*ac06724aSPaolo Bonzini#define mutex_t              byte
57*ac06724aSPaolo Bonzini#define MUTEX_LOCK(m)        atomic { m == 0 -> m = 1 }
58*ac06724aSPaolo Bonzini#define MUTEX_UNLOCK(m)      m = 0
59*ac06724aSPaolo Bonzini
60*ac06724aSPaolo Bonzini#define cond_t               int
61*ac06724aSPaolo Bonzini#define COND_WAIT(c, m)      {                                  \
62*ac06724aSPaolo Bonzini                               saved = c;                       \
63*ac06724aSPaolo Bonzini                               MUTEX_UNLOCK(m);                 \
64*ac06724aSPaolo Bonzini                               c != saved -> MUTEX_LOCK(m);     \
65*ac06724aSPaolo Bonzini                             }
66*ac06724aSPaolo Bonzini#define COND_BROADCAST(c)    c++
67*ac06724aSPaolo Bonzini
68*ac06724aSPaolo Bonzini// this is the logic from cpus-common.c
69*ac06724aSPaolo Bonzini
70*ac06724aSPaolo Bonzinimutex_t mutex;
71*ac06724aSPaolo Bonzinicond_t exclusive_cond;
72*ac06724aSPaolo Bonzinicond_t exclusive_resume;
73*ac06724aSPaolo Bonzinibyte pending_cpus;
74*ac06724aSPaolo Bonzini
75*ac06724aSPaolo Bonzinibyte running[N_CPUS];
76*ac06724aSPaolo Bonzinibyte has_waiter[N_CPUS];
77*ac06724aSPaolo Bonzini
78*ac06724aSPaolo Bonzini#define exclusive_idle()                                          \
79*ac06724aSPaolo Bonzini  do                                                              \
80*ac06724aSPaolo Bonzini      :: pending_cpus -> COND_WAIT(exclusive_resume, mutex);      \
81*ac06724aSPaolo Bonzini      :: else         -> break;                                   \
82*ac06724aSPaolo Bonzini  od
83*ac06724aSPaolo Bonzini
84*ac06724aSPaolo Bonzini#define start_exclusive()                                         \
85*ac06724aSPaolo Bonzini    MUTEX_LOCK(mutex);                                            \
86*ac06724aSPaolo Bonzini    exclusive_idle();                                             \
87*ac06724aSPaolo Bonzini    pending_cpus = 1;                                             \
88*ac06724aSPaolo Bonzini                                                                  \
89*ac06724aSPaolo Bonzini    i = 0;                                                        \
90*ac06724aSPaolo Bonzini    do                                                            \
91*ac06724aSPaolo Bonzini       :: i < N_CPUS -> {                                         \
92*ac06724aSPaolo Bonzini           if                                                     \
93*ac06724aSPaolo Bonzini              :: running[i] -> has_waiter[i] = 1; pending_cpus++; \
94*ac06724aSPaolo Bonzini              :: else       -> skip;                              \
95*ac06724aSPaolo Bonzini           fi;                                                    \
96*ac06724aSPaolo Bonzini           i++;                                                   \
97*ac06724aSPaolo Bonzini       }                                                          \
98*ac06724aSPaolo Bonzini       :: else -> break;                                          \
99*ac06724aSPaolo Bonzini    od;                                                           \
100*ac06724aSPaolo Bonzini                                                                  \
101*ac06724aSPaolo Bonzini    do                                                            \
102*ac06724aSPaolo Bonzini      :: pending_cpus > 1 -> COND_WAIT(exclusive_cond, mutex);    \
103*ac06724aSPaolo Bonzini      :: else             -> break;                               \
104*ac06724aSPaolo Bonzini    od;                                                           \
105*ac06724aSPaolo Bonzini    MUTEX_UNLOCK(mutex);
106*ac06724aSPaolo Bonzini
107*ac06724aSPaolo Bonzini#define end_exclusive()                                           \
108*ac06724aSPaolo Bonzini    MUTEX_LOCK(mutex);                                            \
109*ac06724aSPaolo Bonzini    pending_cpus = 0;                                             \
110*ac06724aSPaolo Bonzini    COND_BROADCAST(exclusive_resume);                             \
111*ac06724aSPaolo Bonzini    MUTEX_UNLOCK(mutex);
112*ac06724aSPaolo Bonzini
113*ac06724aSPaolo Bonzini#ifdef USE_MUTEX
114*ac06724aSPaolo Bonzini// Simple version using mutexes
115*ac06724aSPaolo Bonzini#define cpu_exec_start(id)                                                   \
116*ac06724aSPaolo Bonzini    MUTEX_LOCK(mutex);                                                       \
117*ac06724aSPaolo Bonzini    exclusive_idle();                                                        \
118*ac06724aSPaolo Bonzini    running[id] = 1;                                                         \
119*ac06724aSPaolo Bonzini    MUTEX_UNLOCK(mutex);
120*ac06724aSPaolo Bonzini
121*ac06724aSPaolo Bonzini#define cpu_exec_end(id)                                                     \
122*ac06724aSPaolo Bonzini    MUTEX_LOCK(mutex);                                                       \
123*ac06724aSPaolo Bonzini    running[id] = 0;                                                         \
124*ac06724aSPaolo Bonzini    if                                                                       \
125*ac06724aSPaolo Bonzini        :: pending_cpus -> {                                                 \
126*ac06724aSPaolo Bonzini            pending_cpus--;                                                  \
127*ac06724aSPaolo Bonzini            if                                                               \
128*ac06724aSPaolo Bonzini                :: pending_cpus == 1 -> COND_BROADCAST(exclusive_cond);      \
129*ac06724aSPaolo Bonzini                :: else -> skip;                                             \
130*ac06724aSPaolo Bonzini            fi;                                                              \
131*ac06724aSPaolo Bonzini        }                                                                    \
132*ac06724aSPaolo Bonzini        :: else -> skip;                                                     \
133*ac06724aSPaolo Bonzini    fi;                                                                      \
134*ac06724aSPaolo Bonzini    MUTEX_UNLOCK(mutex);
135*ac06724aSPaolo Bonzini#else
136*ac06724aSPaolo Bonzini// Wait-free fast path, only needs mutex when concurrent with
137*ac06724aSPaolo Bonzini// an exclusive section
138*ac06724aSPaolo Bonzini#define cpu_exec_start(id)                                                   \
139*ac06724aSPaolo Bonzini    running[id] = 1;                                                         \
140*ac06724aSPaolo Bonzini    if                                                                       \
141*ac06724aSPaolo Bonzini        :: pending_cpus -> {                                                 \
142*ac06724aSPaolo Bonzini            MUTEX_LOCK(mutex);                                               \
143*ac06724aSPaolo Bonzini            if                                                               \
144*ac06724aSPaolo Bonzini                :: !has_waiter[id] -> {                                      \
145*ac06724aSPaolo Bonzini                    running[id] = 0;                                         \
146*ac06724aSPaolo Bonzini                    exclusive_idle();                                        \
147*ac06724aSPaolo Bonzini                    running[id] = 1;                                         \
148*ac06724aSPaolo Bonzini                }                                                            \
149*ac06724aSPaolo Bonzini                :: else -> skip;                                             \
150*ac06724aSPaolo Bonzini            fi;                                                              \
151*ac06724aSPaolo Bonzini            MUTEX_UNLOCK(mutex);                                             \
152*ac06724aSPaolo Bonzini        }                                                                    \
153*ac06724aSPaolo Bonzini        :: else -> skip;                                                     \
154*ac06724aSPaolo Bonzini    fi;
155*ac06724aSPaolo Bonzini
156*ac06724aSPaolo Bonzini#define cpu_exec_end(id)                                                     \
157*ac06724aSPaolo Bonzini    running[id] = 0;                                                         \
158*ac06724aSPaolo Bonzini    if                                                                       \
159*ac06724aSPaolo Bonzini        :: pending_cpus -> {                                                 \
160*ac06724aSPaolo Bonzini            MUTEX_LOCK(mutex);                                               \
161*ac06724aSPaolo Bonzini            if                                                               \
162*ac06724aSPaolo Bonzini                :: has_waiter[id] -> {                                       \
163*ac06724aSPaolo Bonzini                    has_waiter[id] = 0;                                      \
164*ac06724aSPaolo Bonzini                    pending_cpus--;                                          \
165*ac06724aSPaolo Bonzini                    if                                                       \
166*ac06724aSPaolo Bonzini                        :: pending_cpus == 1 -> COND_BROADCAST(exclusive_cond); \
167*ac06724aSPaolo Bonzini                        :: else -> skip;                                     \
168*ac06724aSPaolo Bonzini                    fi;                                                      \
169*ac06724aSPaolo Bonzini                }                                                            \
170*ac06724aSPaolo Bonzini                :: else -> skip;                                             \
171*ac06724aSPaolo Bonzini            fi;                                                              \
172*ac06724aSPaolo Bonzini            MUTEX_UNLOCK(mutex);                                             \
173*ac06724aSPaolo Bonzini        }                                                                    \
174*ac06724aSPaolo Bonzini        :: else -> skip;                                                     \
175*ac06724aSPaolo Bonzini    fi
176*ac06724aSPaolo Bonzini#endif
177*ac06724aSPaolo Bonzini
178*ac06724aSPaolo Bonzini// Promela processes
179*ac06724aSPaolo Bonzini
180*ac06724aSPaolo Bonzinibyte done_cpu;
181*ac06724aSPaolo Bonzinibyte in_cpu;
182*ac06724aSPaolo Bonziniactive[N_CPUS] proctype cpu()
183*ac06724aSPaolo Bonzini{
184*ac06724aSPaolo Bonzini    byte id = _pid % N_CPUS;
185*ac06724aSPaolo Bonzini    byte cycles = 0;
186*ac06724aSPaolo Bonzini    cond_t saved;
187*ac06724aSPaolo Bonzini
188*ac06724aSPaolo Bonzini    do
189*ac06724aSPaolo Bonzini       :: cycles == N_CYCLES -> break;
190*ac06724aSPaolo Bonzini       :: else -> {
191*ac06724aSPaolo Bonzini           cycles++;
192*ac06724aSPaolo Bonzini           cpu_exec_start(id)
193*ac06724aSPaolo Bonzini           in_cpu++;
194*ac06724aSPaolo Bonzini           done_cpu++;
195*ac06724aSPaolo Bonzini           in_cpu--;
196*ac06724aSPaolo Bonzini           cpu_exec_end(id)
197*ac06724aSPaolo Bonzini       }
198*ac06724aSPaolo Bonzini    od;
199*ac06724aSPaolo Bonzini}
200*ac06724aSPaolo Bonzini
201*ac06724aSPaolo Bonzinibyte done_exclusive;
202*ac06724aSPaolo Bonzinibyte in_exclusive;
203*ac06724aSPaolo Bonziniactive[N_EXCLUSIVE] proctype exclusive()
204*ac06724aSPaolo Bonzini{
205*ac06724aSPaolo Bonzini    cond_t saved;
206*ac06724aSPaolo Bonzini    byte i;
207*ac06724aSPaolo Bonzini
208*ac06724aSPaolo Bonzini    start_exclusive();
209*ac06724aSPaolo Bonzini    in_exclusive = 1;
210*ac06724aSPaolo Bonzini    done_exclusive++;
211*ac06724aSPaolo Bonzini    in_exclusive = 0;
212*ac06724aSPaolo Bonzini    end_exclusive();
213*ac06724aSPaolo Bonzini}
214*ac06724aSPaolo Bonzini
215*ac06724aSPaolo Bonzini#define LIVENESS   (done_cpu == N_CPUS * N_CYCLES && done_exclusive == N_EXCLUSIVE)
216*ac06724aSPaolo Bonzini#define SAFETY     !(in_exclusive && in_cpu)
217*ac06724aSPaolo Bonzini
218*ac06724aSPaolo Bonzininever {    /* ! ([] SAFETY && <> [] LIVENESS) */
219*ac06724aSPaolo Bonzini    do
220*ac06724aSPaolo Bonzini    // once the liveness property is satisfied, this is not executable
221*ac06724aSPaolo Bonzini    // and the never clause is not accepted
222*ac06724aSPaolo Bonzini    :: ! LIVENESS -> accept_liveness: skip
223*ac06724aSPaolo Bonzini    :: 1          -> assert(SAFETY)
224*ac06724aSPaolo Bonzini    od;
225*ac06724aSPaolo Bonzini}
226