History

drm/vmwgfx: Introduce VMware mks-guest-stats

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

Martin Krastev committed 5 years ago

Commit 7a7a9331 parent d92223e9 files · +919 −27Browse files

Unified · Split

9 files
  1. drivers/gpu/drm/vmwgfx/Kconfig +7 −0
  2. drivers/gpu/drm/vmwgfx/device_include/svga_types.h +90 −2
  3. drivers/gpu/drm/vmwgfx/device_include/vm_basic_types.h +0 −22
  4. drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +20 −0
  5. drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +27 −1
  6. drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +11 −2
  7. drivers/gpu/drm/vmwgfx/vmwgfx_mksstat.h +144 −0
  8. drivers/gpu/drm/vmwgfx/vmwgfx_msg.c +579 −0
  9. include/uapi/drm/vmwgfx_drm.h +41 −0
drivers/gpu/drm/vmwgfx/Kconfig+7−0
@@ -22,3 +22,10 @@ config DRM_VMWGFX_FBCON · 21 unmodified lines
22 Choose this option if you are shipping a new vmwgfx22 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
25config 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
drivers/gpu/drm/vmwgfx/device_include/svga_types.h+90−2
@@ -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
30typedef u32 uint32;32typedef u32 uint32;
31typedef s32 int32;33typedef s32 int32;
@@ -48,4 +50,90 @@ typedef bool Bool; · 16 unmodified lines
48 50
49#define CONST64U(x) x##ULL51#define CONST64U(x) x##ULL
50 52
53/*
54 * MKS Guest Stats types
55 */
56
57typedef struct MKSGuestStatCounter {
58 atomic64_t count;
59} MKSGuestStatCounter;
60
61typedef 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
74typedef __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
126typedef 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#endif139#endif
drivers/gpu/drm/vmwgfx/device_include/vm_basic_types.h+0−22
@@ -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
6typedef u32 uint32;
7typedef s32 int32;
8typedef u64 uint64;
9typedef u16 uint16;
10typedef s16 int16;
11typedef u8 uint8;
12typedef s8 int8;
13
14typedef uint64 PA;
15typedef uint32 PPN;
16typedef uint64 PPN64;
17
18typedef bool Bool;
19
20#define MAX_UINT32 U32_MAX
21
22#endif
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c+20−0
@@ -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 for162 * 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
249static const struct pci_device_id vmw_pci_id_list[] = {267static 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
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h+27−1
@@ -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., USA4 * Copyright 2009-2021 VMware, Inc., Palo Alto, CA., USA
5 *5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the7 * copy of this software and associated documentation files (the
@@ -91,6 +91,9 @@ · 83 unmodified lines
91#define VMW_RES_FENCE ttm_driver_type391#define VMW_RES_FENCE ttm_driver_type3
92#define VMW_RES_SHADER ttm_driver_type492#define VMW_RES_SHADER ttm_driver_type4
93 93
94#define MKSSTAT_CAPACITY_LOG2 5U
95#define MKSSTAT_CAPACITY (1U << MKSSTAT_CAPACITY_LOG2)
96
94struct vmw_fpriv {97struct 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
635static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res)650static 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
1503int vmw_msg_ioctl(struct drm_device *dev, void *data,1518int 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: */
1522int vmw_mksstat_get_kern_slot(pid_t pid, struct vmw_private *dev_priv);
1523
1524int vmw_mksstat_reset_ioctl(struct drm_device *dev, void *data,
1525 struct drm_file *file_priv);
1526int vmw_mksstat_add_ioctl(struct drm_device *dev, void *data,
1527 struct drm_file *file_priv);
1528int vmw_mksstat_remove_ioctl(struct drm_device *dev, void *data,
1529 struct drm_file *file_priv);
1530int vmw_mksstat_remove_all(struct vmw_private *dev_priv);
1531
1506/* VMW logging */1532/* VMW logging */
1507 1533
1508/**1534/**
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c+11−2
@@ -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 1237#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
4458out:4464out:
4459 if (in_fence)4465 if (in_fence)
4460 dma_fence_put(in_fence);4466 dma_fence_put(in_fence);
4467
4468mksstats_out:
4469 MKS_STAT_TIME_POP(MKSSTAT_KERN_EXECBUF);
4461 return ret;4470 return ret;
4462}4471}
drivers/gpu/drm/vmwgfx/vmwgfx_mksstat.h+144−0
@@ -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
42typedef 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
56static 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
69static 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
82static 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
91struct 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
drivers/gpu/drm/vmwgfx/vmwgfx_msg.c+579−0
@@ -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 0x000141#define MESSAGE_STATUS_SUCCESS 0x0001
40#define MESSAGE_STATUS_DORECV 0x000242#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_SIZE68#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 */
629static 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 */
644static 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 */
661static 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 */
678static 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 */
697static 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
713static 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
739static 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
766static 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
831int 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
875static 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
903int 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
987int 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
1007int 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
1120err_pin_strs:
1121 if (nr_pinned_strs > 0)
1122 unpin_user_pages(pages_strs, nr_pinned_strs);
1123
1124err_pin_info:
1125 if (nr_pinned_info > 0)
1126 unpin_user_pages(pages_info, nr_pinned_info);
1127
1128err_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
1150int 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}
include/uapi/drm/vmwgfx_drm.h+41−0
@@ -72,6 +72,9 @@ extern "C" { · 71 unmodified lines
72#define DRM_VMW_GB_SURFACE_CREATE_EXT 2772#define DRM_VMW_GB_SURFACE_CREATE_EXT 27
73#define DRM_VMW_GB_SURFACE_REF_EXT 2873#define DRM_VMW_GB_SURFACE_REF_EXT 28
74#define DRM_VMW_MSG 2974#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 */
1257struct 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 */
1276struct drm_vmw_mksstat_remove_arg {
1277 __u64 id;
1278};
1279
1239#if defined(__cplusplus)1280#if defined(__cplusplus)
1240}1281}
1241#endif1282#endif