123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- /*
- * Copyright (C) 2016 Red Hat, Inc.
- * Author: Michael S. Tsirkin <mst@redhat.com>
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
- * Common macros and functions for ring benchmarking.
- */
- #ifndef MAIN_H
- #define MAIN_H
- #include <stdbool.h>
- extern int param;
- extern bool do_exit;
- #if defined(__x86_64__) || defined(__i386__)
- #include "x86intrin.h"
- static inline void wait_cycles(unsigned long long cycles)
- {
- unsigned long long t;
- t = __rdtsc();
- while (__rdtsc() - t < cycles) {}
- }
- #define VMEXIT_CYCLES 500
- #define VMENTRY_CYCLES 500
- #elif defined(__s390x__)
- static inline void wait_cycles(unsigned long long cycles)
- {
- asm volatile("0: brctg %0,0b" : : "d" (cycles));
- }
- /* tweak me */
- #define VMEXIT_CYCLES 200
- #define VMENTRY_CYCLES 200
- #else
- static inline void wait_cycles(unsigned long long cycles)
- {
- _Exit(5);
- }
- #define VMEXIT_CYCLES 0
- #define VMENTRY_CYCLES 0
- #endif
- static inline void vmexit(void)
- {
- if (!do_exit)
- return;
-
- wait_cycles(VMEXIT_CYCLES);
- }
- static inline void vmentry(void)
- {
- if (!do_exit)
- return;
-
- wait_cycles(VMENTRY_CYCLES);
- }
- /* implemented by ring */
- void alloc_ring(void);
- /* guest side */
- int add_inbuf(unsigned, void *, void *);
- void *get_buf(unsigned *, void **);
- void disable_call();
- bool used_empty();
- bool enable_call();
- void kick_available();
- /* host side */
- void disable_kick();
- bool avail_empty();
- bool enable_kick();
- bool use_buf(unsigned *, void **);
- void call_used();
- /* implemented by main */
- extern bool do_sleep;
- void kick(void);
- void wait_for_kick(void);
- void call(void);
- void wait_for_call(void);
- extern unsigned ring_size;
- /* Compiler barrier - similar to what Linux uses */
- #define barrier() asm volatile("" ::: "memory")
- /* Is there a portable way to do this? */
- #if defined(__x86_64__) || defined(__i386__)
- #define cpu_relax() asm ("rep; nop" ::: "memory")
- #elif defined(__s390x__)
- #define cpu_relax() barrier()
- #else
- #define cpu_relax() assert(0)
- #endif
- extern bool do_relax;
- static inline void busy_wait(void)
- {
- if (do_relax)
- cpu_relax();
- else
- /* prevent compiler from removing busy loops */
- barrier();
- }
- #if defined(__x86_64__) || defined(__i386__)
- #define smp_mb() asm volatile("lock; addl $0,-132(%%rsp)" ::: "memory", "cc")
- #else
- /*
- * Not using __ATOMIC_SEQ_CST since gcc docs say they are only synchronized
- * with other __ATOMIC_SEQ_CST calls.
- */
- #define smp_mb() __sync_synchronize()
- #endif
- /*
- * This abuses the atomic builtins for thread fences, and
- * adds a compiler barrier.
- */
- #define smp_release() do { \
- barrier(); \
- __atomic_thread_fence(__ATOMIC_RELEASE); \
- } while (0)
- #define smp_acquire() do { \
- __atomic_thread_fence(__ATOMIC_ACQUIRE); \
- barrier(); \
- } while (0)
- #if defined(__i386__) || defined(__x86_64__) || defined(__s390x__)
- #define smp_wmb() barrier()
- #else
- #define smp_wmb() smp_release()
- #endif
- #ifdef __alpha__
- #define smp_read_barrier_depends() smp_acquire()
- #else
- #define smp_read_barrier_depends() do {} while(0)
- #endif
- static __always_inline
- void __read_once_size(const volatile void *p, void *res, int size)
- {
- switch (size) { \
- case 1: *(unsigned char *)res = *(volatile unsigned char *)p; break; \
- case 2: *(unsigned short *)res = *(volatile unsigned short *)p; break; \
- case 4: *(unsigned int *)res = *(volatile unsigned int *)p; break; \
- case 8: *(unsigned long long *)res = *(volatile unsigned long long *)p; break; \
- default: \
- barrier(); \
- __builtin_memcpy((void *)res, (const void *)p, size); \
- barrier(); \
- } \
- }
- static __always_inline void __write_once_size(volatile void *p, void *res, int size)
- {
- switch (size) {
- case 1: *(volatile unsigned char *)p = *(unsigned char *)res; break;
- case 2: *(volatile unsigned short *)p = *(unsigned short *)res; break;
- case 4: *(volatile unsigned int *)p = *(unsigned int *)res; break;
- case 8: *(volatile unsigned long long *)p = *(unsigned long long *)res; break;
- default:
- barrier();
- __builtin_memcpy((void *)p, (const void *)res, size);
- barrier();
- }
- }
- #define READ_ONCE(x) \
- ({ \
- union { typeof(x) __val; char __c[1]; } __u; \
- __read_once_size(&(x), __u.__c, sizeof(x)); \
- smp_read_barrier_depends(); /* Enforce dependency ordering from x */ \
- __u.__val; \
- })
- #define WRITE_ONCE(x, val) \
- ({ \
- union { typeof(x) __val; char __c[1]; } __u = \
- { .__val = (typeof(x)) (val) }; \
- __write_once_size(&(x), __u.__c, sizeof(x)); \
- __u.__val; \
- })
- #endif
|