 ac06724a71
			
		
	
	
		ac06724a71
		
	
	
	
	
		
			
			Developer documentation should be its own manual. As a start, move all developer-oriented files to a separate directory. Also move non-text files to their own directories: docs/config/ for QEMU -readconfig input, and docs/spin/ for formal models to be used with the SPIN model checker. Reviewed-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
		
			
				
	
	
		
			226 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Promela
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Promela
		
	
	
	
	
	
| /*
 | |
|  * This model describes the implementation of exclusive sections in
 | |
|  * cpus-common.c (start_exclusive, end_exclusive, cpu_exec_start,
 | |
|  * cpu_exec_end).
 | |
|  *
 | |
|  * Author: Paolo Bonzini <pbonzini@redhat.com>
 | |
|  *
 | |
|  * This file is in the public domain.  If you really want a license,
 | |
|  * the WTFPL will do.
 | |
|  *
 | |
|  * To verify it:
 | |
|  *     spin -a docs/tcg-exclusive.promela
 | |
|  *     gcc pan.c -O2
 | |
|  *     ./a.out -a
 | |
|  *
 | |
|  * Tunable processor macros: N_CPUS, N_EXCLUSIVE, N_CYCLES, USE_MUTEX,
 | |
|  *                           TEST_EXPENSIVE.
 | |
|  */
 | |
| 
 | |
| // Define the missing parameters for the model
 | |
| #ifndef N_CPUS
 | |
| #define N_CPUS 2
 | |
| #warning defaulting to 2 CPU processes
 | |
| #endif
 | |
| 
 | |
| // the expensive test is not so expensive for <= 2 CPUs
 | |
| // If the mutex is used, it's also cheap (300 MB / 4 seconds) for 3 CPUs
 | |
| // For 3 CPUs and the lock-free option it needs 1.5 GB of RAM
 | |
| #if N_CPUS <= 2 || (N_CPUS <= 3 && defined USE_MUTEX)
 | |
| #define TEST_EXPENSIVE
 | |
| #endif
 | |
| 
 | |
| #ifndef N_EXCLUSIVE
 | |
| # if !defined N_CYCLES || N_CYCLES <= 1 || defined TEST_EXPENSIVE
 | |
| #  define N_EXCLUSIVE     2
 | |
| #  warning defaulting to 2 concurrent exclusive sections
 | |
| # else
 | |
| #  define N_EXCLUSIVE     1
 | |
| #  warning defaulting to 1 concurrent exclusive sections
 | |
| # endif
 | |
| #endif
 | |
| #ifndef N_CYCLES
 | |
| # if N_EXCLUSIVE <= 1 || defined TEST_EXPENSIVE
 | |
| #  define N_CYCLES        2
 | |
| #  warning defaulting to 2 CPU cycles
 | |
| # else
 | |
| #  define N_CYCLES        1
 | |
| #  warning defaulting to 1 CPU cycles
 | |
| # endif
 | |
| #endif
 | |
| 
 | |
| 
 | |
| // synchronization primitives.  condition variables require a
 | |
| // process-local "cond_t saved;" variable.
 | |
| 
 | |
| #define mutex_t              byte
 | |
| #define MUTEX_LOCK(m)        atomic { m == 0 -> m = 1 }
 | |
| #define MUTEX_UNLOCK(m)      m = 0
 | |
| 
 | |
| #define cond_t               int
 | |
| #define COND_WAIT(c, m)      {                                  \
 | |
|                                saved = c;                       \
 | |
|                                MUTEX_UNLOCK(m);                 \
 | |
|                                c != saved -> MUTEX_LOCK(m);     \
 | |
|                              }
 | |
| #define COND_BROADCAST(c)    c++
 | |
| 
 | |
| // this is the logic from cpus-common.c
 | |
| 
 | |
| mutex_t mutex;
 | |
| cond_t exclusive_cond;
 | |
| cond_t exclusive_resume;
 | |
| byte pending_cpus;
 | |
| 
 | |
| byte running[N_CPUS];
 | |
| byte has_waiter[N_CPUS];
 | |
| 
 | |
| #define exclusive_idle()                                          \
 | |
|   do                                                              \
 | |
|       :: pending_cpus -> COND_WAIT(exclusive_resume, mutex);      \
 | |
|       :: else         -> break;                                   \
 | |
|   od
 | |
| 
 | |
| #define start_exclusive()                                         \
 | |
|     MUTEX_LOCK(mutex);                                            \
 | |
|     exclusive_idle();                                             \
 | |
|     pending_cpus = 1;                                             \
 | |
|                                                                   \
 | |
|     i = 0;                                                        \
 | |
|     do                                                            \
 | |
|        :: i < N_CPUS -> {                                         \
 | |
|            if                                                     \
 | |
|               :: running[i] -> has_waiter[i] = 1; pending_cpus++; \
 | |
|               :: else       -> skip;                              \
 | |
|            fi;                                                    \
 | |
|            i++;                                                   \
 | |
|        }                                                          \
 | |
|        :: else -> break;                                          \
 | |
|     od;                                                           \
 | |
|                                                                   \
 | |
|     do                                                            \
 | |
|       :: pending_cpus > 1 -> COND_WAIT(exclusive_cond, mutex);    \
 | |
|       :: else             -> break;                               \
 | |
|     od;                                                           \
 | |
