From f7795e4096d8bd1c767c5ddb450fa859ff20490e Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Wed, 4 Mar 2020 16:38:15 +0100 Subject: misc: Replace zero-length arrays with flexible array member (automatic) Description copied from Linux kernel commit from Gustavo A. R. Silva (see [3]): --v-- description start --v-- The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member [1], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being unadvertenly introduced [2] to the Linux codebase from now on. --^-- description end --^-- Do the similar housekeeping in the QEMU codebase (which uses C99 since commit 7be41675f7cb). All these instances of code were found with the help of the following Coccinelle script: @@ identifier s, m, a; type t, T; @@ struct s { ... t m; - T a[0]; + T a[]; }; @@ identifier s, m, a; type t, T; @@ struct s { ... t m; - T a[0]; + T a[]; } QEMU_PACKED; [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76497732932f [3] https://git.kernel.org/pub/scm/linux/kernel/git/gustavoars/linux.git/commit/?id=17642a2fbd2c1 Inspired-by: Gustavo A. R. Silva Reviewed-by: David Hildenbrand Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/hw/acpi/acpi-defs.h | 12 ++++++------ include/hw/arm/smmu-common.h | 2 +- include/hw/i386/intel_iommu.h | 3 ++- include/hw/virtio/virtio-iommu.h | 2 +- include/sysemu/cryptodev.h | 2 +- include/tcg/tcg.h | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 57a3f58b0c..19f7ba7b70 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -518,7 +518,7 @@ struct AcpiDmarDeviceScope { struct { uint8_t device; uint8_t function; - } path[0]; + } path[]; } QEMU_PACKED; typedef struct AcpiDmarDeviceScope AcpiDmarDeviceScope; @@ -530,7 +530,7 @@ struct AcpiDmarHardwareUnit { uint8_t reserved; uint16_t pci_segment; /* The PCI Segment associated with this unit */ uint64_t address; /* Base address of remapping hardware register-set */ - AcpiDmarDeviceScope scope[0]; + AcpiDmarDeviceScope scope[]; } QEMU_PACKED; typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit; @@ -541,7 +541,7 @@ struct AcpiDmarRootPortATS { uint8_t flags; uint8_t reserved; uint16_t pci_segment; - AcpiDmarDeviceScope scope[0]; + AcpiDmarDeviceScope scope[]; } QEMU_PACKED; typedef struct AcpiDmarRootPortATS AcpiDmarRootPortATS; @@ -604,7 +604,7 @@ typedef struct AcpiIortMemoryAccess AcpiIortMemoryAccess; struct AcpiIortItsGroup { ACPI_IORT_NODE_HEADER_DEF uint32_t its_count; - uint32_t identifiers[0]; + uint32_t identifiers[]; } QEMU_PACKED; typedef struct AcpiIortItsGroup AcpiIortItsGroup; @@ -621,7 +621,7 @@ struct AcpiIortSmmu3 { uint32_t pri_gsiv; uint32_t gerr_gsiv; uint32_t sync_gsiv; - AcpiIortIdMapping id_mapping_array[0]; + AcpiIortIdMapping id_mapping_array[]; } QEMU_PACKED; typedef struct AcpiIortSmmu3 AcpiIortSmmu3; @@ -630,7 +630,7 @@ struct AcpiIortRC { AcpiIortMemoryAccess memory_properties; uint32_t ats_attribute; uint32_t pci_segment_number; - AcpiIortIdMapping id_mapping_array[0]; + AcpiIortIdMapping id_mapping_array[]; } QEMU_PACKED; typedef struct AcpiIortRC AcpiIortRC; diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 1f37844e5c..ca4a4b1ad1 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -85,7 +85,7 @@ typedef struct SMMUDevice { typedef struct SMMUPciBus { PCIBus *bus; - SMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */ + SMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */ } SMMUPciBus; typedef struct SMMUIOTLBKey { diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index a1c4afcda5..3870052f5f 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -114,7 +114,8 @@ struct VTDAddressSpace { struct VTDBus { PCIBus* bus; /* A reference to the bus to provide translation for */ - VTDAddressSpace *dev_as[0]; /* A table of VTDAddressSpace objects indexed by devfn */ + /* A table of VTDAddressSpace objects indexed by devfn */ + VTDAddressSpace *dev_as[]; }; struct VTDIOTLBEntry { diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 6f67f1020a..e653004d7c 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -41,7 +41,7 @@ typedef struct IOMMUDevice { typedef struct IOMMUPciBus { PCIBus *bus; - IOMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */ + IOMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */ } IOMMUPciBus; typedef struct VirtIOIOMMU { diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h index a9afb7e5b5..35eab06d0e 100644 --- a/include/sysemu/cryptodev.h +++ b/include/sysemu/cryptodev.h @@ -143,7 +143,7 @@ typedef struct CryptoDevBackendSymOpInfo { uint8_t *dst; uint8_t *aad_data; uint8_t *digest_result; - uint8_t data[0]; + uint8_t data[]; } CryptoDevBackendSymOpInfo; typedef struct CryptoDevBackendClass { diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 54e5446880..c48bd76b0a 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -267,7 +267,7 @@ struct TCGLabel { typedef struct TCGPool { struct TCGPool *next; int size; - uint8_t data[0] __attribute__ ((aligned)); + uint8_t data[] __attribute__ ((aligned)); } TCGPool; #define TCG_POOL_CHUNK_SIZE 32768 -- cgit v1.2.3-55-g7522 From 880a7817c1a82a93d3f83dfb25dce1f0db629c66 Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Wed, 4 Mar 2020 16:38:16 +0100 Subject: misc: Replace zero-length arrays with flexible array member (manual) Description copied from Linux kernel commit from Gustavo A. R. Silva (see [3]): --v-- description start --v-- The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member [1], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being unadvertenly introduced [2] to the Linux codebase from now on. --^-- description end --^-- Do the similar housekeeping in the QEMU codebase (which uses C99 since commit 7be41675f7cb). All these instances of code were found with the help of the following command (then manual analysis, without modifying structures only having a single flexible array member, such QEDTable in block/qed.h): git grep -F '[0];' [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76497732932f [3] https://git.kernel.org/pub/scm/linux/kernel/git/gustavoars/linux.git/commit/?id=17642a2fbd2c1 Inspired-by: Gustavo A. R. Silva Reviewed-by: David Hildenbrand Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- block/vmdk.c | 2 +- docs/interop/vhost-user.rst | 4 ++-- hw/char/sclpconsole-lm.c | 2 +- hw/char/sclpconsole.c | 2 +- hw/s390x/virtio-ccw.c | 2 +- include/hw/acpi/acpi-defs.h | 4 ++-- include/hw/boards.h | 2 +- include/hw/s390x/event-facility.h | 2 +- include/hw/s390x/sclp.h | 8 ++++---- target/s390x/ioinst.c | 2 +- 10 files changed, 15 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/block/vmdk.c b/block/vmdk.c index 20e909d997..8466051bc9 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -187,7 +187,7 @@ typedef struct VmdkMetaData { typedef struct VmdkGrainMarker { uint64_t lba; uint32_t size; - uint8_t data[0]; + uint8_t data[]; } QEMU_PACKED VmdkGrainMarker; enum { diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 401652397c..3b1b6602c7 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -568,7 +568,7 @@ For split virtqueue, queue region can be implemented as: uint16_t used_idx; /* Used to track the state of each descriptor in descriptor table */ - DescStateSplit desc[0]; + DescStateSplit desc[]; } QueueRegionSplit; To track inflight I/O, the queue region should be processed as follows: @@ -690,7 +690,7 @@ For packed virtqueue, queue region can be implemented as: uint8_t padding[7]; /* Used to track the state of each descriptor fetched from descriptor ring */ - DescStatePacked desc[0]; + DescStatePacked desc[]; } QueueRegionPacked; To track inflight I/O, the queue region should be processed as follows: diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index c420dc066e..2b5f37b6a2 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -31,7 +31,7 @@ typedef struct OprtnsCommand { EventBufferHeader header; MDMSU message_unit; - char data[0]; + char data[]; } QEMU_PACKED OprtnsCommand; /* max size for line-mode data in 4K SCCB page */ diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index 1fa124dab9..5c7664905e 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -25,7 +25,7 @@ typedef struct ASCIIConsoleData { EventBufferHeader ebh; - char data[0]; + char data[]; } QEMU_PACKED ASCIIConsoleData; /* max size for ASCII data in 4K SCCB page */ diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 50cf95b781..64f928fc7d 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -193,7 +193,7 @@ typedef struct VirtioThinintInfo { typedef struct VirtioRevInfo { uint16_t revision; uint16_t length; - uint8_t data[0]; + uint8_t data[]; } QEMU_PACKED VirtioRevInfo; /* Specify where the virtqueues for the subchannel are in guest memory. */ diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 19f7ba7b70..c13327fa78 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -152,7 +152,7 @@ typedef struct AcpiSerialPortConsoleRedirection */ struct AcpiRsdtDescriptorRev1 { ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint32_t table_offset_entry[0]; /* Array of pointers to other */ + uint32_t table_offset_entry[]; /* Array of pointers to other */ /* ACPI tables */ } QEMU_PACKED; typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1; @@ -162,7 +162,7 @@ typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1; */ struct AcpiXsdtDescriptorRev2 { ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint64_t table_offset_entry[0]; /* Array of pointers to other */ + uint64_t table_offset_entry[]; /* Array of pointers to other */ /* ACPI tables */ } QEMU_PACKED; typedef struct AcpiXsdtDescriptorRev2 AcpiXsdtDescriptorRev2; diff --git a/include/hw/boards.h b/include/hw/boards.h index 9bc42dfb22..c96120d15f 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -71,7 +71,7 @@ typedef struct CPUArchId { */ typedef struct { int len; - CPUArchId cpus[0]; + CPUArchId cpus[]; } CPUArchIdList; /** diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index bdc32a3c09..700a610f33 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -122,7 +122,7 @@ typedef struct MDBO { typedef struct MDB { MdbHeader header; - MDBO mdbo[0]; + MDBO mdbo[]; } QEMU_PACKED MDB; typedef struct SclpMsg { diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index c54413b78c..cd7b24359f 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -132,7 +132,7 @@ typedef struct ReadInfo { uint16_t highest_cpu; uint8_t _reserved5[124 - 122]; /* 122-123 */ uint32_t hmfai; - struct CPUEntry entries[0]; + struct CPUEntry entries[]; } QEMU_PACKED ReadInfo; typedef struct ReadCpuInfo { @@ -142,7 +142,7 @@ typedef struct ReadCpuInfo { uint16_t nr_standby; /* 12-13 */ uint16_t offset_standby; /* 14-15 */ uint8_t reserved0[24-16]; /* 16-23 */ - struct CPUEntry entries[0]; + struct CPUEntry entries[]; } QEMU_PACKED ReadCpuInfo; typedef struct ReadStorageElementInfo { @@ -151,7 +151,7 @@ typedef struct ReadStorageElementInfo { uint16_t assigned; uint16_t standby; uint8_t _reserved0[16 - 14]; /* 14-15 */ - uint32_t entries[0]; + uint32_t entries[]; } QEMU_PACKED ReadStorageElementInfo; typedef struct AttachStorageElement { @@ -159,7 +159,7 @@ typedef struct AttachStorageElement { uint8_t _reserved0[10 - 8]; /* 8-9 */ uint16_t assigned; uint8_t _reserved1[16 - 12]; /* 12-15 */ - uint32_t entries[0]; + uint32_t entries[]; } QEMU_PACKED AttachStorageElement; typedef struct AssignStorage { diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index c437a1d8c6..0e840cc579 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -347,7 +347,7 @@ typedef struct ChscResp { uint16_t len; uint16_t code; uint32_t param; - char data[0]; + char data[]; } QEMU_PACKED ChscResp; #define CHSC_MIN_RESP_LEN 0x0008 -- cgit v1.2.3-55-g7522 From 27f08ea1c7abf04125f6f9f23b8ba2f8c20e95b6 Mon Sep 17 00:00:00 2001 From: Robert Hoo Date: Sat, 29 Feb 2020 20:34:35 +0800 Subject: util: add util function buffer_zero_avx512() And intialize buffer_is_zero() with it, when Intel AVX512F is available on host. This function utilizes Intel AVX512 fundamental instructions which is faster than its implementation with AVX2 (in my unit test, with 4K buffer, on CascadeLake SP, ~36% faster, buffer_zero_avx512() V.S. buffer_zero_avx2()). Signed-off-by: Robert Hoo Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- include/qemu/cpuid.h | 3 +++ util/bufferiszero.c | 71 ++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/qemu/cpuid.h b/include/qemu/cpuid.h index 69301700bd..09fc245b91 100644 --- a/include/qemu/cpuid.h +++ b/include/qemu/cpuid.h @@ -45,6 +45,9 @@ #ifndef bit_AVX2 #define bit_AVX2 (1 << 5) #endif +#ifndef bit_AVX512F +#define bit_AVX512F (1 << 16) +#endif #ifndef bit_BMI2 #define bit_BMI2 (1 << 8) #endif diff --git a/util/bufferiszero.c b/util/bufferiszero.c index bfb2605466..663903553a 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -63,11 +63,11 @@ buffer_zero_int(const void *buf, size_t len) } } -#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) || defined(__SSE2__) /* Do not use push_options pragmas unnecessarily, because clang * does not support them. */ -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #pragma GCC push_options #pragma GCC target("sse2") #endif @@ -104,7 +104,7 @@ buffer_zero_sse2(const void *buf, size_t len) return _mm_movemask_epi8(_mm_cmpeq_epi8(t, zero)) == 0xFFFF; } -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #pragma GCC pop_options #endif @@ -187,18 +187,54 @@ buffer_zero_avx2(const void *buf, size_t len) #pragma GCC pop_options #endif /* CONFIG_AVX2_OPT */ +#ifdef CONFIG_AVX512F_OPT +#pragma GCC push_options +#pragma GCC target("avx512f") +#include + +static bool +buffer_zero_avx512(const void *buf, size_t len) +{ + /* Begin with an unaligned head of 64 bytes. */ + __m512i t = _mm512_loadu_si512(buf); + __m512i *p = (__m512i *)(((uintptr_t)buf + 5 * 64) & -64); + __m512i *e = (__m512i *)(((uintptr_t)buf + len) & -64); + + /* Loop over 64-byte aligned blocks of 256. */ + while (p <= e) { + __builtin_prefetch(p); + if (unlikely(_mm512_test_epi64_mask(t, t))) { + return false; + } + t = p[-4] | p[-3] | p[-2] | p[-1]; + p += 4; + } + + t |= _mm512_loadu_si512(buf + len - 4 * 64); + t |= _mm512_loadu_si512(buf + len - 3 * 64); + t |= _mm512_loadu_si512(buf + len - 2 * 64); + t |= _mm512_loadu_si512(buf + len - 1 * 64); + + return !_mm512_test_epi64_mask(t, t); + +} +#pragma GCC pop_options +#endif + + /* Note that for test_buffer_is_zero_next_accel, the most preferred * ISA must have the least significant bit. */ -#define CACHE_AVX2 1 -#define CACHE_SSE4 2 -#define CACHE_SSE2 4 +#define CACHE_AVX512F 1 +#define CACHE_AVX2 2 +#define CACHE_SSE4 4 +#define CACHE_SSE2 8 /* Make sure that these variables are appropriately initialized when * SSE2 is enabled on the compiler command-line, but the compiler is * too old to support CONFIG_AVX2_OPT. */ -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) # define INIT_CACHE 0 # define INIT_ACCEL buffer_zero_int #else @@ -211,6 +247,7 @@ buffer_zero_avx2(const void *buf, size_t len) static unsigned cpuid_cache = INIT_CACHE; static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL; +static int length_to_accel = 64; static void init_accel(unsigned cache) { @@ -225,11 +262,17 @@ static void init_accel(unsigned cache) if (cache & CACHE_AVX2) { fn = buffer_zero_avx2; } +#endif +#ifdef CONFIG_AVX512F_OPT + if (cache & CACHE_AVX512F) { + fn = buffer_zero_avx512; + length_to_accel = 256; + } #endif buffer_accel = fn; } -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #include "qemu/cpuid.h" static void __attribute__((constructor)) init_cpuid_cache(void) @@ -252,9 +295,17 @@ static void __attribute__((constructor)) init_cpuid_cache(void) int bv; __asm("xgetbv" : "=a"(bv), "=d"(d) : "c"(0)); __cpuid_count(7, 0, a, b, c, d); - if ((bv & 6) == 6 && (b & bit_AVX2)) { + if ((bv & 0x6) == 0x6 && (b & bit_AVX2)) { cache |= CACHE_AVX2; } + /* 0xe6: + * XCR0[7:5] = 111b (OPMASK state, upper 256-bit of ZMM0-ZMM15 + * and ZMM16-ZMM31 state are enabled by OS) + * XCR0[2:1] = 11b (XMM state and YMM state are enabled by OS) + */ + if ((bv & 0xe6) == 0xe6 && (b & bit_AVX512F)) { + cache |= CACHE_AVX512F; + } } } cpuid_cache = cache; @@ -277,7 +328,7 @@ bool test_buffer_is_zero_next_accel(void) static bool select_accel_fn(const void *buf, size_t len) { - if (likely(len >= 64)) { + if (likely(len >= length_to_accel)) { return buffer_accel(buf, len); } return buffer_zero_int(buf, len); -- cgit v1.2.3-55-g7522 From 6785e767017a3fcc39e245b7bca2c383b8bf39ef Mon Sep 17 00:00:00 2001 From: Sunil Muthuswamy Date: Wed, 26 Feb 2020 20:54:39 +0000 Subject: WHPX: TSC get and set should be dependent on VM state Currently, TSC is set as part of the VM runtime state. Setting TSC at runtime is heavy and additionally can have side effects on the guest, which are not very resilient to variances in the TSC. This patch uses the VM state to determine whether to set TSC or not. Some minor enhancements for getting TSC values as well that considers the VM state. Additionally, while setting the TSC, the partition is suspended to reduce the variance in the TSC value across vCPUs. Signed-off-by: Sunil Muthuswamy Message-Id: Signed-off-by: Paolo Bonzini --- include/sysemu/whpx.h | 7 +++ target/i386/whp-dispatch.h | 9 ++++ target/i386/whpx-all.c | 103 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 110 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sysemu/whpx.h b/include/sysemu/whpx.h index 4794e8effe..a84b49e749 100644 --- a/include/sysemu/whpx.h +++ b/include/sysemu/whpx.h @@ -35,4 +35,11 @@ int whpx_enabled(void); #endif /* CONFIG_WHPX */ +/* state subset only touched by the VCPU itself during runtime */ +#define WHPX_SET_RUNTIME_STATE 1 +/* state subset modified during VCPU reset */ +#define WHPX_SET_RESET_STATE 2 +/* full state set, modified during initialization or on vmload */ +#define WHPX_SET_FULL_STATE 3 + #endif /* QEMU_WHPX_H */ diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h index 87d049ceab..e4695c349f 100644 --- a/target/i386/whp-dispatch.h +++ b/target/i386/whp-dispatch.h @@ -23,6 +23,12 @@ X(HRESULT, WHvGetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, WHV_REGISTER_VALUE* RegisterValues)) \ X(HRESULT, WHvSetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, const WHV_REGISTER_VALUE* RegisterValues)) \ +/* + * These are supplemental functions that may not be present + * on all versions and are not critical for basic functionality. + */ +#define LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(X) \ + X(HRESULT, WHvSuspendPartitionTime, (WHV_PARTITION_HANDLE Partition)) \ #define LIST_WINHVEMULATION_FUNCTIONS(X) \ X(HRESULT, WHvEmulatorCreateEmulator, (const WHV_EMULATOR_CALLBACKS* Callbacks, WHV_EMULATOR_HANDLE* Emulator)) \ @@ -40,10 +46,12 @@ /* Define function typedef */ LIST_WINHVPLATFORM_FUNCTIONS(WHP_DEFINE_TYPE) LIST_WINHVEMULATION_FUNCTIONS(WHP_DEFINE_TYPE) +LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_DEFINE_TYPE) struct WHPDispatch { LIST_WINHVPLATFORM_FUNCTIONS(WHP_DECLARE_MEMBER) LIST_WINHVEMULATION_FUNCTIONS(WHP_DECLARE_MEMBER) + LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_DECLARE_MEMBER) }; extern struct WHPDispatch whp_dispatch; @@ -53,6 +61,7 @@ bool init_whp_dispatch(void); typedef enum WHPFunctionList { WINHV_PLATFORM_FNS_DEFAULT, WINHV_EMULATION_FNS_DEFAULT, + WINHV_PLATFORM_FNS_SUPPLEMENTAL } WHPFunctionList; #endif /* WHP_DISPATCH_H */ diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index 683d49d217..b947eb1da8 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -114,7 +114,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = { WHvX64RegisterXmmControlStatus, /* X64 MSRs */ - WHvX64RegisterTsc, WHvX64RegisterEfer, #ifdef TARGET_X86_64 WHvX64RegisterKernelGsBase, @@ -215,7 +214,44 @@ static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs) return qs; } -static void whpx_set_registers(CPUState *cpu) +static int whpx_set_tsc(CPUState *cpu) +{ + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; + WHV_REGISTER_VALUE tsc_val; + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + + /* + * Suspend the partition prior to setting the TSC to reduce the variance + * in TSC across vCPUs. When the first vCPU runs post suspend, the + * partition is automatically resumed. + */ + if (whp_dispatch.WHvSuspendPartitionTime) { + + /* + * Unable to suspend partition while setting TSC is not a fatal + * error. It just increases the likelihood of TSC variance between + * vCPUs and some guest OS are able to handle that just fine. + */ + hr = whp_dispatch.WHvSuspendPartitionTime(whpx->partition); + if (FAILED(hr)) { + warn_report("WHPX: Failed to suspend partition, hr=%08lx", hr); + } + } + + tsc_val.Reg64 = env->tsc; + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val); + if (FAILED(hr)) { + error_report("WHPX: Failed to set TSC, hr=%08lx", hr); + return -1; + } + + return 0; +} + +static void whpx_set_registers(CPUState *cpu, int level) { struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); @@ -230,6 +266,14 @@ static void whpx_set_registers(CPUState *cpu) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + /* + * Following MSRs have side effects on the guest or are too heavy for + * runtime. Limit them to full state update. + */ + if (level >= WHPX_SET_RESET_STATE) { + whpx_set_tsc(cpu); + } + memset(&vcxt, 0, sizeof(struct whpx_register_set)); v86 = (env->eflags & VM_MASK); @@ -330,8 +374,6 @@ static void whpx_set_registers(CPUState *cpu) idx += 1; /* MSRs */ - assert(whpx_register_names[idx] == WHvX64RegisterTsc); - vcxt.values[idx++].Reg64 = env->tsc; assert(whpx_register_names[idx] == WHvX64RegisterEfer); vcxt.values[idx++].Reg64 = env->efer; #ifdef TARGET_X86_64 @@ -379,6 +421,25 @@ static void whpx_set_registers(CPUState *cpu) return; } +static int whpx_get_tsc(CPUState *cpu) +{ + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; + WHV_REGISTER_VALUE tsc_val; + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val); + if (FAILED(hr)) { + error_report("WHPX: Failed to get TSC, hr=%08lx", hr); + return -1; + } + + env->tsc = tsc_val.Reg64; + return 0; +} + static void whpx_get_registers(CPUState *cpu) { struct whpx_state *whpx = &whpx_global; @@ -394,6 +455,11 @@ static void whpx_get_registers(CPUState *cpu) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + if (!env->tsc_valid) { + whpx_get_tsc(cpu); + env->tsc_valid = !runstate_is_running(); + } + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( whpx->partition, cpu->cpu_index, whpx_register_names, @@ -492,8 +558,6 @@ static void whpx_get_registers(CPUState *cpu) idx += 1; /* MSRs */ - assert(whpx_register_names[idx] == WHvX64RegisterTsc); - env->tsc = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterEfer); env->efer = vcxt.values[idx++].Reg64; #ifdef TARGET_X86_64 @@ -896,7 +960,7 @@ static int whpx_vcpu_run(CPUState *cpu) do { if (cpu->vcpu_dirty) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE); cpu->vcpu_dirty = false; } @@ -1074,14 +1138,14 @@ static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_RESET_STATE); cpu->vcpu_dirty = false; } static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_FULL_STATE); cpu->vcpu_dirty = false; } @@ -1123,6 +1187,15 @@ void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) static Error *whpx_migration_blocker; +static void whpx_cpu_update_state(void *opaque, int running, RunState state) +{ + CPUX86State *env = opaque; + + if (running) { + env->tsc_valid = false; + } +} + int whpx_init_vcpu(CPUState *cpu) { HRESULT hr; @@ -1178,6 +1251,7 @@ int whpx_init_vcpu(CPUState *cpu) cpu->vcpu_dirty = true; cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; + qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr); return 0; } @@ -1367,6 +1441,10 @@ static bool load_whp_dispatch_fns(HMODULE *handle, #define WINHV_PLATFORM_DLL "WinHvPlatform.dll" #define WINHV_EMULATION_DLL "WinHvEmulation.dll" + #define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \ + whp_dispatch.function_name = \ + (function_name ## _t)GetProcAddress(hLib, #function_name); \ + #define WHP_LOAD_FIELD(return_type, function_name, signature) \ whp_dispatch.function_name = \ (function_name ## _t)GetProcAddress(hLib, #function_name); \ @@ -1394,6 +1472,11 @@ static bool load_whp_dispatch_fns(HMODULE *handle, WHP_LOAD_LIB(WINHV_EMULATION_DLL, hLib) LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD) break; + + case WINHV_PLATFORM_FNS_SUPPLEMENTAL: + WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib) + LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL) + break; } *handle = hLib; @@ -1554,6 +1637,8 @@ bool init_whp_dispatch(void) goto error; } + assert(load_whp_dispatch_fns(&hWinHvPlatform, + WINHV_PLATFORM_FNS_SUPPLEMENTAL)); whp_dispatch_initialized = true; return true; -- cgit v1.2.3-55-g7522 From 836e1b3813c522a9e46f70a10d427f70ff590d77 Mon Sep 17 00:00:00 2001 From: Felipe Franciosi Date: Tue, 4 Feb 2020 13:15:58 +0000 Subject: qom/object: enable setter for uint types Traditionally, the uint-specific property helpers only offer getters. When adding object (or class) uint types, one must therefore use the generic property helper if a setter is needed (and probably duplicate some code writing their own getters/setters). This enhances the uint-specific property helper APIs by adding a bitwise-or'd 'flags' field and modifying all clients of that API to set this paramater to OBJ_PROP_FLAG_READ. This maintains the current behaviour whilst allowing others to also set OBJ_PROP_FLAG_WRITE (or use the more convenient OBJ_PROP_FLAG_READWRITE) in the future (which will automatically install a setter). Other flags may be added later. Signed-off-by: Felipe Franciosi Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- hw/acpi/ich9.c | 4 +- hw/acpi/pcihp.c | 7 +- hw/acpi/piix4.c | 12 +-- hw/isa/lpc_ich9.c | 4 +- hw/ppc/spapr_drc.c | 3 +- include/qom/object.h | 48 ++++++++++-- qom/object.c | 212 +++++++++++++++++++++++++++++++++++++++++++++------ ui/console.c | 4 +- 8 files changed, 246 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 4e74284b65..67fe05ac96 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -454,12 +454,12 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) pm->s4_val = 2; object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, - &pm->pm_io_base, errp); + &pm->pm_io_base, OBJ_PROP_FLAG_READ, errp); object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32", ich9_pm_get_gpe0_blk, NULL, NULL, pm, NULL); object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, - &gpe0_len, errp); + &gpe0_len, OBJ_PROP_FLAG_READ, errp); object_property_add_bool(obj, "memory-hotplug-support", ich9_pm_get_memory_hotplug_support, ich9_pm_set_memory_hotplug_support, diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 8413348a33..4dcef372bf 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -80,7 +80,8 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque) *bus_bsel = (*bsel_alloc)++; object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, &error_abort); + bus_bsel, OBJ_PROP_FLAG_READ, + &error_abort); } return bsel_alloc; @@ -373,9 +374,9 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, memory_region_add_subregion(address_space_io, s->io_base, &s->io); object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_BASE_PROP, &s->io_base, - &error_abort); + OBJ_PROP_FLAG_READ, &error_abort); object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_LEN_PROP, &s->io_len, - &error_abort); + OBJ_PROP_FLAG_READ, &error_abort); } const VMStateDescription vmstate_acpi_pcihp_pci_status = { diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index b84dbba2c3..964d6f5990 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -444,17 +444,17 @@ static void piix4_pm_add_propeties(PIIX4PMState *s) static const uint16_t sci_int = 9; object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_ENABLE_CMD, - &acpi_enable_cmd, NULL); + &acpi_enable_cmd, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_DISABLE_CMD, - &acpi_disable_cmd, NULL); + &acpi_disable_cmd, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK, - &gpe0_blk, NULL); + &gpe0_blk, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK_LEN, - &gpe0_blk_len, NULL); + &gpe0_blk_len, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint16_ptr(OBJECT(s), ACPI_PM_PROP_SCI_INT, - &sci_int, NULL); + &sci_int, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_PM_IO_BASE, - &s->io_base, NULL); + &s->io_base, OBJ_PROP_FLAG_READ, NULL); } static void piix4_pm_realize(PCIDevice *dev, Error **errp) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index cb79616ced..d8186f51d9 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -643,9 +643,9 @@ static void ich9_lpc_add_properties(ICH9LPCState *lpc) ich9_lpc_get_sci_int, NULL, NULL, NULL, NULL); object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_ENABLE_CMD, - &acpi_enable_cmd, NULL); + &acpi_enable_cmd, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_DISABLE_CMD, - &acpi_disable_cmd, NULL); + &acpi_disable_cmd, OBJ_PROP_FLAG_READ, NULL); ich9_pm_add_properties(OBJECT(lpc), &lpc->pm, NULL); } diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index e373d342eb..47e6bb12f9 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -583,7 +583,8 @@ static void spapr_dr_connector_instance_init(Object *obj) SpaprDrc *drc = SPAPR_DR_CONNECTOR(obj); SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - object_property_add_uint32_ptr(obj, "id", &drc->id, NULL); + object_property_add_uint32_ptr(obj, "id", &drc->id, OBJ_PROP_FLAG_READ, + NULL); object_property_add(obj, "index", "uint32", prop_get_index, NULL, NULL, NULL, NULL); object_property_add(obj, "fdt", "struct", prop_get_fdt, diff --git a/include/qom/object.h b/include/qom/object.h index 29546496c1..784c97c0e1 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1664,69 +1664,101 @@ ObjectProperty *object_class_property_add_tm(ObjectClass *klass, void (*get)(Object *, struct tm *, Error **), Error **errp); +typedef enum { + /* Automatically add a getter to the property */ + OBJ_PROP_FLAG_READ = 1 << 0, + /* Automatically add a setter to the property */ + OBJ_PROP_FLAG_WRITE = 1 << 1, + /* Automatically add a getter and a setter to the property */ + OBJ_PROP_FLAG_READWRITE = (OBJ_PROP_FLAG_READ | OBJ_PROP_FLAG_WRITE), +} ObjectPropertyFlags; + /** * object_property_add_uint8_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint8'. */ void object_property_add_uint8_ptr(Object *obj, const char *name, - const uint8_t *v, Error **errp); + const uint8_t *v, ObjectPropertyFlags flags, + Error **errp); + ObjectProperty *object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, - const uint8_t *v, Error **errp); + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint16_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint16'. */ void object_property_add_uint16_ptr(Object *obj, const char *name, - const uint16_t *v, Error **errp); + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp); + ObjectProperty *object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, - const uint16_t *v, Error **errp); + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint32_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint32'. */ void object_property_add_uint32_ptr(Object *obj, const char *name, - const uint32_t *v, Error **errp); + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp); + ObjectProperty *object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, - const uint32_t *v, Error **errp); + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint64_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint64'. */ void object_property_add_uint64_ptr(Object *obj, const char *name, - const uint64_t *v, Error **errp); + const uint64_t *v, + ObjectPropertyFlags flags, + Error **Errp); + ObjectProperty *object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, - const uint64_t *v, Error **errp); + const uint64_t *v, + ObjectPropertyFlags flags, + Error **Errp); /** * object_property_add_alias: diff --git a/qom/object.c b/qom/object.c index 555c8b9d07..1812f79224 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2498,6 +2498,22 @@ static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint8(v, name, &value, errp); } +static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t *field = opaque; + uint8_t value; + Error *local_err = NULL; + + visit_type_uint8(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2505,6 +2521,22 @@ static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint16(v, name, &value, errp); } +static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t *field = opaque; + uint16_t value; + Error *local_err = NULL; + + visit_type_uint16(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2512,6 +2544,22 @@ static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, &value, errp); } +static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t *field = opaque; + uint32_t value; + Error *local_err = NULL; + + visit_type_uint32(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2519,68 +2567,184 @@ static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, &value, errp); } +static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint64_t *field = opaque; + uint64_t value; + Error *local_err = NULL; + + visit_type_uint64(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + void object_property_add_uint8_ptr(Object *obj, const char *name, - const uint8_t *v, Error **errp) + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint8", property_get_uint8_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint8_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint8_ptr; + } + + object_property_add(obj, name, "uint8", + getter, setter, NULL, (void *)v, errp); } ObjectProperty * object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, - const uint8_t *v, Error **errp) + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp) { + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint8_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint8_ptr; + } + return object_class_property_add(klass, name, "uint8", - property_get_uint8_ptr, - NULL, NULL, (void *)v, errp); + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint16_ptr(Object *obj, const char *name, - const uint16_t *v, Error **errp) + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint16", property_get_uint16_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint16_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint16_ptr; + } + + object_property_add(obj, name, "uint16", + getter, setter, NULL, (void *)v, errp); } ObjectProperty * object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, - const uint16_t *v, Error **errp) + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp) { + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint16_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint16_ptr; + } + return object_class_property_add(klass, name, "uint16", - property_get_uint16_ptr, - NULL, NULL, (void *)v, errp); + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint32_ptr(Object *obj, const char *name, - const uint32_t *v, Error **errp) + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint32", property_get_uint32_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint32_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint32_ptr; + } + + object_property_add(obj, name, "uint32", + getter, setter, NULL, (void *)v, errp); } ObjectProperty * object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, - const uint32_t *v, Error **errp) + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp) { + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint32_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint32_ptr; + } + return object_class_property_add(klass, name, "uint32", - property_get_uint32_ptr, - NULL, NULL, (void *)v, errp); + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint64_ptr(Object *obj, const char *name, - const uint64_t *v, Error **errp) + const uint64_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint64", property_get_uint64_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint64_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint64_ptr; + } + + object_property_add(obj, name, "uint64", + getter, setter, NULL, (void *)v, errp); } ObjectProperty * object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, - const uint64_t *v, Error **errp) + const uint64_t *v, + ObjectPropertyFlags flags, + Error **errp) { + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint64_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint64_ptr; + } + return object_class_property_add(klass, name, "uint64", - property_get_uint64_ptr, - NULL, NULL, (void *)v, errp); + getter, setter, NULL, (void *)v, errp); } typedef struct { diff --git a/ui/console.c b/ui/console.c index 179901c35e..184e173687 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1299,8 +1299,8 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, object_property_allow_set_link, OBJ_PROP_LINK_STRONG, &error_abort); - object_property_add_uint32_ptr(obj, "head", - &s->head, &error_abort); + object_property_add_uint32_ptr(obj, "head", &s->head, + OBJ_PROP_FLAG_READ, &error_abort); if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && (console_type == GRAPHIC_CONSOLE))) { -- cgit v1.2.3-55-g7522 From 8834dcf47e8543b92e072706d3a5621762bfa106 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 17 Mar 2020 15:17:20 +0100 Subject: lockable: add QEMU_MAKE_LOCKABLE_NONNULL This will be needed for lock guards, because if the lock is NULL the dummy for loop of the lock guard never runs. This can cause confusion and dummy warnings in the compiler, but even if it did not, aborting with a NULL pointer dereference is a less surprising behavior. Signed-off-by: Paolo Bonzini --- include/qemu/lockable.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 84ea794bcf..313d4d9b73 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -65,7 +65,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) * In C++ it would be different, but then C++ wouldn't need QemuLockable * either... */ -#define QEMU_MAKE_LOCKABLE_(x) qemu_make_lockable((x), &(QemuLockable) { \ +#define QEMU_MAKE_LOCKABLE_(x) (&(QemuLockable) { \ .object = (x), \ .lock = QEMU_LOCK_FUNC(x), \ .unlock = QEMU_UNLOCK_FUNC(x), \ @@ -76,9 +76,22 @@ qemu_make_lockable(void *x, QemuLockable *lockable) * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). * * Returns a QemuLockable object that can be passed around - * to a function that can operate with locks of any kind. + * to a function that can operate with locks of any kind, or + * NULL if @x is %NULL. */ #define QEMU_MAKE_LOCKABLE(x) \ + QEMU_GENERIC(x, \ + (QemuLockable *, (x)), \ + qemu_make_lockable((x), QEMU_MAKE_LOCKABLE_(x))) + +/* QEMU_MAKE_LOCKABLE_NONNULL - Make a polymorphic QemuLockable + * + * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * + * Returns a QemuLockable object that can be passed around + * to a function that can operate with locks of any kind. + */ +#define QEMU_MAKE_LOCKABLE_NONNULL(x) \ QEMU_GENERIC(x, \ (QemuLockable *, (x)), \ QEMU_MAKE_LOCKABLE_(x)) -- cgit v1.2.3-55-g7522 From 3284c3ddc48ba8fc853858c95d87dcc2ab160b29 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 16 Mar 2020 11:09:56 +0000 Subject: lockable: add lock guards This patch introduces two lock guard macros that automatically unlock a lock object (QemuMutex and others): void f(void) { QEMU_LOCK_GUARD(&mutex); if (!may_fail()) { return; /* automatically unlocks mutex */ } ... } and: WITH_QEMU_LOCK_GUARD(&mutex) { if (!may_fail()) { return; /* automatically unlocks mutex */ } } /* automatically unlocks mutex here */ ... Convert qemu-timer.c functions that benefit from these macros as an example. Manual qemu_mutex_lock/unlock() callers are left unmodified in cases where clarity would not improve by switching to the macros. Many other QemuMutex users remain in the codebase that might benefit from lock guards. Over time they can be converted, if that is desirable. Signed-off-by: Stefan Hajnoczi [Use QEMU_MAKE_LOCKABLE_NONNULL. - Paolo] Signed-off-by: Paolo Bonzini --- include/qemu/lockable.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ util/qemu-timer.c | 23 +++++++++-------- 2 files changed, 76 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 313d4d9b73..90342bab98 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -106,4 +106,69 @@ static inline void qemu_lockable_unlock(QemuLockable *x) x->unlock(x->object); } +static inline QemuLockable *qemu_lockable_auto_lock(QemuLockable *x) +{ + qemu_lockable_lock(x); + return x; +} + +static inline void qemu_lockable_auto_unlock(QemuLockable *x) +{ + if (x) { + qemu_lockable_unlock(x); + } +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuLockable, qemu_lockable_auto_unlock) + +#define WITH_QEMU_LOCK_GUARD_(x, var) \ + for (g_autoptr(QemuLockable) var = \ + qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ + var; \ + qemu_lockable_auto_unlock(var), var = NULL) + +/** + * WITH_QEMU_LOCK_GUARD - Lock a lock object for scope + * + * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * + * This macro defines a lock scope such that entering the scope takes the lock + * and leaving the scope releases the lock. Return statements are allowed + * within the scope and release the lock. Break and continue statements leave + * the scope early and release the lock. + * + * WITH_QEMU_LOCK_GUARD(&mutex) { + * ... + * if (error) { + * return; <-- mutex is automatically unlocked + * } + * + * if (early_exit) { + * break; <-- leave this scope early + * } + * ... + * } + */ +#define WITH_QEMU_LOCK_GUARD(x) \ + WITH_QEMU_LOCK_GUARD_((x), qemu_lockable_auto##__COUNTER__) + +/** + * QEMU_LOCK_GUARD - Lock an object until the end of the scope + * + * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * + * This macro takes a lock until the end of the scope. Return statements + * release the lock. + * + * ... <-- mutex not locked + * QEMU_LOCK_GUARD(&mutex); <-- mutex locked from here onwards + * ... + * if (error) { + * return; <-- mutex is automatically unlocked + * } + */ +#define QEMU_LOCK_GUARD(x) \ + g_autoptr(QemuLockable) qemu_lockable_auto##__COUNTER__ = \ + qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE((x))) + #endif diff --git a/util/qemu-timer.c b/util/qemu-timer.c index ef52d28d37..d548d3c1ad 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/timer.h" +#include "qemu/lockable.h" #include "sysemu/replay.h" #include "sysemu/cpus.h" @@ -186,13 +187,12 @@ bool timerlist_expired(QEMUTimerList *timer_list) return false; } - qemu_mutex_lock(&timer_list->active_timers_lock); - if (!timer_list->active_timers) { - qemu_mutex_unlock(&timer_list->active_timers_lock); - return false; + WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { + if (!timer_list->active_timers) { + return false; + } + expire_time = timer_list->active_timers->expire_time; } - expire_time = timer_list->active_timers->expire_time; - qemu_mutex_unlock(&timer_list->active_timers_lock); return expire_time <= qemu_clock_get_ns(timer_list->clock->type); } @@ -225,13 +225,12 @@ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) * value but ->notify_cb() is called when the deadline changes. Therefore * the caller should notice the change and there is no race condition. */ - qemu_mutex_lock(&timer_list->active_timers_lock); - if (!timer_list->active_timers) { - qemu_mutex_unlock(&timer_list->active_timers_lock); - return -1; + WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { + if (!timer_list->active_timers) { + return -1; + } + expire_time = timer_list->active_timers->expire_time; } - expire_time = timer_list->active_timers->expire_time; - qemu_mutex_unlock(&timer_list->active_timers_lock); delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); -- cgit v1.2.3-55-g7522 From ac90871cf8030753a7bcef26fa1662c4e3c90078 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 16 Mar 2020 11:09:57 +0000 Subject: lockable: add QemuRecMutex support The polymorphic locking macros don't support QemuRecMutex yet. Add it so that lock guards can be used with QemuRecMutex. Convert TCG plugins functions that benefit from these macros. Manual qemu_rec_mutex_lock/unlock() callers are left unmodified in cases where clarity would not improve by switching to the macros. Signed-off-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini --- include/qemu/lockable.h | 6 ++++-- plugins/core.c | 7 +++---- plugins/loader.c | 16 ++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 90342bab98..1aeb2cb1a6 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -50,6 +50,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) #define QEMU_LOCK_FUNC(x) ((QemuLockUnlockFunc *) \ QEMU_GENERIC(x, \ (QemuMutex *, qemu_mutex_lock), \ + (QemuRecMutex *, qemu_rec_mutex_lock), \ (CoMutex *, qemu_co_mutex_lock), \ (QemuSpin *, qemu_spin_lock), \ unknown_lock_type)) @@ -57,6 +58,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) #define QEMU_UNLOCK_FUNC(x) ((QemuLockUnlockFunc *) \ QEMU_GENERIC(x, \ (QemuMutex *, qemu_mutex_unlock), \ + (QemuRecMutex *, qemu_rec_mutex_unlock), \ (CoMutex *, qemu_co_mutex_unlock), \ (QemuSpin *, qemu_spin_unlock), \ unknown_lock_type)) @@ -73,7 +75,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) /* QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable * - * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin). * * Returns a QemuLockable object that can be passed around * to a function that can operate with locks of any kind, or @@ -86,7 +88,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) /* QEMU_MAKE_LOCKABLE_NONNULL - Make a polymorphic QemuLockable * - * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin). * * Returns a QemuLockable object that can be passed around * to a function that can operate with locks of any kind. diff --git a/plugins/core.c b/plugins/core.c index ed863011ba..51bfc94787 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -15,6 +15,7 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qapi/error.h" +#include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/rcu_queue.h" #include "qemu/xxhash.h" @@ -150,11 +151,11 @@ do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, { struct qemu_plugin_ctx *ctx; - qemu_rec_mutex_lock(&plugin.lock); + QEMU_LOCK_GUARD(&plugin.lock); ctx = plugin_id_to_ctx_locked(id); /* if the plugin is on its way out, ignore this request */ if (unlikely(ctx->uninstalling)) { - goto out_unlock; + return; } if (func) { struct qemu_plugin_cb *cb = ctx->callbacks[ev]; @@ -178,8 +179,6 @@ do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, } else { plugin_unregister_cb__locked(ctx, ev); } - out_unlock: - qemu_rec_mutex_unlock(&plugin.lock); } void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, diff --git a/plugins/loader.c b/plugins/loader.c index 15fc7e5515..685d334e1a 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -19,6 +19,7 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qapi/error.h" +#include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/rcu_queue.h" #include "qemu/qht.h" @@ -367,15 +368,14 @@ void plugin_reset_uninstall(qemu_plugin_id_t id, struct qemu_plugin_reset_data *data; struct qemu_plugin_ctx *ctx; - qemu_rec_mutex_lock(&plugin.lock); - ctx = plugin_id_to_ctx_locked(id); - if (ctx->uninstalling || (reset && ctx->resetting)) { - qemu_rec_mutex_unlock(&plugin.lock); - return; + WITH_QEMU_LOCK_GUARD(&plugin.lock) { + ctx = plugin_id_to_ctx_locked(id); + if (ctx->uninstalling || (reset && ctx->resetting)) { + return; + } + ctx->resetting = reset; + ctx->uninstalling = !reset; } - ctx->resetting = reset; - ctx->uninstalling = !reset; - qemu_rec_mutex_unlock(&plugin.lock); data = g_new(struct qemu_plugin_reset_data, 1); data->ctx = ctx; -- cgit v1.2.3-55-g7522