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
2222 Choose this option if you are shipping a new vmwgfx
2323 userspace driver that supports using the kernel driver.
2424
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+
drivers/gpu/drm/vmwgfx/device_include/svga_types.h+90−2
@@ -23,9 +23,11 @@ · 22 unmodified lines
2323 * SOFTWARE.
2424 *
2525 **********************************************************/
26−#ifndef _VM_BASIC_TYPES_H_
27−#define _VM_BASIC_TYPES_H_
26+#ifndef _SVGA_TYPES_H_
27+#define _SVGA_TYPES_H_
2828 #include <linux/kernel.h>
29+#include <linux/mm.h>
30+#include <asm/page.h>
2931
3032 typedef u32 uint32;
3133 typedef s32 int32;
@@ -48,4 +50,90 @@ typedef bool Bool; · 16 unmodified lines
4850
4951 #define CONST64U(x) x##ULL
5052
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+
51139 #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−
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
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c+20−0
@@ -43,6 +43,7 @@ · 42 unmodified lines
4343 #include "vmwgfx_binding.h"
4444 #include "vmwgfx_devcaps.h"
4545 #include "vmwgfx_drv.h"
46+#include "vmwgfx_mksstat.h"
4647
4748 #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
4849
@@ -148,6 +149,14 @@ · 99 unmodified lines
148149 #define DRM_IOCTL_VMW_MSG \
149150 DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_MSG, \
150151 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)
151160
152161 /*
153162 * 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
244253 VMW_IOCTL_DEF(VMW_MSG,
245254 vmw_msg_ioctl,
246255 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),
247265 };
248266
249267 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
11371155 for (i = vmw_res_context; i < vmw_res_max; ++i)
11381156 idr_destroy(&dev_priv->res_idr[i]);
11391157
1158+ vmw_mksstat_remove_all(dev_priv);
1159+
11401160 pci_release_regions(pdev);
11411161 }
11421162
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h+27−1
@@ -1,7 +1,7 @@
11 /* SPDX-License-Identifier: GPL-2.0 OR MIT */
22 /**************************************************************************
33 *
4− * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA
4+ * Copyright 2009-2021 VMware, Inc., Palo Alto, CA., USA
55 *
66 * Permission is hereby granted, free of charge, to any person obtaining a
77 * copy of this software and associated documentation files (the
@@ -91,6 +91,9 @@ · 83 unmodified lines
9191 #define VMW_RES_FENCE ttm_driver_type3
9292 #define VMW_RES_SHADER ttm_driver_type4
9393
94+#define MKSSTAT_CAPACITY_LOG2 5U
95+#define MKSSTAT_CAPACITY (1U << MKSSTAT_CAPACITY_LOG2)
96+
9497 struct vmw_fpriv {
9598 struct ttm_object_file *tfile;
9699 bool gb_aware; /* user-space is guest-backed aware */
@@ -630,6 +633,18 @@ struct vmw_private { · 533 unmodified lines
630633 struct vmw_validation_mem vvm;
631634
632635 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
633648 };
634649
635650 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
15031518 int vmw_msg_ioctl(struct drm_device *dev, void *data,
15041519 struct drm_file *file_priv);
15051520
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+
15061532 /* VMW logging */
15071533
15081534 /**
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c+11−2
@@ -32,6 +32,7 @@ · 31 unmodified lines
3232 #include <drm/ttm/ttm_placement.h>
3333 #include "vmwgfx_so.h"
3434 #include "vmwgfx_binding.h"
35+#include "vmwgfx_mksstat.h"
3536
3637 #define VMW_RES_HT_ORDER 12
3738
@@ -4406,6 +4407,9 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, · 4368 unmodified lines
44064407 int ret;
44074408 struct dma_fence *in_fence = NULL;
44084409
4410+ MKS_STAT_TIME_DECL(MKSSTAT_KERN_EXECBUF);
4411+ MKS_STAT_TIME_PUSH(MKSSTAT_KERN_EXECBUF);
4412+
44094413 /*
44104414 * Extend the ioctl argument while maintaining backwards compatibility:
44114415 * 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
44154419 if (unlikely(arg->version > DRM_VMW_EXECBUF_VERSION ||
44164420 arg->version == 0)) {
44174421 VMW_DEBUG_USER("Incorrect execbuf version.\n");
4418− return -EINVAL;
4422+ ret = -EINVAL;
4423+ goto mksstats_out;
44194424 }
44204425
44214426 switch (arg->version) {
@@ -4435,7 +4440,8 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, · 13 unmodified lines
44354440
44364441 if (!in_fence) {
44374442 VMW_DEBUG_USER("Cannot get imported fence\n");
4438− return -EINVAL;
4443+ ret = -EINVAL;
4444+ goto mksstats_out;
44394445 }
44404446
44414447 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
44584464 out:
44594465 if (in_fence)
44604466 dma_fence_put(in_fence);
4467+
4468+mksstats_out:
4469+ MKS_STAT_TIME_POP(MKSSTAT_KERN_EXECBUF);
44614470 return ret;
44624471 }
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+
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
drivers/gpu/drm/vmwgfx/vmwgfx_msg.c+579−0
@@ -31,10 +31,12 @@ · 30 unmodified lines
3131 #include <linux/mem_encrypt.h>
3232
3333 #include <asm/hypervisor.h>
34+#include <drm/drm_ioctl.h>
3435
3536 #include "vmwgfx_drv.h"
3637 #include "vmwgfx_msg_x86.h"
3738 #include "vmwgfx_msg_arm64.h"
39+#include "vmwgfx_mksstat.h"
3840
3941 #define MESSAGE_STATUS_SUCCESS 0x0001
4042 #define MESSAGE_STATUS_DORECV 0x0002
@@ -56,6 +58,11 @@ · 15 unmodified lines
5658 #define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG)
5759 #define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG)
5860
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+
5966 #define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16)
6067
6168 #define MAX_USER_MSG_LENGTH PAGE_SIZE
@@ -612,3 +619,575 @@ out_open: · 550 unmodified lines
612619
613620 return -EINVAL;
614621 }
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+}
include/uapi/drm/vmwgfx_drm.h+41−0
@@ -72,6 +72,9 @@ extern "C" { · 71 unmodified lines
7272 #define DRM_VMW_GB_SURFACE_CREATE_EXT 27
7373 #define DRM_VMW_GB_SURFACE_REF_EXT 28
7474 #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
7578
7679 /*************************************************************************/
7780 /**
@@ -1236,6 +1239,44 @@ struct drm_vmw_msg_arg { · 1158 unmodified lines
12361239 __u32 receive_len;
12371240 };
12381241
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+
12391280 #if defined(__cplusplus)
12401281 }
12411282 #endif