VMware mks-guest-stats mechanism allows the collection of performance stats from guest userland GL contexts, as well as from vmwgfx kernelspace, via a set of sw- defined performance counters. The userspace performance counters are (de)registerd with vmware-vmx-stats hypervisor via new iocts. The vmwgfx kernelspace counters are controlled at build-time via a new config DRM_VMWGFX_MKSSTATS. * Add vmw_mksstat_{add|remove|reset}_ioctl controlling the tracking of mks-guest-stats in guest winsys contexts * Add DRM_VMWGFX_MKSSTATS config to drivers/gpu/drm/vmwgfx/Kconfig controlling the instrumentation of vmwgfx for kernelspace mks-guest-stats counters * Instrument vmwgfx vmw_execbuf_ioctl to collect mks-guest-stats according to DRM_VMWGFX_MKSSTATS
Unified · Split
| @@ -22,3 +22,10 @@ config DRM_VMWGFX_FBCON · 21 unmodified lines | |||
| 22 | Choose this option if you are shipping a new vmwgfx | 22 | Choose this option if you are shipping a new vmwgfx |
| 23 | userspace driver that supports using the kernel driver. | 23 | userspace driver that supports using the kernel driver. |
| 24 | 24 | ||
| 25 | config DRM_VMWGFX_MKSSTATS | ||
| 26 | bool "Enable mksGuestStats instrumentation of vmwgfx by default" | ||
| 27 | depends on DRM_VMWGFX | ||
| 28 | default n | ||
| 29 | help | ||
| 30 | Choose this option to instrument the kernel driver for mksGuestStats. | ||
| 31 | |||
| @@ -23,9 +23,11 @@ · 22 unmodified lines | |||
| 23 | * SOFTWARE. | 23 | * SOFTWARE. |
| 24 | * | 24 | * |
| 25 | **********************************************************/ | 25 | **********************************************************/ |
| 26 | #ifndef _VM_BASIC_TYPES_H_ | 26 | #ifndef _SVGA_TYPES_H_ |
| 27 | #define _VM_BASIC_TYPES_H_ | 27 | #define _SVGA_TYPES_H_ |
| 28 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
| 29 | #include <linux/mm.h> | ||
| 30 | #include <asm/page.h> | ||
| 29 | 31 | ||
| 30 | typedef u32 uint32; | 32 | typedef u32 uint32; |
| 31 | typedef s32 int32; | 33 | typedef s32 int32; |
| @@ -48,4 +50,90 @@ typedef bool Bool; · 16 unmodified lines | |||
| 48 | 50 | ||
| 49 | #define CONST64U(x) x##ULL | 51 | #define CONST64U(x) x##ULL |
| 50 | 52 | ||
| 53 | /* | ||
| 54 | * MKS Guest Stats types | ||
| 55 | */ | ||
| 56 | |||
| 57 | typedef struct MKSGuestStatCounter { | ||
| 58 | atomic64_t count; | ||
| 59 | } MKSGuestStatCounter; | ||
| 60 | |||
| 61 | typedef struct MKSGuestStatCounterTime { | ||
| 62 | MKSGuestStatCounter counter; | ||
| 63 | atomic64_t selfCycles; | ||
| 64 | atomic64_t totalCycles; | ||
| 65 | } MKSGuestStatCounterTime; | ||
| 66 | |||
| 67 | /* | ||
| 68 | * Flags for MKSGuestStatInfoEntry::flags below | ||
| 69 | */ | ||
| 70 | |||
| 71 | #define MKS_GUEST_STAT_FLAG_NONE 0 | ||
| 72 | #define MKS_GUEST_STAT_FLAG_TIME (1U << 0) | ||
| 73 | |||
| 74 | typedef __attribute__((aligned(32))) struct MKSGuestStatInfoEntry { | ||
| 75 | union { | ||
| 76 | const char *s; | ||
| 77 | uint64 u; | ||
| 78 | } name; | ||
| 79 | union { | ||
| 80 | const char *s; | ||
| 81 | uint64 u; | ||
| 82 | } description; | ||
| 83 | uint64 flags; | ||
| 84 | union { | ||
| 85 | MKSGuestStatCounter *counter; | ||
| 86 | MKSGuestStatCounterTime *counterTime; | ||
| 87 | uint64 u; | ||
| 88 | } stat; | ||
| 89 | } MKSGuestStatInfoEntry; | ||
| 90 | |||
| 91 | #define INVALID_PPN64 ((PPN64)0x000fffffffffffffULL) | ||
| 92 | #define vmw_num_pages(size) (PAGE_ALIGN(size) >> PAGE_SHIFT) | ||
| 93 | |||
| 94 | #define MKS_GUEST_STAT_INSTANCE_DESC_LENGTH 1024 | ||
| 95 | #define MKS_GUEST_STAT_INSTANCE_MAX_STATS 4096 | ||
| 96 | #define MKS_GUEST_STAT_INSTANCE_MAX_STAT_PPNS \ | ||
| 97 | (vmw_num_pages(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \ | ||
| 98 | sizeof(MKSGuestStatCounterTime))) | ||
| 99 | #define MKS_GUEST_STAT_INSTANCE_MAX_INFO_PPNS \ | ||
| 100 | (vmw_num_pages(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \ | ||
| 101 | sizeof(MKSGuestStatInfoEntry))) | ||
| 102 | #define MKS_GUEST_STAT_AVERAGE_NAME_LENGTH 40 | ||
| 103 | #define MKS_GUEST_STAT_INSTANCE_MAX_STRS_PPNS \ | ||
| 104 | (vmw_num_pages(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \ | ||
| 105 | MKS_GUEST_STAT_AVERAGE_NAME_LENGTH)) | ||
| 106 | |||
| 107 | /* | ||
| 108 | * The MKSGuestStatInstanceDescriptor is used as main interface to | ||
| 109 | * communicate guest stats back to the host code. The guest must | ||
| 110 | * allocate an instance of this structure at the start of a page and | ||
| 111 | * provide the physical address to the host. From there the host code | ||
| 112 | * can walk this structure to find other (pinned) pages containing the | ||
| 113 | * stats data. | ||
| 114 | * | ||
| 115 | * Since the MKSGuestStatInfoEntry structures contain userlevel | ||
| 116 | * pointers, the InstanceDescriptor also contains pointers to the | ||
| 117 | * begining of these sections allowing the host side code to correctly | ||
| 118 | * interpret the pointers. | ||
| 119 | * | ||
| 120 | * Because the host side code never acknowledges anything back to the | ||
| 121 | * guest there is no strict requirement to maintain compatability | ||
| 122 | * across releases. If the interface changes the host might not be | ||
| 123 | * able to log stats, but the guest will continue to run normally. | ||
| 124 | */ | ||
| 125 | |||
| 126 | typedef struct MKSGuestStatInstanceDescriptor { | ||
| 127 | uint64 reservedMBZ; /* must be zero for now. */ | ||
| 128 | uint64 statStartVA; /* VA of the start of the stats section. */ | ||
| 129 | uint64 strsStartVA; /* VA of the start of the strings section. */ | ||
| 130 | uint64 statLength; /* length of the stats section in bytes. */ | ||
| 131 | uint64 infoLength; /* length of the info entry section in bytes. */ | ||
| 132 | uint64 strsLength; /* length of the strings section in bytes. */ | ||
| 133 | PPN64 statPPNs[MKS_GUEST_STAT_INSTANCE_MAX_STAT_PPNS]; /* stat counters */ | ||
| 134 | PPN64 infoPPNs[MKS_GUEST_STAT_INSTANCE_MAX_INFO_PPNS]; /* stat info */ | ||
| 135 | PPN64 strsPPNs[MKS_GUEST_STAT_INSTANCE_MAX_STRS_PPNS]; /* strings */ | ||
| 136 | char description[MKS_GUEST_STAT_INSTANCE_DESC_LENGTH]; | ||
| 137 | } MKSGuestStatInstanceDescriptor; | ||
| 138 | |||
| 51 | #endif | 139 | #endif |
| @@ -1,22 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef _VM_BASIC_TYPES_H_ | ||
| 3 | #define _VM_BASIC_TYPES_H_ | ||
| 4 | #include <linux/kernel.h> | ||
| 5 | |||
| 6 | typedef u32 uint32; | ||
| 7 | typedef s32 int32; | ||
| 8 | typedef u64 uint64; | ||
| 9 | typedef u16 uint16; | ||
| 10 | typedef s16 int16; | ||
| 11 | typedef u8 uint8; | ||
| 12 | typedef s8 int8; | ||
| 13 | |||
| 14 | typedef uint64 PA; | ||
| 15 | typedef uint32 PPN; | ||
| 16 | typedef uint64 PPN64; | ||
| 17 | |||
| 18 | typedef bool Bool; | ||
| 19 | |||
| 20 | #define MAX_UINT32 U32_MAX | ||
| 21 | |||
| 22 | #endif | ||
| @@ -43,6 +43,7 @@ · 42 unmodified lines | |||
| 43 | #include "vmwgfx_binding.h" | 43 | #include "vmwgfx_binding.h" |
| 44 | #include "vmwgfx_devcaps.h" | 44 | #include "vmwgfx_devcaps.h" |
| 45 | #include "vmwgfx_drv.h" | 45 | #include "vmwgfx_drv.h" |
| 46 | #include "vmwgfx_mksstat.h" | ||
| 46 | 47 | ||
| 47 | #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" | 48 | #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" |
| 48 | 49 | ||
| @@ -148,6 +149,14 @@ · 99 unmodified lines | |||
| 148 | #define DRM_IOCTL_VMW_MSG \ | 149 | #define DRM_IOCTL_VMW_MSG \ |
| 149 | DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_MSG, \ | 150 | DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_MSG, \ |
| 150 | struct drm_vmw_msg_arg) | 151 | struct drm_vmw_msg_arg) |
| 152 | #define DRM_IOCTL_VMW_MKSSTAT_RESET \ | ||
| 153 | DRM_IO(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_RESET) | ||
| 154 | #define DRM_IOCTL_VMW_MKSSTAT_ADD \ | ||
| 155 | DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_ADD, \ | ||
| 156 | struct drm_vmw_mksstat_add_arg) | ||
| 157 | #define DRM_IOCTL_VMW_MKSSTAT_REMOVE \ | ||
| 158 | DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_REMOVE, \ | ||
| 159 | struct drm_vmw_mksstat_remove_arg) | ||
| 151 | 160 | ||
| 152 | /* | 161 | /* |
| 153 | * The core DRM version of this macro doesn't account for | 162 | * The core DRM version of this macro doesn't account for |
| @@ -244,6 +253,15 @@ static const struct drm_ioctl_desc vmw_ioctls[] = { · 90 unmodified lines | |||
| 244 | VMW_IOCTL_DEF(VMW_MSG, | 253 | VMW_IOCTL_DEF(VMW_MSG, |
| 245 | vmw_msg_ioctl, | 254 | vmw_msg_ioctl, |
| 246 | DRM_RENDER_ALLOW), | 255 | DRM_RENDER_ALLOW), |
| 256 | VMW_IOCTL_DEF(VMW_MKSSTAT_RESET, | ||
| 257 | vmw_mksstat_reset_ioctl, | ||
| 258 | DRM_RENDER_ALLOW), | ||
| 259 | VMW_IOCTL_DEF(VMW_MKSSTAT_ADD, | ||
| 260 | vmw_mksstat_add_ioctl, | ||
| 261 | DRM_RENDER_ALLOW), | ||
| 262 | VMW_IOCTL_DEF(VMW_MKSSTAT_REMOVE, | ||
| 263 | vmw_mksstat_remove_ioctl, | ||
| 264 | DRM_RENDER_ALLOW), | ||
| 247 | }; | 265 | }; |
| 248 | 266 | ||
| 249 | static const struct pci_device_id vmw_pci_id_list[] = { | 267 | static const struct pci_device_id vmw_pci_id_list[] = { |
| @@ -1137,6 +1155,8 @@ static void vmw_driver_unload(struct drm_device *dev) · 887 unmodified lines | |||
| 1137 | for (i = vmw_res_context; i < vmw_res_max; ++i) | 1155 | for (i = vmw_res_context; i < vmw_res_max; ++i) |
| 1138 | idr_destroy(&dev_priv->res_idr[i]); | 1156 | idr_destroy(&dev_priv->res_idr[i]); |
| 1139 | 1157 | ||
| 1158 | vmw_mksstat_remove_all(dev_priv); | ||
| 1159 | |||
| 1140 | pci_release_regions(pdev); | 1160 | pci_release_regions(pdev); |
| 1141 | } | 1161 | } |
| 1142 | 1162 | ||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ | 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ |
| 2 | /************************************************************************** | 2 | /************************************************************************** |
| 3 | * | 3 | * |
| 4 | * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA | 4 | * Copyright 2009-2021 VMware, Inc., Palo Alto, CA., USA |
| 5 | * | 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a | 6 | * Permission is hereby granted, free of charge, to any person obtaining a |
| 7 | * copy of this software and associated documentation files (the | 7 | * copy of this software and associated documentation files (the |
| @@ -91,6 +91,9 @@ · 83 unmodified lines | |||
| 91 | #define VMW_RES_FENCE ttm_driver_type3 | 91 | #define VMW_RES_FENCE ttm_driver_type3 |
| 92 | #define VMW_RES_SHADER ttm_driver_type4 | 92 | #define VMW_RES_SHADER ttm_driver_type4 |
| 93 | 93 | ||
| 94 | #define MKSSTAT_CAPACITY_LOG2 5U | ||
| 95 | #define MKSSTAT_CAPACITY (1U << MKSSTAT_CAPACITY_LOG2) | ||
| 96 | |||
| 94 | struct vmw_fpriv { | 97 | struct vmw_fpriv { |
| 95 | struct ttm_object_file *tfile; | 98 | struct ttm_object_file *tfile; |
| 96 | bool gb_aware; /* user-space is guest-backed aware */ | 99 | bool gb_aware; /* user-space is guest-backed aware */ |
| @@ -630,6 +633,18 @@ struct vmw_private { · 533 unmodified lines | |||
| 630 | struct vmw_validation_mem vvm; | 633 | struct vmw_validation_mem vvm; |
| 631 | 634 | ||
| 632 | uint32 *devcaps; | 635 | uint32 *devcaps; |
| 636 | |||
| 637 | /* | ||
| 638 | * mksGuestStat instance-descriptor and pid arrays | ||
| 639 | */ | ||
| 640 | struct page *mksstat_user_pages[MKSSTAT_CAPACITY]; | ||
| 641 | atomic_t mksstat_user_pids[MKSSTAT_CAPACITY]; | ||
| 642 | |||
| 643 | #if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) | ||
| 644 | struct page *mksstat_kern_pages[MKSSTAT_CAPACITY]; | ||
| 645 | u8 mksstat_kern_top_timer[MKSSTAT_CAPACITY]; | ||
| 646 | atomic_t mksstat_kern_pids[MKSSTAT_CAPACITY]; | ||
| 647 | #endif | ||
| 633 | }; | 648 | }; |
| 634 | 649 | ||
| 635 | static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) | 650 | static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) |
| @@ -1503,6 +1518,17 @@ __printf(1, 2) int vmw_host_printf(const char *fmt, ...); · 867 unmodified lines | |||
| 1503 | int vmw_msg_ioctl(struct drm_device *dev, void *data, | 1518 | int vmw_msg_ioctl(struct drm_device *dev, void *data, |
| 1504 | struct drm_file *file_priv); | 1519 | struct drm_file *file_priv); |
| 1505 | 1520 | ||
| 1521 | /* Host mksGuestStats -vmwgfx_msg.c: */ | ||
| 1522 | int vmw_mksstat_get_kern_slot(pid_t pid, struct vmw_private *dev_priv); | ||
| 1523 | |||
| 1524 | int vmw_mksstat_reset_ioctl(struct drm_device *dev, void *data, | ||
| 1525 | struct drm_file *file_priv); | ||
| 1526 | int vmw_mksstat_add_ioctl(struct drm_device *dev, void *data, | ||
| 1527 | struct drm_file *file_priv); | ||
| 1528 | int vmw_mksstat_remove_ioctl(struct drm_device *dev, void *data, | ||
| 1529 | struct drm_file *file_priv); | ||
| 1530 | int vmw_mksstat_remove_all(struct vmw_private *dev_priv); | ||
| 1531 | |||
| 1506 | /* VMW logging */ | 1532 | /* VMW logging */ |
| 1507 | 1533 | ||
| 1508 | /** | 1534 | /** |
| @@ -32,6 +32,7 @@ · 31 unmodified lines | |||
| 32 | #include <drm/ttm/ttm_placement.h> | 32 | #include <drm/ttm/ttm_placement.h> |
| 33 | #include "vmwgfx_so.h" | 33 | #include "vmwgfx_so.h" |
| 34 | #include "vmwgfx_binding.h" | 34 | #include "vmwgfx_binding.h" |
| 35 | #include "vmwgfx_mksstat.h" | ||
| 35 | 36 | ||
| 36 | #define VMW_RES_HT_ORDER 12 | 37 | #define VMW_RES_HT_ORDER 12 |
| 37 | 38 | ||
| @@ -4406,6 +4407,9 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, · 4368 unmodified lines | |||
| 4406 | int ret; | 4407 | int ret; |
| 4407 | struct dma_fence *in_fence = NULL; | 4408 | struct dma_fence *in_fence = NULL; |
| 4408 | 4409 | ||
| 4410 | MKS_STAT_TIME_DECL(MKSSTAT_KERN_EXECBUF); | ||
| 4411 | MKS_STAT_TIME_PUSH(MKSSTAT_KERN_EXECBUF); | ||
| 4412 | |||
| 4409 | /* | 4413 | /* |
| 4410 | * Extend the ioctl argument while maintaining backwards compatibility: | 4414 | * Extend the ioctl argument while maintaining backwards compatibility: |
| 4411 | * We take different code paths depending on the value of arg->version. | 4415 | * We take different code paths depending on the value of arg->version. |
| @@ -4415,7 +4419,8 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, · 3 unmodified lines | |||
| 4415 | if (unlikely(arg->version > DRM_VMW_EXECBUF_VERSION || | 4419 | if (unlikely(arg->version > DRM_VMW_EXECBUF_VERSION || |
| 4416 | arg->version == 0)) { | 4420 | arg->version == 0)) { |
| 4417 | VMW_DEBUG_USER("Incorrect execbuf version.\n"); | 4421 | VMW_DEBUG_USER("Incorrect execbuf version.\n"); |
| 4418 | return -EINVAL; | 4422 | ret = -EINVAL; |
| 4423 | goto mksstats_out; | ||
| 4419 | } | 4424 | } |
| 4420 | 4425 | ||
| 4421 | switch (arg->version) { | 4426 | switch (arg->version) { |
| @@ -4435,7 +4440,8 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, · 13 unmodified lines | |||
| 4435 | 4440 | ||
| 4436 | if (!in_fence) { | 4441 | if (!in_fence) { |
| 4437 | VMW_DEBUG_USER("Cannot get imported fence\n"); | 4442 | VMW_DEBUG_USER("Cannot get imported fence\n"); |
| 4438 | return -EINVAL; | 4443 | ret = -EINVAL; |
| 4444 | goto mksstats_out; | ||
| 4439 | } | 4445 | } |
| 4440 | 4446 | ||
| 4441 | ret = vmw_wait_dma_fence(dev_priv->fman, in_fence); | 4447 | ret = vmw_wait_dma_fence(dev_priv->fman, in_fence); |
| @@ -4458,5 +4464,8 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, · 16 unmodified lines | |||
| 4458 | out: | 4464 | out: |
| 4459 | if (in_fence) | 4465 | if (in_fence) |
| 4460 | dma_fence_put(in_fence); | 4466 | dma_fence_put(in_fence); |
| 4467 | |||
| 4468 | mksstats_out: | ||
| 4469 | MKS_STAT_TIME_POP(MKSSTAT_KERN_EXECBUF); | ||
| 4461 | return ret; | 4470 | return ret; |
| 4462 | } | 4471 | } |
| @@ -0,0 +1,144 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ | ||
| 2 | /************************************************************************** | ||
| 3 | * | ||
| 4 | * Copyright 2021 VMware, Inc., Palo Alto, CA., USA | ||
| 5 | * | ||
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
| 7 | * copy of this software and associated documentation files (the | ||
| 8 | * "Software"), to deal in the Software without restriction, including | ||
| 9 | * without limitation the rights to use, copy, modify, merge, publish, | ||
| 10 | * distribute, sub license, and/or sell copies of the Software, and to | ||
| 11 | * permit persons to whom the Software is furnished to do so, subject to | ||
| 12 | * the following conditions: | ||
| 13 | * | ||
| 14 | * The above copyright notice and this permission notice (including the | ||
| 15 | * next paragraph) shall be included in all copies or substantial portions | ||
| 16 | * of the Software. | ||
| 17 | * | ||
| 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
| 21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
| 22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
| 23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
| 24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 25 | * | ||
| 26 | **************************************************************************/ | ||
| 27 | |||
| 28 | #ifndef _VMWGFX_MKSSTAT_H_ | ||
| 29 | #define _VMWGFX_MKSSTAT_H_ | ||
| 30 | |||
| 31 | #include <asm/page.h> | ||
| 32 | |||
| 33 | /* Reservation marker for mksstat pid's */ | ||
| 34 | #define MKSSTAT_PID_RESERVED -1 | ||
| 35 | |||
| 36 | #if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) | ||
| 37 | /* | ||
| 38 | * Kernel-internal mksGuestStat counters. The order of this enum dictates the | ||
| 39 | * order of instantiation of these counters in the mksGuestStat pages. | ||
| 40 | */ | ||
| 41 | |||
| 42 | typedef enum { | ||
| 43 | MKSSTAT_KERN_EXECBUF, /* vmw_execbuf_ioctl */ | ||
| 44 | |||
| 45 | MKSSTAT_KERN_COUNT /* Reserved entry; always last */ | ||
| 46 | } mksstat_kern_stats_t; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * vmw_mksstat_get_kern_pstat: Computes the address of the MKSGuestStatCounterTime | ||
| 50 | * array from the address of the base page. | ||
| 51 | * | ||
| 52 | * @page_addr: Pointer to the base page. | ||
| 53 | * Return: Pointer to the MKSGuestStatCounterTime array. | ||
| 54 | */ | ||
| 55 | |||
| 56 | static inline void *vmw_mksstat_get_kern_pstat(void *page_addr) | ||
| 57 | { | ||
| 58 | return page_addr + PAGE_SIZE * 1; | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * vmw_mksstat_get_kern_pinfo: Computes the address of the MKSGuestStatInfoEntry | ||
| 63 | * array from the address of the base page. | ||
| 64 | * | ||
| 65 | * @page_addr: Pointer to the base page. | ||
| 66 | * Return: Pointer to the MKSGuestStatInfoEntry array. | ||
| 67 | */ | ||
| 68 | |||
| 69 | static inline void *vmw_mksstat_get_kern_pinfo(void *page_addr) | ||
| 70 | { | ||
| 71 | return page_addr + PAGE_SIZE * 2; | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * vmw_mksstat_get_kern_pstrs: Computes the address of the mksGuestStat strings | ||
| 76 | * sequence from the address of the base page. | ||
| 77 | * | ||
| 78 | * @page_addr: Pointer to the base page. | ||
| 79 | * Return: Pointer to the mksGuestStat strings sequence. | ||
| 80 | */ | ||
| 81 | |||
| 82 | static inline void *vmw_mksstat_get_kern_pstrs(void *page_addr) | ||
| 83 | { | ||
| 84 | return page_addr + PAGE_SIZE * 3; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* | ||
| 88 | * MKS_STAT_TIME_DECL/PUSH/POP macros to be used in timer-counted routines. | ||
| 89 | */ | ||
| 90 | |||
| 91 | struct mksstat_timer_t { | ||
| 92 | /* mutable */ mksstat_kern_stats_t old_top; | ||
| 93 | const u64 t0; | ||
| 94 | const int slot; | ||
| 95 | }; | ||
| 96 | |||
| 97 | #define MKS_STAT_TIME_DECL(kern_cntr) \ | ||
| 98 | struct mksstat_timer_t _##kern_cntr = { \ | ||
| 99 | .t0 = rdtsc(), \ | ||
| 100 | .slot = vmw_mksstat_get_kern_slot(current->pid, dev_priv) \ | ||
| 101 | } | ||
| 102 | |||
| 103 | #define MKS_STAT_TIME_PUSH(kern_cntr) \ | ||
| 104 | do { \ | ||
| 105 | if (_##kern_cntr.slot >= 0) { \ | ||
| 106 | _##kern_cntr.old_top = dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot]; \ | ||
| 107 | dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot] = kern_cntr; \ | ||
| 108 | } \ | ||
| 109 | } while (0) | ||
| 110 | |||
| 111 | #define MKS_STAT_TIME_POP(kern_cntr) \ | ||
| 112 | do { \ | ||
| 113 | if (_##kern_cntr.slot >= 0) { \ | ||
| 114 | const pid_t pid = atomic_cmpxchg(&dev_priv->mksstat_kern_pids[_##kern_cntr.slot], current->pid, MKSSTAT_PID_RESERVED); \ | ||
| 115 | dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot] = _##kern_cntr.old_top; \ | ||
| 116 | \ | ||
| 117 | if (pid == current->pid) { \ | ||
| 118 | const u64 dt = rdtsc() - _##kern_cntr.t0; \ | ||
| 119 | MKSGuestStatCounterTime *pstat; \ | ||
| 120 | \ | ||
| 121 | BUG_ON(!dev_priv->mksstat_kern_pages[_##kern_cntr.slot]); \ | ||
| 122 | \ | ||
| 123 | pstat = vmw_mksstat_get_kern_pstat(page_address(dev_priv->mksstat_kern_pages[_##kern_cntr.slot])); \ | ||
| 124 | \ | ||
| 125 | atomic64_inc(&pstat[kern_cntr].counter.count); \ | ||
| 126 | atomic64_add(dt, &pstat[kern_cntr].selfCycles); \ | ||
| 127 | atomic64_add(dt, &pstat[kern_cntr].totalCycles); \ | ||
| 128 | \ | ||
| 129 | if (_##kern_cntr.old_top != MKSSTAT_KERN_COUNT) \ | ||
| 130 | atomic64_sub(dt, &pstat[_##kern_cntr.old_top].selfCycles); \ | ||
| 131 | \ | ||
| 132 | atomic_set(&dev_priv->mksstat_kern_pids[_##kern_cntr.slot], current->pid); \ | ||
| 133 | } \ | ||
| 134 | } \ | ||
| 135 | } while (0) | ||
| 136 | |||
| 137 | #else | ||
| 138 | #define MKS_STAT_TIME_DECL(kern_cntr) | ||
| 139 | #define MKS_STAT_TIME_PUSH(kern_cntr) | ||
| 140 | #define MKS_STAT_TIME_POP(kern_cntr) | ||
| 141 | |||
| 142 | #endif /* IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS */ | ||
| 143 | |||
| 144 | #endif | ||
| @@ -31,10 +31,12 @@ · 30 unmodified lines | |||
| 31 | #include <linux/mem_encrypt.h> | 31 | #include <linux/mem_encrypt.h> |
| 32 | 32 | ||
| 33 | #include <asm/hypervisor.h> | 33 | #include <asm/hypervisor.h> |
| 34 | #include <drm/drm_ioctl.h> | ||
| 34 | 35 | ||
| 35 | #include "vmwgfx_drv.h" | 36 | #include "vmwgfx_drv.h" |
| 36 | #include "vmwgfx_msg_x86.h" | 37 | #include "vmwgfx_msg_x86.h" |
| 37 | #include "vmwgfx_msg_arm64.h" | 38 | #include "vmwgfx_msg_arm64.h" |
| 39 | #include "vmwgfx_mksstat.h" | ||
| 38 | 40 | ||
| 39 | #define MESSAGE_STATUS_SUCCESS 0x0001 | 41 | #define MESSAGE_STATUS_SUCCESS 0x0001 |
| 40 | #define MESSAGE_STATUS_DORECV 0x0002 | 42 | #define MESSAGE_STATUS_DORECV 0x0002 |
| @@ -56,6 +58,11 @@ · 15 unmodified lines | |||
| 56 | #define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG) | 58 | #define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG) |
| 57 | #define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG) | 59 | #define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG) |
| 58 | 60 | ||
| 61 | #define VMW_PORT_CMD_MKS_GUEST_STATS 85 | ||
| 62 | #define VMW_PORT_CMD_MKSGS_RESET (0 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS) | ||
| 63 | #define VMW_PORT_CMD_MKSGS_ADD_PPN (1 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS) | ||
| 64 | #define VMW_PORT_CMD_MKSGS_REMOVE_PPN (2 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS) | ||
| 65 | |||
| 59 | #define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16) | 66 | #define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16) |
| 60 | 67 | ||
| 61 | #define MAX_USER_MSG_LENGTH PAGE_SIZE | 68 | #define MAX_USER_MSG_LENGTH PAGE_SIZE |
| @@ -612,3 +619,575 @@ out_open: · 550 unmodified lines | |||
| 612 | 619 | ||
| 613 | return -EINVAL; | 620 | return -EINVAL; |
| 614 | } | 621 | } |
| 622 | |||
| 623 | /** | ||
| 624 | * reset_ppn_array: Resets a PPN64 array to INVALID_PPN64 content | ||
| 625 | * | ||
| 626 | * @arr: Array to reset. | ||
| 627 | * @size: Array length. | ||
| 628 | */ | ||
| 629 | static inline void reset_ppn_array(PPN64 *arr, size_t size) | ||
| 630 | { | ||
| 631 | size_t i; | ||
| 632 | |||
| 633 | BUG_ON(!arr || size == 0); | ||
| 634 | |||
| 635 | for (i = 0; i < size; ++i) | ||
| 636 | arr[i] = INVALID_PPN64; | ||
| 637 | } | ||
| 638 | |||
| 639 | /** | ||
| 640 | * hypervisor_ppn_reset_all: Removes all mksGuestStat instance descriptors from | ||
| 641 | * the hypervisor. All related pages should be subsequently unpinned or freed. | ||
| 642 | * | ||
| 643 | */ | ||
| 644 | static inline void hypervisor_ppn_reset_all(void) | ||
| 645 | { | ||
| 646 | unsigned long eax, ebx, ecx, edx, si = 0, di = 0; | ||
| 647 | |||
| 648 | VMW_PORT(VMW_PORT_CMD_MKSGS_RESET, | ||
| 649 | 0, si, di, | ||
| 650 | 0, | ||
| 651 | VMW_HYPERVISOR_MAGIC, | ||
| 652 | eax, ebx, ecx, edx, si, di); | ||
| 653 | } | ||
| 654 | |||
| 655 | /** | ||
| 656 | * hypervisor_ppn_add: Adds a single mksGuestStat instance descriptor to the | ||
| 657 | * hypervisor. Any related userspace pages should be pinned in advance. | ||
| 658 | * | ||
| 659 | * @pfn: Physical page number of the instance descriptor | ||
| 660 | */ | ||
| 661 | static inline void hypervisor_ppn_add(PPN64 pfn) | ||
| 662 | { | ||
| 663 | unsigned long eax, ebx, ecx, edx, si = 0, di = 0; | ||
| 664 | |||
| 665 | VMW_PORT(VMW_PORT_CMD_MKSGS_ADD_PPN, | ||
| 666 | pfn, si, di, | ||
| 667 | 0, | ||
| 668 | VMW_HYPERVISOR_MAGIC, | ||
| 669 | eax, ebx, ecx, edx, si, di); | ||
| 670 | } | ||
| 671 | |||
| 672 | /** | ||
| 673 | * hypervisor_ppn_remove: Removes a single mksGuestStat instance descriptor from | ||
| 674 | * the hypervisor. All related pages should be subsequently unpinned or freed. | ||
| 675 | * | ||
| 676 | * @pfn: Physical page number of the instance descriptor | ||
| 677 | */ | ||
| 678 | static inline void hypervisor_ppn_remove(PPN64 pfn) | ||
| 679 | { | ||
| 680 | unsigned long eax, ebx, ecx, edx, si = 0, di = 0; | ||
| 681 | |||
| 682 | VMW_PORT(VMW_PORT_CMD_MKSGS_REMOVE_PPN, | ||
| 683 | pfn, si, di, | ||
| 684 | 0, | ||
| 685 | VMW_HYPERVISOR_MAGIC, | ||
| 686 | eax, ebx, ecx, edx, si, di); | ||
| 687 | } | ||
| 688 | |||
| 689 | #if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) | ||
| 690 | |||
| 691 | /* Order of the total number of pages used for kernel-internal mksGuestStat; at least 2 */ | ||
| 692 | #define MKSSTAT_KERNEL_PAGES_ORDER 2 | ||
| 693 | /* Header to the text description of mksGuestStat instance descriptor */ | ||
| 694 | #define MKSSTAT_KERNEL_DESCRIPTION "vmwgfx" | ||
| 695 | |||
| 696 | /* Kernel mksGuestStats counter names and desciptions; same order as enum mksstat_kern_stats_t */ | ||
| 697 | static const char* const mksstat_kern_name_desc[MKSSTAT_KERN_COUNT][2] = | ||
| 698 | { | ||
| 699 | { "vmw_execbuf_ioctl", "vmw_execbuf_ioctl" }, | ||
| 700 | }; | ||
| 701 | |||
| 702 | /** | ||
| 703 | * mksstat_init_record: Initializes an MKSGuestStatCounter-based record | ||
| 704 | * for the respective mksGuestStat index. | ||
| 705 | * | ||
| 706 | * @stat_idx: Index of the MKSGuestStatCounter-based mksGuestStat record. | ||
| 707 | * @pstat: Pointer to array of MKSGuestStatCounterTime. | ||
| 708 | * @pinfo: Pointer to array of MKSGuestStatInfoEntry. | ||
| 709 | * @pstrs: Pointer to current end of the name/description sequence. | ||
| 710 | * Return: Pointer to the new end of the names/description sequence. | ||
| 711 | */ | ||
| 712 | |||
| 713 | static inline char *mksstat_init_record(mksstat_kern_stats_t stat_idx, | ||
| 714 | MKSGuestStatCounterTime *pstat, MKSGuestStatInfoEntry *pinfo, char *pstrs) | ||
| 715 | { | ||
| 716 | char *const pstrd = pstrs + strlen(mksstat_kern_name_desc[stat_idx][0]) + 1; | ||
| 717 | strcpy(pstrs, mksstat_kern_name_desc[stat_idx][0]); | ||
| 718 | strcpy(pstrd, mksstat_kern_name_desc[stat_idx][1]); | ||
| 719 | |||
| 720 | pinfo[stat_idx].name.s = pstrs; | ||
| 721 | pinfo[stat_idx].description.s = pstrd; | ||
| 722 | pinfo[stat_idx].flags = MKS_GUEST_STAT_FLAG_NONE; | ||
| 723 | pinfo[stat_idx].stat.counter = (MKSGuestStatCounter *)&pstat[stat_idx]; | ||
| 724 | |||
| 725 | return pstrd + strlen(mksstat_kern_name_desc[stat_idx][1]) + 1; | ||
| 726 | } | ||
| 727 | |||
| 728 | /** | ||
| 729 | * mksstat_init_record_time: Initializes an MKSGuestStatCounterTime-based record | ||
| 730 | * for the respective mksGuestStat index. | ||
| 731 | * | ||
| 732 | * @stat_idx: Index of the MKSGuestStatCounterTime-based mksGuestStat record. | ||
| 733 | * @pstat: Pointer to array of MKSGuestStatCounterTime. | ||
| 734 | * @pinfo: Pointer to array of MKSGuestStatInfoEntry. | ||
| 735 | * @pstrs: Pointer to current end of the name/description sequence. | ||
| 736 | * Return: Pointer to the new end of the names/description sequence. | ||
| 737 | */ | ||
| 738 | |||
| 739 | static inline char *mksstat_init_record_time(mksstat_kern_stats_t stat_idx, | ||
| 740 | MKSGuestStatCounterTime *pstat, MKSGuestStatInfoEntry *pinfo, char *pstrs) | ||
| 741 | { | ||
| 742 | char *const pstrd = pstrs + strlen(mksstat_kern_name_desc[stat_idx][0]) + 1; | ||
| 743 | strcpy(pstrs, mksstat_kern_name_desc[stat_idx][0]); | ||
| 744 | strcpy(pstrd, mksstat_kern_name_desc[stat_idx][1]); | ||
| 745 | |||
| 746 | pinfo[stat_idx].name.s = pstrs; | ||
| 747 | pinfo[stat_idx].description.s = pstrd; | ||
| 748 | pinfo[stat_idx].flags = MKS_GUEST_STAT_FLAG_TIME; | ||
| 749 | pinfo[stat_idx].stat.counterTime = &pstat[stat_idx]; | ||
| 750 | |||
| 751 | return pstrd + strlen(mksstat_kern_name_desc[stat_idx][1]) + 1; | ||
| 752 | } | ||
| 753 | |||
| 754 | /** | ||
| 755 | * mksstat_init_kern_id: Creates a single mksGuestStat instance descriptor and | ||
| 756 | * kernel-internal counters. Adds PFN mapping to the hypervisor. | ||
| 757 | * | ||
| 758 | * Create a single mksGuestStat instance descriptor and corresponding structures | ||
| 759 | * for all kernel-internal counters. The corresponding PFNs are mapped with the | ||
| 760 | * hypervisor. | ||
| 761 | * | ||
| 762 | * @ppage: Output pointer to page containing the instance descriptor. | ||
| 763 | * Return: Zero on success, negative error code on error. | ||
| 764 | */ | ||
| 765 | |||
| 766 | static int mksstat_init_kern_id(struct page **ppage) | ||
| 767 | { | ||
| 768 | MKSGuestStatInstanceDescriptor *pdesc; | ||
| 769 | MKSGuestStatCounterTime *pstat; | ||
| 770 | MKSGuestStatInfoEntry *pinfo; | ||
| 771 | char *pstrs, *pstrs_acc; | ||
| 772 | |||
| 773 | /* Allocate pages for the kernel-internal instance descriptor */ | ||
| 774 | struct page *page = alloc_pages(GFP_KERNEL | __GFP_ZERO, MKSSTAT_KERNEL_PAGES_ORDER); | ||
| 775 | |||
| 776 | if (!page) | ||
| 777 | return -ENOMEM; | ||
| 778 | |||
| 779 | pdesc = page_address(page); | ||
| 780 | pstat = vmw_mksstat_get_kern_pstat(pdesc); | ||
| 781 | pinfo = vmw_mksstat_get_kern_pinfo(pdesc); | ||
| 782 | pstrs = vmw_mksstat_get_kern_pstrs(pdesc); | ||
| 783 | |||
| 784 | /* Set up all kernel-internal counters and corresponding structures */ | ||
| 785 | pstrs_acc = pstrs; | ||
| 786 | pstrs_acc = mksstat_init_record_time(MKSSTAT_KERN_EXECBUF, pstat, pinfo, pstrs_acc); | ||
| 787 | |||
| 788 | /* Add new counters above, in their order of appearance in mksstat_kern_stats_t */ | ||
| 789 | |||
| 790 | BUG_ON(pstrs_acc - pstrs > PAGE_SIZE); | ||
| 791 | |||
| 792 | /* Set up the kernel-internal instance descriptor */ | ||
| 793 | pdesc->reservedMBZ = 0; | ||
| 794 | pdesc->statStartVA = (uintptr_t)pstat; | ||
| 795 | pdesc->strsStartVA = (uintptr_t)pstrs; | ||
| 796 | pdesc->statLength = sizeof(*pstat) * MKSSTAT_KERN_COUNT; | ||
| 797 | pdesc->infoLength = sizeof(*pinfo) * MKSSTAT_KERN_COUNT; | ||
| 798 | pdesc->strsLength = pstrs_acc - pstrs; | ||
| 799 | snprintf(pdesc->description, ARRAY_SIZE(pdesc->description) - 1, "%s pid=%d", | ||
| 800 | MKSSTAT_KERNEL_DESCRIPTION, current->pid); | ||
| 801 | |||
| 802 | pdesc->statPPNs[0] = page_to_pfn(virt_to_page(pstat)); | ||
| 803 | reset_ppn_array(pdesc->statPPNs + 1, ARRAY_SIZE(pdesc->statPPNs) - 1); | ||
| 804 | |||
| 805 | pdesc->infoPPNs[0] = page_to_pfn(virt_to_page(pinfo)); | ||
| 806 | reset_ppn_array(pdesc->infoPPNs + 1, ARRAY_SIZE(pdesc->infoPPNs) - 1); | ||
| 807 | |||
| 808 | pdesc->strsPPNs[0] = page_to_pfn(virt_to_page(pstrs)); | ||
| 809 | reset_ppn_array(pdesc->strsPPNs + 1, ARRAY_SIZE(pdesc->strsPPNs) - 1); | ||
| 810 | |||
| 811 | *ppage = page; | ||
| 812 | |||
| 813 | hypervisor_ppn_add((PPN64)page_to_pfn(page)); | ||
| 814 | |||
| 815 | return 0; | ||
| 816 | } | ||
| 817 | |||
| 818 | /** | ||
| 819 | * vmw_mksstat_get_kern_slot: Acquires a slot for a single kernel-internal | ||
| 820 | * mksGuestStat instance descriptor. | ||
| 821 | * | ||
| 822 | * Find a slot for a single kernel-internal mksGuestStat instance descriptor. | ||
| 823 | * In case no such was already present, allocate a new one and set up a kernel- | ||
| 824 | * internal mksGuestStat instance descriptor for the former. | ||
| 825 | * | ||
| 826 | * @pid: Process for which a slot is sought. | ||
| 827 | * @dev_priv: Identifies the drm private device. | ||
| 828 | * Return: Non-negative slot on success, negative error code on error. | ||
| 829 | */ | ||
| 830 | |||
| 831 | int vmw_mksstat_get_kern_slot(pid_t pid, struct vmw_private *dev_priv) | ||
| 832 | { | ||
| 833 | const size_t base = (u32)hash_32(pid, MKSSTAT_CAPACITY_LOG2); | ||
| 834 | size_t i; | ||
| 835 | |||
| 836 | for (i = 0; i < ARRAY_SIZE(dev_priv->mksstat_kern_pids); ++i) { | ||
| 837 | const size_t slot = (i + base) % ARRAY_SIZE(dev_priv->mksstat_kern_pids); | ||
| 838 | |||
| 839 | /* Check if an instance descriptor for this pid is already present */ | ||
| 840 | if (pid == (pid_t)atomic_read(&dev_priv->mksstat_kern_pids[slot])) | ||
| 841 | return (int)slot; | ||
| 842 | |||
| 843 | /* Set up a new instance descriptor for this pid */ | ||
| 844 | if (!atomic_cmpxchg(&dev_priv->mksstat_kern_pids[slot], 0, MKSSTAT_PID_RESERVED)) { | ||
| 845 | const int ret = mksstat_init_kern_id(&dev_priv->mksstat_kern_pages[slot]); | ||
| 846 | |||
| 847 | if (!ret) { | ||
| 848 | /* Reset top-timer tracking for this slot */ | ||
| 849 | dev_priv->mksstat_kern_top_timer[slot] = MKSSTAT_KERN_COUNT; | ||
| 850 | |||
| 851 | atomic_set(&dev_priv->mksstat_kern_pids[slot], pid); | ||
| 852 | return (int)slot; | ||
| 853 | } | ||
| 854 | |||
| 855 | atomic_set(&dev_priv->mksstat_kern_pids[slot], 0); | ||
| 856 | return ret; | ||
| 857 | } | ||
| 858 | } | ||
| 859 | |||
| 860 | return -ENOSPC; | ||
| 861 | } | ||
| 862 | |||
| 863 | #endif | ||
| 864 | |||
| 865 | /** | ||
| 866 | * vmw_mksstat_cleanup_descriptor: Frees a single userspace-originating | ||
| 867 | * mksGuestStat instance-descriptor page and unpins all related user pages. | ||
| 868 | * | ||
| 869 | * Unpin all user pages realated to this instance descriptor and free | ||
| 870 | * the instance-descriptor page itself. | ||
| 871 | * | ||
| 872 | * @page: Page of the instance descriptor. | ||
| 873 | */ | ||
| 874 | |||
| 875 | static void vmw_mksstat_cleanup_descriptor(struct page *page) | ||
| 876 | { | ||
| 877 | MKSGuestStatInstanceDescriptor *pdesc = page_address(page); | ||
| 878 | size_t i; | ||
| 879 | |||
| 880 | for (i = 0; i < ARRAY_SIZE(pdesc->statPPNs) && pdesc->statPPNs[i] != INVALID_PPN64; ++i) | ||
| 881 | unpin_user_page(pfn_to_page(pdesc->statPPNs[i])); | ||
| 882 | |||
| 883 | for (i = 0; i < ARRAY_SIZE(pdesc->infoPPNs) && pdesc->infoPPNs[i] != INVALID_PPN64; ++i) | ||
| 884 | unpin_user_page(pfn_to_page(pdesc->infoPPNs[i])); | ||
| 885 | |||
| 886 | for (i = 0; i < ARRAY_SIZE(pdesc->strsPPNs) && pdesc->strsPPNs[i] != INVALID_PPN64; ++i) | ||
| 887 | unpin_user_page(pfn_to_page(pdesc->strsPPNs[i])); | ||
| 888 | |||
| 889 | __free_page(page); | ||
| 890 | } | ||
| 891 | |||
| 892 | /** | ||
| 893 | * vmw_mksstat_remove_all: Resets all mksGuestStat instance descriptors | ||
| 894 | * from the hypervisor. | ||
| 895 | * | ||
| 896 | * Discard all hypervisor PFN mappings, containing active mksGuestState instance | ||
| 897 | * descriptors, unpin the related userspace pages and free the related kernel pages. | ||
| 898 | * | ||
| 899 | * @dev_priv: Identifies the drm private device. | ||
| 900 | * Return: Zero on success, negative error code on error. | ||
| 901 | */ | ||
| 902 | |||
| 903 | int vmw_mksstat_remove_all(struct vmw_private *dev_priv) | ||
| 904 | { | ||
| 905 | int ret = 0; | ||
| 906 | size_t i; | ||
| 907 | |||
| 908 | /* Discard all PFN mappings with the hypervisor */ | ||
| 909 | hypervisor_ppn_reset_all(); | ||
| 910 | |||
| 911 | /* Discard all userspace-originating instance descriptors and unpin all related pages */ | ||
| 912 | for (i = 0; i < ARRAY_SIZE(dev_priv->mksstat_user_pids); ++i) { | ||
| 913 | const pid_t pid0 = (pid_t)atomic_read(&dev_priv->mksstat_user_pids[i]); | ||
| 914 | |||
| 915 | if (!pid0) | ||
| 916 | continue; | ||
| 917 | |||
| 918 | if (pid0 != MKSSTAT_PID_RESERVED) { | ||
| 919 | const pid_t pid1 = atomic_cmpxchg(&dev_priv->mksstat_user_pids[i], pid0, MKSSTAT_PID_RESERVED); | ||
| 920 | |||
| 921 | if (!pid1) | ||
| 922 | continue; | ||
| 923 | |||
| 924 | if (pid1 == pid0) { | ||
| 925 | struct page *const page = dev_priv->mksstat_user_pages[i]; | ||
| 926 | |||
| 927 | BUG_ON(!page); | ||
| 928 | |||
| 929 | dev_priv->mksstat_user_pages[i] = NULL; | ||
| 930 | atomic_set(&dev_priv->mksstat_user_pids[i], 0); | ||
| 931 | |||
| 932 | vmw_mksstat_cleanup_descriptor(page); | ||
| 933 | continue; | ||
| 934 | } | ||
| 935 | } | ||
| 936 | |||
| 937 | ret = -EAGAIN; | ||
| 938 | } | ||
| 939 | |||
| 940 | #if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) | ||
| 941 | /* Discard all kernel-internal instance descriptors and free all related pages */ | ||
| 942 | for (i = 0; i < ARRAY_SIZE(dev_priv->mksstat_kern_pids); ++i) { | ||
| 943 | const pid_t pid0 = (pid_t)atomic_read(&dev_priv->mksstat_kern_pids[i]); | ||
| 944 | |||
| 945 | if (!pid0) | ||
| 946 | continue; | ||
| 947 | |||
| 948 | if (pid0 != MKSSTAT_PID_RESERVED) { | ||
| 949 | const pid_t pid1 = atomic_cmpxchg(&dev_priv->mksstat_kern_pids[i], pid0, MKSSTAT_PID_RESERVED); | ||
| 950 | |||
| 951 | if (!pid1) | ||
| 952 | continue; | ||
| 953 | |||
| 954 | if (pid1 == pid0) { | ||
| 955 | struct page *const page = dev_priv->mksstat_kern_pages[i]; | ||
| 956 | |||
| 957 | BUG_ON(!page); | ||
| 958 | |||
| 959 | dev_priv->mksstat_kern_pages[i] = NULL; | ||
| 960 | atomic_set(&dev_priv->mksstat_kern_pids[i], 0); | ||
| 961 | |||
| 962 | __free_pages(page, MKSSTAT_KERNEL_PAGES_ORDER); | ||
| 963 | continue; | ||
| 964 | } | ||
| 965 | } | ||
| 966 | |||
| 967 | ret = -EAGAIN; | ||
| 968 | } | ||
| 969 | |||
| 970 | #endif | ||
| 971 | return ret; | ||
| 972 | } | ||
| 973 | |||
| 974 | /** | ||
| 975 | * vmw_mksstat_reset_ioctl: Resets all mksGuestStat instance descriptors | ||
| 976 | * from the hypervisor. | ||
| 977 | * | ||
| 978 | * Discard all hypervisor PFN mappings, containing active mksGuestStat instance | ||
| 979 | * descriptors, unpin the related userspace pages and free the related kernel pages. | ||
| 980 | * | ||
| 981 | * @dev: Identifies the drm device. | ||
| 982 | * @data: Pointer to the ioctl argument. | ||
| 983 | * @file_priv: Identifies the caller; unused. | ||
| 984 | * Return: Zero on success, negative error code on error. | ||
| 985 | */ | ||
| 986 | |||
| 987 | int vmw_mksstat_reset_ioctl(struct drm_device *dev, void *data, | ||
| 988 | struct drm_file *file_priv) | ||
| 989 | { | ||
| 990 | struct vmw_private *const dev_priv = vmw_priv(dev); | ||
| 991 | return vmw_mksstat_remove_all(dev_priv); | ||
| 992 | } | ||
| 993 | |||
| 994 | /** | ||
| 995 | * vmw_mksstat_add_ioctl: Creates a single userspace-originating mksGuestStat | ||
| 996 | * instance descriptor and registers that with the hypervisor. | ||
| 997 | * | ||
| 998 | * Create a hypervisor PFN mapping, containing a single mksGuestStat instance | ||
| 999 | * descriptor and pin the corresponding userspace pages. | ||
| 1000 | * | ||
| 1001 | * @dev: Identifies the drm device. | ||
| 1002 | * @data: Pointer to the ioctl argument. | ||
| 1003 | * @file_priv: Identifies the caller; unused. | ||
| 1004 | * Return: Zero on success, negative error code on error. | ||
| 1005 | */ | ||
| 1006 | |||
| 1007 | int vmw_mksstat_add_ioctl(struct drm_device *dev, void *data, | ||
| 1008 | struct drm_file *file_priv) | ||
| 1009 | { | ||
| 1010 | struct drm_vmw_mksstat_add_arg *arg = | ||
| 1011 | (struct drm_vmw_mksstat_add_arg *) data; | ||
| 1012 | |||
| 1013 | struct vmw_private *const dev_priv = vmw_priv(dev); | ||
| 1014 | |||
| 1015 | struct page *page; | ||
| 1016 | MKSGuestStatInstanceDescriptor *pdesc; | ||
| 1017 | const size_t num_pages_stat = vmw_num_pages(arg->stat_len); | ||
| 1018 | const size_t num_pages_info = vmw_num_pages(arg->info_len); | ||
| 1019 | const size_t num_pages_strs = vmw_num_pages(arg->strs_len); | ||
| 1020 | long desc_len; | ||
| 1021 | long nr_pinned_stat; | ||
| 1022 | long nr_pinned_info; | ||
| 1023 | long nr_pinned_strs; | ||
| 1024 | struct page *pages_stat[ARRAY_SIZE(pdesc->statPPNs)]; | ||
| 1025 | struct page *pages_info[ARRAY_SIZE(pdesc->infoPPNs)]; | ||
| 1026 | struct page *pages_strs[ARRAY_SIZE(pdesc->strsPPNs)]; | ||
| 1027 | size_t i, slot; | ||
| 1028 | |||
| 1029 | arg->id = -1; | ||
| 1030 | |||
| 1031 | if (!arg->stat || !arg->info || !arg->strs) | ||
| 1032 | return -EINVAL; | ||
| 1033 | |||
| 1034 | if (!arg->stat_len || !arg->info_len || !arg->strs_len) | ||
| 1035 | return -EINVAL; | ||
| 1036 | |||
| 1037 | if (!arg->description) | ||
| 1038 | return -EINVAL; | ||
| 1039 | |||
| 1040 | if (num_pages_stat > ARRAY_SIZE(pdesc->statPPNs) || | ||
| 1041 | num_pages_info > ARRAY_SIZE(pdesc->infoPPNs) || | ||
| 1042 | num_pages_strs > ARRAY_SIZE(pdesc->strsPPNs)) | ||
| 1043 | return -EINVAL; | ||
| 1044 | |||
| 1045 | /* Find an available slot in the mksGuestStats user array and reserve it */ | ||
| 1046 | for (slot = 0; slot < ARRAY_SIZE(dev_priv->mksstat_user_pids); ++slot) | ||
| 1047 | if (!atomic_cmpxchg(&dev_priv->mksstat_user_pids[slot], 0, MKSSTAT_PID_RESERVED)) | ||
| 1048 | break; | ||
| 1049 | |||
| 1050 | if (slot == ARRAY_SIZE(dev_priv->mksstat_user_pids)) | ||
| 1051 | return -ENOSPC; | ||
| 1052 | |||
| 1053 | BUG_ON(dev_priv->mksstat_user_pages[slot]); | ||
| 1054 | |||
| 1055 | /* Allocate a page for the instance descriptor */ | ||
| 1056 | page = alloc_page(GFP_KERNEL | __GFP_ZERO); | ||
| 1057 | |||
| 1058 | if (!page) { | ||
| 1059 | atomic_set(&dev_priv->mksstat_user_pids[slot], 0); | ||
| 1060 | return -ENOMEM; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | /* Set up the instance descriptor */ | ||
| 1064 | pdesc = page_address(page); | ||
| 1065 | |||
| 1066 | pdesc->reservedMBZ = 0; | ||
| 1067 | pdesc->statStartVA = arg->stat; | ||
| 1068 | pdesc->strsStartVA = arg->strs; | ||
| 1069 | pdesc->statLength = arg->stat_len; | ||
| 1070 | pdesc->infoLength = arg->info_len; | ||
| 1071 | pdesc->strsLength = arg->strs_len; | ||
| 1072 | desc_len = strncpy_from_user(pdesc->description, u64_to_user_ptr(arg->description), | ||
| 1073 | ARRAY_SIZE(pdesc->description) - 1); | ||
| 1074 | |||
| 1075 | if (desc_len < 0) { | ||
| 1076 | atomic_set(&dev_priv->mksstat_user_pids[slot], 0); | ||
| 1077 | return -EFAULT; | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | reset_ppn_array(pdesc->statPPNs, ARRAY_SIZE(pdesc->statPPNs)); | ||
| 1081 | reset_ppn_array(pdesc->infoPPNs, ARRAY_SIZE(pdesc->infoPPNs)); | ||
| 1082 | reset_ppn_array(pdesc->strsPPNs, ARRAY_SIZE(pdesc->strsPPNs)); | ||
| 1083 | |||
| 1084 | /* Pin mksGuestStat user pages and store those in the instance descriptor */ | ||
| 1085 | nr_pinned_stat = pin_user_pages(arg->stat, num_pages_stat, FOLL_LONGTERM, pages_stat, NULL); | ||
| 1086 | if (num_pages_stat != nr_pinned_stat) | ||
| 1087 | goto err_pin_stat; | ||
| 1088 | |||
| 1089 | for (i = 0; i < num_pages_stat; ++i) | ||
| 1090 | pdesc->statPPNs[i] = page_to_pfn(pages_stat[i]); | ||
| 1091 | |||
| 1092 | nr_pinned_info = pin_user_pages(arg->info, num_pages_info, FOLL_LONGTERM, pages_info, NULL); | ||
| 1093 | if (num_pages_info != nr_pinned_info) | ||
| 1094 | goto err_pin_info; | ||
| 1095 | |||
| 1096 | for (i = 0; i < num_pages_info; ++i) | ||
| 1097 | pdesc->infoPPNs[i] = page_to_pfn(pages_info[i]); | ||
| 1098 | |||
| 1099 | nr_pinned_strs = pin_user_pages(arg->strs, num_pages_strs, FOLL_LONGTERM, pages_strs, NULL); | ||
| 1100 | if (num_pages_strs != nr_pinned_strs) | ||
| 1101 | goto err_pin_strs; | ||
| 1102 | |||
| 1103 | for (i = 0; i < num_pages_strs; ++i) | ||
| 1104 | pdesc->strsPPNs[i] = page_to_pfn(pages_strs[i]); | ||
| 1105 | |||
| 1106 | /* Send the descriptor to the host via a hypervisor call. The mksGuestStat | ||
| 1107 | pages will remain in use until the user requests a matching remove stats | ||
| 1108 | or a stats reset occurs. */ | ||
| 1109 | hypervisor_ppn_add((PPN64)page_to_pfn(page)); | ||
| 1110 | |||
| 1111 | dev_priv->mksstat_user_pages[slot] = page; | ||
| 1112 | atomic_set(&dev_priv->mksstat_user_pids[slot], current->pid); | ||
| 1113 | |||
| 1114 | arg->id = slot; | ||
| 1115 | |||
| 1116 | DRM_DEV_INFO(dev->dev, "pid=%d arg.description='%.*s' id=%lu\n", current->pid, (int)desc_len, pdesc->description, slot); | ||
| 1117 | |||
| 1118 | return 0; | ||
| 1119 | |||
| 1120 | err_pin_strs: | ||
| 1121 | if (nr_pinned_strs > 0) | ||
| 1122 | unpin_user_pages(pages_strs, nr_pinned_strs); | ||
| 1123 | |||
| 1124 | err_pin_info: | ||
| 1125 | if (nr_pinned_info > 0) | ||
| 1126 | unpin_user_pages(pages_info, nr_pinned_info); | ||
| 1127 | |||
| 1128 | err_pin_stat: | ||
| 1129 | if (nr_pinned_stat > 0) | ||
| 1130 | unpin_user_pages(pages_stat, nr_pinned_stat); | ||
| 1131 | |||
| 1132 | atomic_set(&dev_priv->mksstat_user_pids[slot], 0); | ||
| 1133 | __free_page(page); | ||
| 1134 | return -ENOMEM; | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | /** | ||
| 1138 | * vmw_mksstat_remove_ioctl: Removes a single userspace-originating mksGuestStat | ||
| 1139 | * instance descriptor from the hypervisor. | ||
| 1140 | * | ||
| 1141 | * Discard a hypervisor PFN mapping, containing a single mksGuestStat instance | ||
| 1142 | * descriptor and unpin the corresponding userspace pages. | ||
| 1143 | * | ||
| 1144 | * @dev: Identifies the drm device. | ||
| 1145 | * @data: Pointer to the ioctl argument. | ||
| 1146 | * @file_priv: Identifies the caller; unused. | ||
| 1147 | * Return: Zero on success, negative error code on error. | ||
| 1148 | */ | ||
| 1149 | |||
| 1150 | int vmw_mksstat_remove_ioctl(struct drm_device *dev, void *data, | ||
| 1151 | struct drm_file *file_priv) | ||
| 1152 | { | ||
| 1153 | struct drm_vmw_mksstat_remove_arg *arg = | ||
| 1154 | (struct drm_vmw_mksstat_remove_arg *) data; | ||
| 1155 | |||
| 1156 | struct vmw_private *const dev_priv = vmw_priv(dev); | ||
| 1157 | |||
| 1158 | const size_t slot = arg->id; | ||
| 1159 | pid_t pid0; | ||
| 1160 | |||
| 1161 | if (slot >= ARRAY_SIZE(dev_priv->mksstat_user_pids)) | ||
| 1162 | return -EINVAL; | ||
| 1163 | |||
| 1164 | DRM_DEV_INFO(dev->dev, "pid=%d arg.id=%lu\n", current->pid, slot); | ||
| 1165 | |||
| 1166 | pid0 = atomic_read(&dev_priv->mksstat_user_pids[slot]); | ||
| 1167 | |||
| 1168 | if (!pid0) | ||
| 1169 | return 0; | ||
| 1170 | |||
| 1171 | if (pid0 != MKSSTAT_PID_RESERVED) { | ||
| 1172 | const pid_t pid1 = atomic_cmpxchg(&dev_priv->mksstat_user_pids[slot], pid0, MKSSTAT_PID_RESERVED); | ||
| 1173 | |||
| 1174 | if (!pid1) | ||
| 1175 | return 0; | ||
| 1176 | |||
| 1177 | if (pid1 == pid0) { | ||
| 1178 | struct page *const page = dev_priv->mksstat_user_pages[slot]; | ||
| 1179 | |||
| 1180 | BUG_ON(!page); | ||
| 1181 | |||
| 1182 | dev_priv->mksstat_user_pages[slot] = NULL; | ||
| 1183 | atomic_set(&dev_priv->mksstat_user_pids[slot], 0); | ||
| 1184 | |||
| 1185 | hypervisor_ppn_remove((PPN64)page_to_pfn(page)); | ||
| 1186 | |||
| 1187 | vmw_mksstat_cleanup_descriptor(page); | ||
| 1188 | return 0; | ||
| 1189 | } | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | return -EAGAIN; | ||
| 1193 | } | ||
| @@ -72,6 +72,9 @@ extern "C" { · 71 unmodified lines | |||
| 72 | #define DRM_VMW_GB_SURFACE_CREATE_EXT 27 | 72 | #define DRM_VMW_GB_SURFACE_CREATE_EXT 27 |
| 73 | #define DRM_VMW_GB_SURFACE_REF_EXT 28 | 73 | #define DRM_VMW_GB_SURFACE_REF_EXT 28 |
| 74 | #define DRM_VMW_MSG 29 | 74 | #define DRM_VMW_MSG 29 |
| 75 | #define DRM_VMW_MKSSTAT_RESET 30 | ||
| 76 | #define DRM_VMW_MKSSTAT_ADD 31 | ||
| 77 | #define DRM_VMW_MKSSTAT_REMOVE 32 | ||
| 75 | 78 | ||
| 76 | /*************************************************************************/ | 79 | /*************************************************************************/ |
| 77 | /** | 80 | /** |
| @@ -1236,6 +1239,44 @@ struct drm_vmw_msg_arg { · 1158 unmodified lines | |||
| 1236 | __u32 receive_len; | 1239 | __u32 receive_len; |
| 1237 | }; | 1240 | }; |
| 1238 | 1241 | ||
| 1242 | /** | ||
| 1243 | * struct drm_vmw_mksstat_add_arg | ||
| 1244 | * | ||
| 1245 | * @stat: Pointer to user-space stat-counters array, page-aligned. | ||
| 1246 | * @info: Pointer to user-space counter-infos array, page-aligned. | ||
| 1247 | * @strs: Pointer to user-space stat strings, page-aligned. | ||
| 1248 | * @stat_len: Length in bytes of stat-counters array. | ||
| 1249 | * @info_len: Length in bytes of counter-infos array. | ||
| 1250 | * @strs_len: Length in bytes of the stat strings, terminators included. | ||
| 1251 | * @description: Pointer to instance descriptor string; will be truncated | ||
| 1252 | * to MKS_GUEST_STAT_INSTANCE_DESC_LENGTH chars. | ||
| 1253 | * @id: Output identifier of the produced record; -1 if error. | ||
| 1254 | * | ||
| 1255 | * Argument to the DRM_VMW_MKSSTAT_ADD ioctl. | ||
| 1256 | */ | ||
| 1257 | struct drm_vmw_mksstat_add_arg { | ||
| 1258 | __u64 stat; | ||
| 1259 | __u64 info; | ||
| 1260 | __u64 strs; | ||
| 1261 | __u64 stat_len; | ||
| 1262 | __u64 info_len; | ||
| 1263 | __u64 strs_len; | ||
| 1264 | __u64 description; | ||
| 1265 | __u64 id; | ||
| 1266 | }; | ||
| 1267 | |||
| 1268 | /** | ||
| 1269 | * struct drm_vmw_mksstat_remove_arg | ||
| 1270 | * | ||
| 1271 | * @id: Identifier of the record being disposed, originally obtained through | ||
| 1272 | * DRM_VMW_MKSSTAT_ADD ioctl. | ||
| 1273 | * | ||
| 1274 | * Argument to the DRM_VMW_MKSSTAT_REMOVE ioctl. | ||
| 1275 | */ | ||
| 1276 | struct drm_vmw_mksstat_remove_arg { | ||
| 1277 | __u64 id; | ||
| 1278 | }; | ||
| 1279 | |||
| 1239 | #if defined(__cplusplus) | 1280 | #if defined(__cplusplus) |
| 1240 | } | 1281 | } |
| 1241 | #endif | 1282 | #endif |