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