Commit 686b21b5 authored by Rob Clark's avatar Rob Clark Committed by Neil Armstrong

drm: Add fdinfo memory stats

Add support to dump GEM stats to fdinfo.

v2: Fix typos, change size units to match docs, use div_u64
v3: Do it in core
v4: more kerneldoc
v5: doc fixes
v6: Actually use u64, bit more comment docs
Signed-off-by: default avatarRob Clark <robdclark@chromium.org>
Reviewed-by: default avatarEmil Velikov <emil.l.velikov@gmail.com>
Reviewed-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Acked-by: default avatarTvrtko Ursulin <tvrtko.ursulin@intel.com>
Acked-by: default avatarDave Airlie <airlied@redhat.com>
Signed-off-by: default avatarNeil Armstrong <neil.armstrong@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20230524155956.382440-6-robdclark@gmail.com
parent 376c25f8
...@@ -52,6 +52,9 @@ String shall contain the name this driver registered as via the respective ...@@ -52,6 +52,9 @@ String shall contain the name this driver registered as via the respective
Optional fully standardised keys Optional fully standardised keys
-------------------------------- --------------------------------
Identification
^^^^^^^^^^^^^^
- drm-pdev: <aaaa:bb.cc.d> - drm-pdev: <aaaa:bb.cc.d>
For PCI devices this should contain the PCI slot address of the device in For PCI devices this should contain the PCI slot address of the device in
...@@ -69,6 +72,9 @@ scope of each device, in which case `drm-pdev` shall be present as well. ...@@ -69,6 +72,9 @@ scope of each device, in which case `drm-pdev` shall be present as well.
Userspace should make sure to not double account any usage statistics by using Userspace should make sure to not double account any usage statistics by using
the above described criteria in order to associate data to individual clients. the above described criteria in order to associate data to individual clients.
Utilization
^^^^^^^^^^^
- drm-engine-<str>: <uint> ns - drm-engine-<str>: <uint> ns
GPUs usually contain multiple execution engines. Each shall be given a stable GPUs usually contain multiple execution engines. Each shall be given a stable
...@@ -93,18 +99,6 @@ exported engine corresponds to a group of identical hardware engines. ...@@ -93,18 +99,6 @@ exported engine corresponds to a group of identical hardware engines.
In the absence of this tag parser shall assume capacity of one. Zero capacity In the absence of this tag parser shall assume capacity of one. Zero capacity
is not allowed. is not allowed.
- drm-memory-<str>: <uint> [KiB|MiB]
Each possible memory type which can be used to store buffer objects by the
GPU in question shall be given a stable and unique name to be returned as the
string here.
Value shall reflect the amount of storage currently consumed by the buffer
object belong to this client, in the respective memory region.
Default unit shall be bytes with optional unit specifiers of 'KiB' or 'MiB'
indicating kibi- or mebi-bytes.
- drm-cycles-<str>: <uint> - drm-cycles-<str>: <uint>
Engine identifier string must be the same as the one specified in the Engine identifier string must be the same as the one specified in the
...@@ -126,6 +120,42 @@ percentage utilization of the engine, whereas drm-engine-<str> only reflects ...@@ -126,6 +120,42 @@ percentage utilization of the engine, whereas drm-engine-<str> only reflects
time active without considering what frequency the engine is operating as a time active without considering what frequency the engine is operating as a
percentage of it's maximum frequency. percentage of it's maximum frequency.
Memory
^^^^^^
- drm-memory-<region>: <uint> [KiB|MiB]
Each possible memory type which can be used to store buffer objects by the
GPU in question shall be given a stable and unique name to be returned as the
string here. The name "memory" is reserved to refer to normal system memory.
Value shall reflect the amount of storage currently consumed by the buffer
objects belong to this client, in the respective memory region.
Default unit shall be bytes with optional unit specifiers of 'KiB' or 'MiB'
indicating kibi- or mebi-bytes.
- drm-shared-<region>: <uint> [KiB|MiB]
The total size of buffers that are shared with another file (ie. have more
than a single handle).
- drm-total-<region>: <uint> [KiB|MiB]
The total size of buffers that including shared and private memory.
- drm-resident-<region>: <uint> [KiB|MiB]
The total size of buffers that are resident in the specified region.
- drm-purgeable-<region>: <uint> [KiB|MiB]
The total size of buffers that are purgeable.
- drm-active-<region>: <uint> [KiB|MiB]
The total size of buffers that are active on one or more engines.
Implementation Details Implementation Details
====================== ======================
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <drm/drm_client.h> #include <drm/drm_client.h>
#include <drm/drm_drv.h> #include <drm/drm_drv.h>
#include <drm/drm_file.h> #include <drm/drm_file.h>
#include <drm/drm_gem.h>
#include <drm/drm_print.h> #include <drm/drm_print.h>
#include "drm_crtc_internal.h" #include "drm_crtc_internal.h"
...@@ -871,9 +872,105 @@ void drm_send_event(struct drm_device *dev, struct drm_pending_event *e) ...@@ -871,9 +872,105 @@ void drm_send_event(struct drm_device *dev, struct drm_pending_event *e)
} }
EXPORT_SYMBOL(drm_send_event); EXPORT_SYMBOL(drm_send_event);
static void print_size(struct drm_printer *p, const char *stat,
const char *region, u64 sz)
{
const char *units[] = {"", " KiB", " MiB"};
unsigned u;
for (u = 0; u < ARRAY_SIZE(units) - 1; u++) {
if (sz < SZ_1K)
break;
sz = div_u64(sz, SZ_1K);
}
drm_printf(p, "drm-%s-%s:\t%llu%s\n", stat, region, sz, units[u]);
}
/**
* drm_print_memory_stats - A helper to print memory stats
* @p: The printer to print output to
* @stats: The collected memory stats
* @supported_status: Bitmask of optional stats which are available
* @region: The memory region
*
*/
void drm_print_memory_stats(struct drm_printer *p,
const struct drm_memory_stats *stats,
enum drm_gem_object_status supported_status,
const char *region)
{
print_size(p, "total", region, stats->private + stats->shared);
print_size(p, "shared", region, stats->shared);
print_size(p, "active", region, stats->active);
if (supported_status & DRM_GEM_OBJECT_RESIDENT)
print_size(p, "resident", region, stats->resident);
if (supported_status & DRM_GEM_OBJECT_PURGEABLE)
print_size(p, "purgeable", region, stats->purgeable);
}
EXPORT_SYMBOL(drm_print_memory_stats);
/**
* drm_show_memory_stats - Helper to collect and show standard fdinfo memory stats
* @p: the printer to print output to
* @file: the DRM file
*
* Helper to iterate over GEM objects with a handle allocated in the specified
* file.
*/
void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file)
{
struct drm_gem_object *obj;
struct drm_memory_stats status = {};
enum drm_gem_object_status supported_status;
int id;
spin_lock(&file->table_lock);
idr_for_each_entry (&file->object_idr, obj, id) {
enum drm_gem_object_status s = 0;
if (obj->funcs && obj->funcs->status) {
s = obj->funcs->status(obj);
supported_status = DRM_GEM_OBJECT_RESIDENT |
DRM_GEM_OBJECT_PURGEABLE;
}
if (obj->handle_count > 1) {
status.shared += obj->size;
} else {
status.private += obj->size;
}
if (s & DRM_GEM_OBJECT_RESIDENT) {
status.resident += obj->size;
} else {
/* If already purged or not yet backed by pages, don't
* count it as purgeable:
*/
s &= ~DRM_GEM_OBJECT_PURGEABLE;
}
if (!dma_resv_test_signaled(obj->resv, dma_resv_usage_rw(true))) {
status.active += obj->size;
/* If still active, don't count as purgeable: */
s &= ~DRM_GEM_OBJECT_PURGEABLE;
}
if (s & DRM_GEM_OBJECT_PURGEABLE)
status.purgeable += obj->size;
}
spin_unlock(&file->table_lock);
drm_print_memory_stats(p, &status, supported_status, "memory");
}
EXPORT_SYMBOL(drm_show_memory_stats);
/** /**
* drm_show_fdinfo - helper for drm file fops * drm_show_fdinfo - helper for drm file fops
* @seq_file: output stream * @m: output stream
* @f: the device file instance * @f: the device file instance
* *
* Helper to implement fdinfo, for userspace to query usage stats, etc, of a * Helper to implement fdinfo, for userspace to query usage stats, etc, of a
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
struct dma_fence; struct dma_fence;
struct drm_file; struct drm_file;
struct drm_device; struct drm_device;
struct drm_printer;
struct device; struct device;
struct file; struct file;
...@@ -441,6 +442,33 @@ void drm_send_event(struct drm_device *dev, struct drm_pending_event *e); ...@@ -441,6 +442,33 @@ void drm_send_event(struct drm_device *dev, struct drm_pending_event *e);
void drm_send_event_timestamp_locked(struct drm_device *dev, void drm_send_event_timestamp_locked(struct drm_device *dev,
struct drm_pending_event *e, struct drm_pending_event *e,
ktime_t timestamp); ktime_t timestamp);
/**
* struct drm_memory_stats - GEM object stats associated
* @shared: Total size of GEM objects shared between processes
* @private: Total size of GEM objects
* @resident: Total size of GEM objects backing pages
* @purgeable: Total size of GEM objects that can be purged (resident and not active)
* @active: Total size of GEM objects active on one or more engines
*
* Used by drm_print_memory_stats()
*/
struct drm_memory_stats {
u64 shared;
u64 private;
u64 resident;
u64 purgeable;
u64 active;
};
enum drm_gem_object_status;
void drm_print_memory_stats(struct drm_printer *p,
const struct drm_memory_stats *stats,
enum drm_gem_object_status supported_status,
const char *region);
void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file);
void drm_show_fdinfo(struct seq_file *m, struct file *f); void drm_show_fdinfo(struct seq_file *m, struct file *f);
struct file *mock_drm_getfile(struct drm_minor *minor, unsigned int flags); struct file *mock_drm_getfile(struct drm_minor *minor, unsigned int flags);
......
...@@ -42,6 +42,25 @@ ...@@ -42,6 +42,25 @@
struct iosys_map; struct iosys_map;
struct drm_gem_object; struct drm_gem_object;
/**
* enum drm_gem_object_status - bitmask of object state for fdinfo reporting
* @DRM_GEM_OBJECT_RESIDENT: object is resident in memory (ie. not unpinned)
* @DRM_GEM_OBJECT_PURGEABLE: object marked as purgeable by userspace
*
* Bitmask of status used for fdinfo memory stats, see &drm_gem_object_funcs.status
* and drm_show_fdinfo(). Note that an object can DRM_GEM_OBJECT_PURGEABLE if
* it still active or not resident, in which case drm_show_fdinfo() will not
* account for it as purgeable. So drivers do not need to check if the buffer
* is idle and resident to return this bit. (Ie. userspace can mark a buffer
* as purgeable even while it is still busy on the GPU.. it does not _actually_
* become puregeable until it becomes idle. The status gem object func does
* not need to consider this.)
*/
enum drm_gem_object_status {
DRM_GEM_OBJECT_RESIDENT = BIT(0),
DRM_GEM_OBJECT_PURGEABLE = BIT(1),
};
/** /**
* struct drm_gem_object_funcs - GEM object functions * struct drm_gem_object_funcs - GEM object functions
*/ */
...@@ -174,6 +193,19 @@ struct drm_gem_object_funcs { ...@@ -174,6 +193,19 @@ struct drm_gem_object_funcs {
*/ */
int (*evict)(struct drm_gem_object *obj); int (*evict)(struct drm_gem_object *obj);
/**
* @status:
*
* The optional status callback can return additional object state
* which determines which stats the object is counted against. The
* callback is called under table_lock. Racing against object status
* change is "harmless", and the callback can expect to not race
* against object destruction.
*
* Called by drm_show_memory_stats().
*/
enum drm_gem_object_status (*status)(struct drm_gem_object *obj);
/** /**
* @vm_ops: * @vm_ops:
* *
......
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