Commit 7a7a933e authored by Martin Krastev's avatar Martin Krastev Committed by Zack Rusin

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
Signed-off-by: default avatarMartin Krastev <krastevm@vmware.com>
Reviewed-by: default avatarZack Rusin <zackr@vmware.com>
Signed-off-by: default avatarZack Rusin <zackr@vmware.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210609172307.131929-3-zackr@vmware.com
parent d92223ea
......@@ -22,3 +22,10 @@ config DRM_VMWGFX_FBCON
Choose this option if you are shipping a new vmwgfx
userspace driver that supports using the kernel driver.
config DRM_VMWGFX_MKSSTATS
bool "Enable mksGuestStats instrumentation of vmwgfx by default"
depends on DRM_VMWGFX
default n
help
Choose this option to instrument the kernel driver for mksGuestStats.
......@@ -23,9 +23,11 @@
* SOFTWARE.
*
**********************************************************/
#ifndef _VM_BASIC_TYPES_H_
#define _VM_BASIC_TYPES_H_
#ifndef _SVGA_TYPES_H_
#define _SVGA_TYPES_H_
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/page.h>
typedef u32 uint32;
typedef s32 int32;
......@@ -48,4 +50,90 @@ typedef bool Bool;
#define CONST64U(x) x##ULL
/*
* MKS Guest Stats types
*/
typedef struct MKSGuestStatCounter {
atomic64_t count;
} MKSGuestStatCounter;
typedef struct MKSGuestStatCounterTime {
MKSGuestStatCounter counter;
atomic64_t selfCycles;
atomic64_t totalCycles;
} MKSGuestStatCounterTime;
/*
* Flags for MKSGuestStatInfoEntry::flags below
*/
#define MKS_GUEST_STAT_FLAG_NONE 0
#define MKS_GUEST_STAT_FLAG_TIME (1U << 0)
typedef __attribute__((aligned(32))) struct MKSGuestStatInfoEntry {
union {
const char *s;
uint64 u;
} name;
union {
const char *s;
uint64 u;
} description;
uint64 flags;
union {
MKSGuestStatCounter *counter;
MKSGuestStatCounterTime *counterTime;
uint64 u;
} stat;
} MKSGuestStatInfoEntry;
#define INVALID_PPN64 ((PPN64)0x000fffffffffffffULL)
#define vmw_num_pages(size) (PAGE_ALIGN(size) >> PAGE_SHIFT)
#define MKS_GUEST_STAT_INSTANCE_DESC_LENGTH 1024
#define MKS_GUEST_STAT_INSTANCE_MAX_STATS 4096
#define MKS_GUEST_STAT_INSTANCE_MAX_STAT_PPNS \
(vmw_num_pages(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \
sizeof(MKSGuestStatCounterTime)))
#define MKS_GUEST_STAT_INSTANCE_MAX_INFO_PPNS \
(vmw_num_pages(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \
sizeof(MKSGuestStatInfoEntry)))
#define MKS_GUEST_STAT_AVERAGE_NAME_LENGTH 40
#define MKS_GUEST_STAT_INSTANCE_MAX_STRS_PPNS \
(vmw_num_pages(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \
MKS_GUEST_STAT_AVERAGE_NAME_LENGTH))
/*
* The MKSGuestStatInstanceDescriptor is used as main interface to
* communicate guest stats back to the host code. The guest must
* allocate an instance of this structure at the start of a page and
* provide the physical address to the host. From there the host code
* can walk this structure to find other (pinned) pages containing the
* stats data.
*
* Since the MKSGuestStatInfoEntry structures contain userlevel
* pointers, the InstanceDescriptor also contains pointers to the
* begining of these sections allowing the host side code to correctly
* interpret the pointers.
*
* Because the host side code never acknowledges anything back to the
* guest there is no strict requirement to maintain compatability
* across releases. If the interface changes the host might not be
* able to log stats, but the guest will continue to run normally.
*/
typedef struct MKSGuestStatInstanceDescriptor {
uint64 reservedMBZ; /* must be zero for now. */
uint64 statStartVA; /* VA of the start of the stats section. */
uint64 strsStartVA; /* VA of the start of the strings section. */
uint64 statLength; /* length of the stats section in bytes. */
uint64 infoLength; /* length of the info entry section in bytes. */
uint64 strsLength; /* length of the strings section in bytes. */
PPN64 statPPNs[MKS_GUEST_STAT_INSTANCE_MAX_STAT_PPNS]; /* stat counters */
PPN64 infoPPNs[MKS_GUEST_STAT_INSTANCE_MAX_INFO_PPNS]; /* stat info */
PPN64 strsPPNs[MKS_GUEST_STAT_INSTANCE_MAX_STRS_PPNS]; /* strings */
char description[MKS_GUEST_STAT_INSTANCE_DESC_LENGTH];
} MKSGuestStatInstanceDescriptor;
#endif
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _VM_BASIC_TYPES_H_
#define _VM_BASIC_TYPES_H_
#include <linux/kernel.h>
typedef u32 uint32;
typedef s32 int32;
typedef u64 uint64;
typedef u16 uint16;
typedef s16 int16;
typedef u8 uint8;
typedef s8 int8;
typedef uint64 PA;
typedef uint32 PPN;
typedef uint64 PPN64;
typedef bool Bool;
#define MAX_UINT32 U32_MAX
#endif
......@@ -43,6 +43,7 @@
#include "vmwgfx_binding.h"
#include "vmwgfx_devcaps.h"
#include "vmwgfx_drv.h"
#include "vmwgfx_mksstat.h"
#define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
......@@ -148,6 +149,14 @@
#define DRM_IOCTL_VMW_MSG \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_MSG, \
struct drm_vmw_msg_arg)
#define DRM_IOCTL_VMW_MKSSTAT_RESET \
DRM_IO(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_RESET)
#define DRM_IOCTL_VMW_MKSSTAT_ADD \
DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_ADD, \
struct drm_vmw_mksstat_add_arg)
#define DRM_IOCTL_VMW_MKSSTAT_REMOVE \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_REMOVE, \
struct drm_vmw_mksstat_remove_arg)
/*
* The core DRM version of this macro doesn't account for
......@@ -244,6 +253,15 @@ static const struct drm_ioctl_desc vmw_ioctls[] = {
VMW_IOCTL_DEF(VMW_MSG,
vmw_msg_ioctl,
DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_MKSSTAT_RESET,
vmw_mksstat_reset_ioctl,
DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_MKSSTAT_ADD,
vmw_mksstat_add_ioctl,
DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_MKSSTAT_REMOVE,
vmw_mksstat_remove_ioctl,
DRM_RENDER_ALLOW),
};
static const struct pci_device_id vmw_pci_id_list[] = {
......@@ -1137,6 +1155,8 @@ static void vmw_driver_unload(struct drm_device *dev)
for (i = vmw_res_context; i < vmw_res_max; ++i)
idr_destroy(&dev_priv->res_idr[i]);
vmw_mksstat_remove_all(dev_priv);
pci_release_regions(pdev);
}
......
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/**************************************************************************
*
* Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA
* Copyright 2009-2021 VMware, Inc., Palo Alto, CA., USA
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
......@@ -91,6 +91,9 @@
#define VMW_RES_FENCE ttm_driver_type3
#define VMW_RES_SHADER ttm_driver_type4
#define MKSSTAT_CAPACITY_LOG2 5U
#define MKSSTAT_CAPACITY (1U << MKSSTAT_CAPACITY_LOG2)
struct vmw_fpriv {
struct ttm_object_file *tfile;
bool gb_aware; /* user-space is guest-backed aware */
......@@ -630,6 +633,18 @@ struct vmw_private {
struct vmw_validation_mem vvm;
uint32 *devcaps;
/*
* mksGuestStat instance-descriptor and pid arrays
*/
struct page *mksstat_user_pages[MKSSTAT_CAPACITY];
atomic_t mksstat_user_pids[MKSSTAT_CAPACITY];
#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS)
struct page *mksstat_kern_pages[MKSSTAT_CAPACITY];
u8 mksstat_kern_top_timer[MKSSTAT_CAPACITY];
atomic_t mksstat_kern_pids[MKSSTAT_CAPACITY];
#endif
};
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, ...);
int vmw_msg_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/* Host mksGuestStats -vmwgfx_msg.c: */
int vmw_mksstat_get_kern_slot(pid_t pid, struct vmw_private *dev_priv);
int vmw_mksstat_reset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int vmw_mksstat_add_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int vmw_mksstat_remove_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int vmw_mksstat_remove_all(struct vmw_private *dev_priv);
/* VMW logging */
/**
......
......@@ -32,6 +32,7 @@
#include <drm/ttm/ttm_placement.h>
#include "vmwgfx_so.h"
#include "vmwgfx_binding.h"
#include "vmwgfx_mksstat.h"
#define VMW_RES_HT_ORDER 12
......@@ -4406,6 +4407,9 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
int ret;
struct dma_fence *in_fence = NULL;
MKS_STAT_TIME_DECL(MKSSTAT_KERN_EXECBUF);
MKS_STAT_TIME_PUSH(MKSSTAT_KERN_EXECBUF);
/*
* Extend the ioctl argument while maintaining backwards compatibility:
* 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,
if (unlikely(arg->version > DRM_VMW_EXECBUF_VERSION ||
arg->version == 0)) {
VMW_DEBUG_USER("Incorrect execbuf version.\n");
return -EINVAL;
ret = -EINVAL;
goto mksstats_out;
}
switch (arg->version) {
......@@ -4435,7 +4440,8 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
if (!in_fence) {
VMW_DEBUG_USER("Cannot get imported fence\n");
return -EINVAL;
ret = -EINVAL;
goto mksstats_out;
}
ret = vmw_wait_dma_fence(dev_priv->fman, in_fence);
......@@ -4458,5 +4464,8 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
out:
if (in_fence)
dma_fence_put(in_fence);
mksstats_out:
MKS_STAT_TIME_POP(MKSSTAT_KERN_EXECBUF);
return ret;
}
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/**************************************************************************
*
* Copyright 2021 VMware, Inc., Palo Alto, CA., USA
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
#ifndef _VMWGFX_MKSSTAT_H_
#define _VMWGFX_MKSSTAT_H_
#include <asm/page.h>
/* Reservation marker for mksstat pid's */
#define MKSSTAT_PID_RESERVED -1
#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS)
/*
* Kernel-internal mksGuestStat counters. The order of this enum dictates the
* order of instantiation of these counters in the mksGuestStat pages.
*/
typedef enum {
MKSSTAT_KERN_EXECBUF, /* vmw_execbuf_ioctl */
MKSSTAT_KERN_COUNT /* Reserved entry; always last */
} mksstat_kern_stats_t;
/**
* vmw_mksstat_get_kern_pstat: Computes the address of the MKSGuestStatCounterTime
* array from the address of the base page.
*
* @page_addr: Pointer to the base page.
* Return: Pointer to the MKSGuestStatCounterTime array.
*/
static inline void *vmw_mksstat_get_kern_pstat(void *page_addr)
{
return page_addr + PAGE_SIZE * 1;
}
/**
* vmw_mksstat_get_kern_pinfo: Computes the address of the MKSGuestStatInfoEntry
* array from the address of the base page.
*
* @page_addr: Pointer to the base page.
* Return: Pointer to the MKSGuestStatInfoEntry array.
*/
static inline void *vmw_mksstat_get_kern_pinfo(void *page_addr)
{
return page_addr + PAGE_SIZE * 2;
}
/**
* vmw_mksstat_get_kern_pstrs: Computes the address of the mksGuestStat strings
* sequence from the address of the base page.
*
* @page_addr: Pointer to the base page.
* Return: Pointer to the mksGuestStat strings sequence.
*/
static inline void *vmw_mksstat_get_kern_pstrs(void *page_addr)
{
return page_addr + PAGE_SIZE * 3;
}
/*
* MKS_STAT_TIME_DECL/PUSH/POP macros to be used in timer-counted routines.
*/
struct mksstat_timer_t {
/* mutable */ mksstat_kern_stats_t old_top;
const u64 t0;
const int slot;
};
#define MKS_STAT_TIME_DECL(kern_cntr) \
struct mksstat_timer_t _##kern_cntr = { \
.t0 = rdtsc(), \
.slot = vmw_mksstat_get_kern_slot(current->pid, dev_priv) \
}
#define MKS_STAT_TIME_PUSH(kern_cntr) \
do { \
if (_##kern_cntr.slot >= 0) { \
_##kern_cntr.old_top = dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot]; \
dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot] = kern_cntr; \
} \
} while (0)
#define MKS_STAT_TIME_POP(kern_cntr) \
do { \
if (_##kern_cntr.slot >= 0) { \
const pid_t pid = atomic_cmpxchg(&dev_priv->mksstat_kern_pids[_##kern_cntr.slot], current->pid, MKSSTAT_PID_RESERVED); \
dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot] = _##kern_cntr.old_top; \
\
if (pid == current->pid) { \
const u64 dt = rdtsc() - _##kern_cntr.t0; \
MKSGuestStatCounterTime *pstat; \
\
BUG_ON(!dev_priv->mksstat_kern_pages[_##kern_cntr.slot]); \
\
pstat = vmw_mksstat_get_kern_pstat(page_address(dev_priv->mksstat_kern_pages[_##kern_cntr.slot])); \
\
atomic64_inc(&pstat[kern_cntr].counter.count); \
atomic64_add(dt, &pstat[kern_cntr].selfCycles); \
atomic64_add(dt, &pstat[kern_cntr].totalCycles); \
\
if (_##kern_cntr.old_top != MKSSTAT_KERN_COUNT) \
atomic64_sub(dt, &pstat[_##kern_cntr.old_top].selfCycles); \
\
atomic_set(&dev_priv->mksstat_kern_pids[_##kern_cntr.slot], current->pid); \
} \
} \
} while (0)
#else
#define MKS_STAT_TIME_DECL(kern_cntr)
#define MKS_STAT_TIME_PUSH(kern_cntr)
#define MKS_STAT_TIME_POP(kern_cntr)
#endif /* IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS */
#endif
This diff is collapsed.
......@@ -72,6 +72,9 @@ extern "C" {
#define DRM_VMW_GB_SURFACE_CREATE_EXT 27
#define DRM_VMW_GB_SURFACE_REF_EXT 28
#define DRM_VMW_MSG 29
#define DRM_VMW_MKSSTAT_RESET 30
#define DRM_VMW_MKSSTAT_ADD 31
#define DRM_VMW_MKSSTAT_REMOVE 32
/*************************************************************************/
/**
......@@ -1236,6 +1239,44 @@ struct drm_vmw_msg_arg {
__u32 receive_len;
};
/**
* struct drm_vmw_mksstat_add_arg
*
* @stat: Pointer to user-space stat-counters array, page-aligned.
* @info: Pointer to user-space counter-infos array, page-aligned.
* @strs: Pointer to user-space stat strings, page-aligned.
* @stat_len: Length in bytes of stat-counters array.
* @info_len: Length in bytes of counter-infos array.
* @strs_len: Length in bytes of the stat strings, terminators included.
* @description: Pointer to instance descriptor string; will be truncated
* to MKS_GUEST_STAT_INSTANCE_DESC_LENGTH chars.
* @id: Output identifier of the produced record; -1 if error.
*
* Argument to the DRM_VMW_MKSSTAT_ADD ioctl.
*/
struct drm_vmw_mksstat_add_arg {
__u64 stat;
__u64 info;
__u64 strs;
__u64 stat_len;
__u64 info_len;
__u64 strs_len;
__u64 description;
__u64 id;
};
/**
* struct drm_vmw_mksstat_remove_arg
*
* @id: Identifier of the record being disposed, originally obtained through
* DRM_VMW_MKSSTAT_ADD ioctl.
*
* Argument to the DRM_VMW_MKSSTAT_REMOVE ioctl.
*/
struct drm_vmw_mksstat_remove_arg {
__u64 id;
};
#if defined(__cplusplus)
}
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment