Commit | Line | Data |
---|---|---|
481eaec3 MT |
1 | /* |
2 | * Copyright (C) 2016 Red Hat, Inc. | |
3 | * Author: Michael S. Tsirkin <mst@redhat.com> | |
4 | * This work is licensed under the terms of the GNU GPL, version 2. | |
5 | * | |
6 | * Common macros and functions for ring benchmarking. | |
7 | */ | |
8 | #ifndef MAIN_H | |
9 | #define MAIN_H | |
10 | ||
11 | #include <stdbool.h> | |
12 | ||
13 | extern bool do_exit; | |
14 | ||
15 | #if defined(__x86_64__) || defined(__i386__) | |
16 | #include "x86intrin.h" | |
17 | ||
18 | static inline void wait_cycles(unsigned long long cycles) | |
19 | { | |
20 | unsigned long long t; | |
21 | ||
22 | t = __rdtsc(); | |
23 | while (__rdtsc() - t < cycles) {} | |
24 | } | |
25 | ||
26 | #define VMEXIT_CYCLES 500 | |
27 | #define VMENTRY_CYCLES 500 | |
28 | ||
29 | #else | |
30 | static inline void wait_cycles(unsigned long long cycles) | |
31 | { | |
32 | _Exit(5); | |
33 | } | |
34 | #define VMEXIT_CYCLES 0 | |
35 | #define VMENTRY_CYCLES 0 | |
36 | #endif | |
37 | ||
38 | static inline void vmexit(void) | |
39 | { | |
40 | if (!do_exit) | |
41 | return; | |
42 | ||
43 | wait_cycles(VMEXIT_CYCLES); | |
44 | } | |
45 | static inline void vmentry(void) | |
46 | { | |
47 | if (!do_exit) | |
48 | return; | |
49 | ||
50 | wait_cycles(VMENTRY_CYCLES); | |
51 | } | |
52 | ||
53 | /* implemented by ring */ | |
54 | void alloc_ring(void); | |
55 | /* guest side */ | |
56 | int add_inbuf(unsigned, void *, void *); | |
57 | void *get_buf(unsigned *, void **); | |
58 | void disable_call(); | |
59 | bool enable_call(); | |
60 | void kick_available(); | |
61 | void poll_used(); | |
62 | /* host side */ | |
63 | void disable_kick(); | |
64 | bool enable_kick(); | |
65 | bool use_buf(unsigned *, void **); | |
66 | void call_used(); | |
67 | void poll_avail(); | |
68 | ||
69 | /* implemented by main */ | |
70 | extern bool do_sleep; | |
71 | void kick(void); | |
72 | void wait_for_kick(void); | |
73 | void call(void); | |
74 | void wait_for_call(void); | |
75 | ||
76 | extern unsigned ring_size; | |
77 | ||
78 | /* Compiler barrier - similar to what Linux uses */ | |
79 | #define barrier() asm volatile("" ::: "memory") | |
80 | ||
81 | /* Is there a portable way to do this? */ | |
82 | #if defined(__x86_64__) || defined(__i386__) | |
83 | #define cpu_relax() asm ("rep; nop" ::: "memory") | |
84 | #else | |
85 | #define cpu_relax() assert(0) | |
86 | #endif | |
87 | ||
88 | extern bool do_relax; | |
89 | ||
90 | static inline void busy_wait(void) | |
91 | { | |
92 | if (do_relax) | |
93 | cpu_relax(); | |
94 | else | |
95 | /* prevent compiler from removing busy loops */ | |
96 | barrier(); | |
97 | } | |
98 | ||
99 | /* | |
100 | * Not using __ATOMIC_SEQ_CST since gcc docs say they are only synchronized | |
101 | * with other __ATOMIC_SEQ_CST calls. | |
102 | */ | |
103 | #define smp_mb() __sync_synchronize() | |
104 | ||
105 | /* | |
106 | * This abuses the atomic builtins for thread fences, and | |
107 | * adds a compiler barrier. | |
108 | */ | |
109 | #define smp_release() do { \ | |
110 | barrier(); \ | |
111 | __atomic_thread_fence(__ATOMIC_RELEASE); \ | |
112 | } while (0) | |
113 | ||
114 | #define smp_acquire() do { \ | |
115 | __atomic_thread_fence(__ATOMIC_ACQUIRE); \ | |
116 | barrier(); \ | |
117 | } while (0) | |
118 | ||
119 | #endif |