|     MUTEX_UNLOCK(mutex);
 | |
| 
 | |
| #define end_exclusive()                                           \
 | |
|     MUTEX_LOCK(mutex);                                            \
 | |
|     pending_cpus = 0;                                             \
 | |
|     COND_BROADCAST(exclusive_resume);                             \
 | |
|     MUTEX_UNLOCK(mutex);
 | |
| 
 | |
| #ifdef USE_MUTEX
 | |
| // Simple version using mutexes
 | |
| #define cpu_exec_start(id)                                                   \
 | |
|     MUTEX_LOCK(mutex);                                                       \
 | |
|     exclusive_idle();                                                        \
 | |
|     running[id] = 1;                                                         \
 | |
|     MUTEX_UNLOCK(mutex);
 | |
| 
 | |
| #define cpu_exec_end(id)                                                     \
 | |
|     MUTEX_LOCK(mutex);                                                       \
 | |
|     running[id] = 0;                                                         \
 | |
|     if                                                                       \
 | |
|         :: pending_cpus -> {                                                 \
 | |
|             pending_cpus--;                                                  \
 | |
|             if                                                               \
 | |
|                 :: pending_cpus == 1 -> COND_BROADCAST(exclusive_cond);      \
 | |
|                 :: else -> skip;                                             \
 | |
|             fi;                                                              \
 | |
|         }                                                                    \
 | |
|         :: else -> skip;                                                     \
 | |
|     fi;                                                                      \
 | |
|     MUTEX_UNLOCK(mutex);
 | |
| #else
 | |
| // Wait-free fast path, only needs mutex when concurrent with
 | |
| // an exclusive section
 | |
| #define cpu_exec_start(id)                                                   \
 | |
|     running[id] = 1;                                                         \
 | |
|     if                                                                       \
 | |
|         :: pending_cpus -> {                                                 \
 | |
|             MUTEX_LOCK(mutex);                                               \
 | |
|             if                                                               \
 | |
|                 :: !has_waiter[id] -> {                                      \
 | |
|                     running[id] = 0;                                         \
 | |
|                     exclusive_idle();                                        \
 | |
|                     running[id] = 1;                                         \
 | |
|                 }                                                            \
 | |
|                 :: else -> skip;                                             \
 | |
|             fi;                                                              \
 | |
|             MUTEX_UNLOCK(mutex);                                             \
 | |
|         }                                                                    \
 | |
|         :: else -> skip;                                                     \
 | |
|     fi;
 | |
| 
 | |
| #define cpu_exec_end(id)                                                     \
 | |
|     running[id] = 0;                                                         \
 | |
|     if                                                                       \
 | |
|         :: pending_cpus -> {                                                 \
 | |
|             MUTEX_LOCK(mutex);                                               \
 | |
|             if                                                               \
 | |
|                 :: has_waiter[id] -> {                                       \
 | |
|                     has_waiter[id] = 0;                                      \
 | |
|                     pending_cpus--;                                          \
 | |
|                     if                                                       \
 | |
|                         :: pending_cpus == 1 -> COND_BROADCAST(exclusive_cond); \
 | |
|                         :: else -> skip;                                     \
 | |
|                     fi;                                                      \
 | |
|                 }                                                            \
 | |
|                 :: else -> skip;                                             \
 | |
|             fi;                                                              \
 | |
|             MUTEX_UNLOCK(mutex);                                             \
 | |
|         }                                                                    \
 | |
|         :: else -> skip;                                                     \
 | |
|     fi
 | |
| #endif
 | |
| 
 | |
| // Promela processes
 | |
| 
 | |
| byte done_cpu;
 | |
| byte in_cpu;
 | |
| active[N_CPUS] proctype cpu()
 | |
| {
 | |
|     byte id = _pid % N_CPUS;
 | |
|     byte cycles = 0;
 | |
|     cond_t saved;
 | |
| 
 | |
|     do
 | |
|        :: cycles == N_CYCLES -> break;
 | |
|        :: else -> {
 | |
|            cycles++;
 | |
|            cpu_exec_start(id)
 | |
|            in_cpu++;
 | |
|            done_cpu++;
 | |
|            in_cpu--;
 | |
|            cpu_exec_end(id)
 | |
|        }
 | |
|     od;
 | |
| }
 | |
| 
 | |
| byte done_exclusive;
 | |
| byte in_exclusive;
 | |
| active[N_EXCLUSIVE] proctype exclusive()
 | |
| {
 | |
|     cond_t saved;
 | |
|     byte i;
 | |
| 
 | |
|     start_exclusive();
 | |
|     in_exclusive = 1;
 | |
|     done_exclusive++;
 | |
|     in_exclusive = 0;
 | |
|     end_exclusive();
 | |
| }
 | |
| 
 | |
| #define LIVENESS   (done_cpu == N_CPUS * N_CYCLES && done_exclusive == N_EXCLUSIVE)
 | |
| #define SAFETY     !(in_exclusive && in_cpu)
 | |
| 
 | |
| never {    /* ! ([] SAFETY && <> [] LIVENESS) */
 | |
|     do
 | |
|     // once the liveness property is satisfied, this is not executable
 | |
|     // and the never clause is not accepted
 | |
|     :: ! LIVENESS -> accept_liveness: skip
 | |
|     :: 1          -> assert(SAFETY)
 | |
|     od;
 | |
| }
 |