qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

lockable: add QemuLockable

QemuLockable is a polymorphic lock type that takes an object and
knows which function to use for locking and unlocking. The
implementation could use C11 _Generic, but since the support is
not very widespread I am instead using __builtin_choose_expr and
__builtin_types_compatible_p, which are already used by
include/qemu/atomic.h.

QemuLockable can be used to implement lock guards, or to pass around
a lock in such a way that a function can release it and re-acquire it.
The next patch will do this for CoQueue.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20180203153935.8056-3-pbonzini@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Fam Zheng <famz@redhat.com>

authored by

Paolo Bonzini and committed by
Fam Zheng
e70372fc 439b6e5e

+168 -5
+39
include/qemu/compiler.h
··· 114 114 #ifndef __has_feature 115 115 #define __has_feature(x) 0 /* compatibility with non-clang compilers */ 116 116 #endif 117 + /* Implement C11 _Generic via GCC builtins. Example: 118 + * 119 + * QEMU_GENERIC(x, (float, sinf), (long double, sinl), sin) (x) 120 + * 121 + * The first argument is the discriminator. The last is the default value. 122 + * The middle ones are tuples in "(type, expansion)" format. 123 + */ 124 + 125 + /* First, find out the number of generic cases. */ 126 + #define QEMU_GENERIC(x, ...) \ 127 + QEMU_GENERIC_(typeof(x), __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 128 + 129 + /* There will be extra arguments, but they are not used. */ 130 + #define QEMU_GENERIC_(x, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, count, ...) \ 131 + QEMU_GENERIC##count(x, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) 132 + 133 + /* Two more helper macros, this time to extract items from a parenthesized 134 + * list. 135 + */ 136 + #define QEMU_FIRST_(a, b) a 137 + #define QEMU_SECOND_(a, b) b 138 + 139 + /* ... and a final one for the common part of the "recursion". */ 140 + #define QEMU_GENERIC_IF(x, type_then, else_) \ 141 + __builtin_choose_expr(__builtin_types_compatible_p(x, \ 142 + QEMU_FIRST_ type_then), \ 143 + QEMU_SECOND_ type_then, else_) 144 + 145 + /* CPP poor man's "recursion". */ 146 + #define QEMU_GENERIC1(x, a0, ...) (a0) 147 + #define QEMU_GENERIC2(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC1(x, __VA_ARGS__)) 148 + #define QEMU_GENERIC3(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC2(x, __VA_ARGS__)) 149 + #define QEMU_GENERIC4(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC3(x, __VA_ARGS__)) 150 + #define QEMU_GENERIC5(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC4(x, __VA_ARGS__)) 151 + #define QEMU_GENERIC6(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC5(x, __VA_ARGS__)) 152 + #define QEMU_GENERIC7(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC6(x, __VA_ARGS__)) 153 + #define QEMU_GENERIC8(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC7(x, __VA_ARGS__)) 154 + #define QEMU_GENERIC9(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC8(x, __VA_ARGS__)) 155 + #define QEMU_GENERIC10(x, a0, ...) QEMU_GENERIC_IF(x, a0, QEMU_GENERIC9(x, __VA_ARGS__)) 117 156 118 157 #endif /* COMPILER_H */
+2 -2
include/qemu/coroutine.h
··· 121 121 * Provides a mutex that can be used to synchronise coroutines 122 122 */ 123 123 struct CoWaitRecord; 124 - typedef struct CoMutex { 124 + struct CoMutex { 125 125 /* Count of pending lockers; 0 for a free mutex, 1 for an 126 126 * uncontended mutex. 127 127 */ ··· 142 142 unsigned handoff, sequence; 143 143 144 144 Coroutine *holder; 145 - } CoMutex; 145 + }; 146 146 147 147 /** 148 148 * Initialises a CoMutex. This must be called before any other operation is used
+96
include/qemu/lockable.h
··· 1 + /* 2 + * Polymorphic locking functions (aka poor man templates) 3 + * 4 + * Copyright Red Hat, Inc. 2017, 2018 5 + * 6 + * Author: Paolo Bonzini <pbonzini@redhat.com> 7 + * 8 + * This work is licensed under the terms of the GNU LGPL, version 2 or later. 9 + * See the COPYING.LIB file in the top-level directory. 10 + * 11 + */ 12 + 13 + #ifndef QEMU_LOCKABLE_H 14 + #define QEMU_LOCKABLE_H 15 + 16 + #include "qemu/coroutine.h" 17 + #include "qemu/thread.h" 18 + 19 + typedef void QemuLockUnlockFunc(void *); 20 + 21 + struct QemuLockable { 22 + void *object; 23 + QemuLockUnlockFunc *lock; 24 + QemuLockUnlockFunc *unlock; 25 + }; 26 + 27 + /* This function gives an error if an invalid, non-NULL pointer type is passed 28 + * to QEMU_MAKE_LOCKABLE. For optimized builds, we can rely on dead-code elimination 29 + * from the compiler, and give the errors already at link time. 30 + */ 31 + #ifdef __OPTIMIZE__ 32 + void unknown_lock_type(void *); 33 + #else 34 + static inline void unknown_lock_type(void *unused) 35 + { 36 + abort(); 37 + } 38 + #endif 39 + 40 + static inline __attribute__((__always_inline__)) QemuLockable * 41 + qemu_make_lockable(void *x, QemuLockable *lockable) 42 + { 43 + /* We cannot test this in a macro, otherwise we get compiler 44 + * warnings like "the address of 'm' will always evaluate as 'true'". 45 + */ 46 + return x ? lockable : NULL; 47 + } 48 + 49 + /* Auxiliary macros to simplify QEMU_MAKE_LOCABLE. */ 50 + #define QEMU_LOCK_FUNC(x) ((QemuLockUnlockFunc *) \ 51 + QEMU_GENERIC(x, \ 52 + (QemuMutex *, qemu_mutex_lock), \ 53 + (CoMutex *, qemu_co_mutex_lock), \ 54 + (QemuSpin *, qemu_spin_lock), \ 55 + unknown_lock_type)) 56 + 57 + #define QEMU_UNLOCK_FUNC(x) ((QemuLockUnlockFunc *) \ 58 + QEMU_GENERIC(x, \ 59 + (QemuMutex *, qemu_mutex_unlock), \ 60 + (CoMutex *, qemu_co_mutex_unlock), \ 61 + (QemuSpin *, qemu_spin_unlock), \ 62 + unknown_lock_type)) 63 + 64 + /* In C, compound literals have the lifetime of an automatic variable. 65 + * In C++ it would be different, but then C++ wouldn't need QemuLockable 66 + * either... 67 + */ 68 + #define QEMU_MAKE_LOCKABLE_(x) qemu_make_lockable((x), &(QemuLockable) { \ 69 + .object = (x), \ 70 + .lock = QEMU_LOCK_FUNC(x), \ 71 + .unlock = QEMU_UNLOCK_FUNC(x), \ 72 + }) 73 + 74 + /* QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable 75 + * 76 + * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). 77 + * 78 + * Returns a QemuLockable object that can be passed around 79 + * to a function that can operate with locks of any kind. 80 + */ 81 + #define QEMU_MAKE_LOCKABLE(x) \ 82 + QEMU_GENERIC(x, \ 83 + (QemuLockable *, (x)), \ 84 + QEMU_MAKE_LOCKABLE_(x)) 85 + 86 + static inline void qemu_lockable_lock(QemuLockable *x) 87 + { 88 + x->lock(x->object); 89 + } 90 + 91 + static inline void qemu_lockable_unlock(QemuLockable *x) 92 + { 93 + x->unlock(x->object); 94 + } 95 + 96 + #endif
+2 -3
include/qemu/thread.h
··· 4 4 #include "qemu/processor.h" 5 5 #include "qemu/atomic.h" 6 6 7 - typedef struct QemuMutex QemuMutex; 8 7 typedef struct QemuCond QemuCond; 9 8 typedef struct QemuSemaphore QemuSemaphore; 10 9 typedef struct QemuEvent QemuEvent; ··· 97 96 void qemu_thread_atexit_add(struct Notifier *notifier); 98 97 void qemu_thread_atexit_remove(struct Notifier *notifier); 99 98 100 - typedef struct QemuSpin { 99 + struct QemuSpin { 101 100 int value; 102 - } QemuSpin; 101 + }; 103 102 104 103 static inline void qemu_spin_init(QemuSpin *spin) 105 104 {
+4
include/qemu/typedefs.h
··· 19 19 typedef struct BusState BusState; 20 20 typedef struct Chardev Chardev; 21 21 typedef struct CompatProperty CompatProperty; 22 + typedef struct CoMutex CoMutex; 22 23 typedef struct CPUAddressSpace CPUAddressSpace; 23 24 typedef struct CPUState CPUState; 24 25 typedef struct DeviceListener DeviceListener; ··· 86 87 typedef struct QemuConsole QemuConsole; 87 88 typedef struct QemuDmaBuf QemuDmaBuf; 88 89 typedef struct QEMUFile QEMUFile; 90 + typedef struct QemuLockable QemuLockable; 91 + typedef struct QemuMutex QemuMutex; 89 92 typedef struct QemuOpt QemuOpt; 90 93 typedef struct QemuOpts QemuOpts; 91 94 typedef struct QemuOptsList QemuOptsList; 95 + typedef struct QemuSpin QemuSpin; 92 96 typedef struct QEMUSGList QEMUSGList; 93 97 typedef struct QEMUTimer QEMUTimer; 94 98 typedef struct QEMUTimerListGroup QEMUTimerListGroup;
+25
tests/test-coroutine.c
··· 14 14 #include "qemu/osdep.h" 15 15 #include "qemu/coroutine.h" 16 16 #include "qemu/coroutine_int.h" 17 + #include "qemu/lockable.h" 17 18 18 19 /* 19 20 * Check that qemu_in_coroutine() works ··· 207 208 qemu_coroutine_yield(); 208 209 locked = false; 209 210 qemu_co_mutex_unlock(m); 211 + done++; 212 + } 213 + 214 + static void coroutine_fn lockable_fn(void *opaque) 215 + { 216 + QemuLockable *x = opaque; 217 + qemu_lockable_lock(x); 218 + assert(!locked); 219 + locked = true; 220 + qemu_coroutine_yield(); 221 + locked = false; 222 + qemu_lockable_unlock(x); 210 223 done++; 211 224 } 212 225 ··· 240 253 do_test_co_mutex(mutex_fn, &m); 241 254 } 242 255 256 + static void test_co_mutex_lockable(void) 257 + { 258 + CoMutex m; 259 + CoMutex *null_pointer = NULL; 260 + 261 + qemu_co_mutex_init(&m); 262 + do_test_co_mutex(lockable_fn, QEMU_MAKE_LOCKABLE(&m)); 263 + 264 + g_assert(QEMU_MAKE_LOCKABLE(null_pointer) == NULL); 265 + } 266 + 243 267 /* 244 268 * Check that creation, enter, and return work 245 269 */ ··· 478 502 g_test_add_func("/basic/in_coroutine", test_in_coroutine); 479 503 g_test_add_func("/basic/order", test_order); 480 504 g_test_add_func("/locking/co-mutex", test_co_mutex); 505 + g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable); 481 506 if (g_test_perf()) { 482 507 g_test_add_func("/perf/lifecycle", perf_lifecycle); 483 508 g_test_add_func("/perf/nesting", perf_nesting);