Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Rob Clark <robdclark@chromium.org>
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Acked-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Acked-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20230524155956.382440-6-robdclark@gmail.com

authored by

Rob Clark and committed by
Neil Armstrong
686b21b5 376c25f8

+200 -13
+42 -12
Documentation/gpu/drm-usage-stats.rst
··· 52 52 Optional fully standardised keys 53 53 -------------------------------- 54 54 55 + Identification 56 + ^^^^^^^^^^^^^^ 57 + 55 58 - drm-pdev: <aaaa:bb.cc.d> 56 59 57 60 For PCI devices this should contain the PCI slot address of the device in ··· 71 68 72 69 Userspace should make sure to not double account any usage statistics by using 73 70 the above described criteria in order to associate data to individual clients. 71 + 72 + Utilization 73 + ^^^^^^^^^^^ 74 74 75 75 - drm-engine-<str>: <uint> ns 76 76 ··· 99 93 In the absence of this tag parser shall assume capacity of one. Zero capacity 100 94 is not allowed. 101 95 102 - - drm-memory-<str>: <uint> [KiB|MiB] 103 - 104 - Each possible memory type which can be used to store buffer objects by the 105 - GPU in question shall be given a stable and unique name to be returned as the 106 - string here. 107 - 108 - Value shall reflect the amount of storage currently consumed by the buffer 109 - object belong to this client, in the respective memory region. 110 - 111 - Default unit shall be bytes with optional unit specifiers of 'KiB' or 'MiB' 112 - indicating kibi- or mebi-bytes. 113 - 114 96 - drm-cycles-<str>: <uint> 115 97 116 98 Engine identifier string must be the same as the one specified in the ··· 119 125 percentage utilization of the engine, whereas drm-engine-<str> only reflects 120 126 time active without considering what frequency the engine is operating as a 121 127 percentage of it's maximum frequency. 128 + 129 + Memory 130 + ^^^^^^ 131 + 132 + - drm-memory-<region>: <uint> [KiB|MiB] 133 + 134 + Each possible memory type which can be used to store buffer objects by the 135 + GPU in question shall be given a stable and unique name to be returned as the 136 + string here. The name "memory" is reserved to refer to normal system memory. 137 + 138 + Value shall reflect the amount of storage currently consumed by the buffer 139 + objects belong to this client, in the respective memory region. 140 + 141 + Default unit shall be bytes with optional unit specifiers of 'KiB' or 'MiB' 142 + indicating kibi- or mebi-bytes. 143 + 144 + - drm-shared-<region>: <uint> [KiB|MiB] 145 + 146 + The total size of buffers that are shared with another file (ie. have more 147 + than a single handle). 148 + 149 + - drm-total-<region>: <uint> [KiB|MiB] 150 + 151 + The total size of buffers that including shared and private memory. 152 + 153 + - drm-resident-<region>: <uint> [KiB|MiB] 154 + 155 + The total size of buffers that are resident in the specified region. 156 + 157 + - drm-purgeable-<region>: <uint> [KiB|MiB] 158 + 159 + The total size of buffers that are purgeable. 160 + 161 + - drm-active-<region>: <uint> [KiB|MiB] 162 + 163 + The total size of buffers that are active on one or more engines. 122 164 123 165 Implementation Details 124 166 ======================
+98 -1
drivers/gpu/drm/drm_file.c
··· 42 42 #include <drm/drm_client.h> 43 43 #include <drm/drm_drv.h> 44 44 #include <drm/drm_file.h> 45 + #include <drm/drm_gem.h> 45 46 #include <drm/drm_print.h> 46 47 47 48 #include "drm_crtc_internal.h" ··· 872 871 } 873 872 EXPORT_SYMBOL(drm_send_event); 874 873 874 + static void print_size(struct drm_printer *p, const char *stat, 875 + const char *region, u64 sz) 876 + { 877 + const char *units[] = {"", " KiB", " MiB"}; 878 + unsigned u; 879 + 880 + for (u = 0; u < ARRAY_SIZE(units) - 1; u++) { 881 + if (sz < SZ_1K) 882 + break; 883 + sz = div_u64(sz, SZ_1K); 884 + } 885 + 886 + drm_printf(p, "drm-%s-%s:\t%llu%s\n", stat, region, sz, units[u]); 887 + } 888 + 889 + /** 890 + * drm_print_memory_stats - A helper to print memory stats 891 + * @p: The printer to print output to 892 + * @stats: The collected memory stats 893 + * @supported_status: Bitmask of optional stats which are available 894 + * @region: The memory region 895 + * 896 + */ 897 + void drm_print_memory_stats(struct drm_printer *p, 898 + const struct drm_memory_stats *stats, 899 + enum drm_gem_object_status supported_status, 900 + const char *region) 901 + { 902 + print_size(p, "total", region, stats->private + stats->shared); 903 + print_size(p, "shared", region, stats->shared); 904 + print_size(p, "active", region, stats->active); 905 + 906 + if (supported_status & DRM_GEM_OBJECT_RESIDENT) 907 + print_size(p, "resident", region, stats->resident); 908 + 909 + if (supported_status & DRM_GEM_OBJECT_PURGEABLE) 910 + print_size(p, "purgeable", region, stats->purgeable); 911 + } 912 + EXPORT_SYMBOL(drm_print_memory_stats); 913 + 914 + /** 915 + * drm_show_memory_stats - Helper to collect and show standard fdinfo memory stats 916 + * @p: the printer to print output to 917 + * @file: the DRM file 918 + * 919 + * Helper to iterate over GEM objects with a handle allocated in the specified 920 + * file. 921 + */ 922 + void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file) 923 + { 924 + struct drm_gem_object *obj; 925 + struct drm_memory_stats status = {}; 926 + enum drm_gem_object_status supported_status; 927 + int id; 928 + 929 + spin_lock(&file->table_lock); 930 + idr_for_each_entry (&file->object_idr, obj, id) { 931 + enum drm_gem_object_status s = 0; 932 + 933 + if (obj->funcs && obj->funcs->status) { 934 + s = obj->funcs->status(obj); 935 + supported_status = DRM_GEM_OBJECT_RESIDENT | 936 + DRM_GEM_OBJECT_PURGEABLE; 937 + } 938 + 939 + if (obj->handle_count > 1) { 940 + status.shared += obj->size; 941 + } else { 942 + status.private += obj->size; 943 + } 944 + 945 + if (s & DRM_GEM_OBJECT_RESIDENT) { 946 + status.resident += obj->size; 947 + } else { 948 + /* If already purged or not yet backed by pages, don't 949 + * count it as purgeable: 950 + */ 951 + s &= ~DRM_GEM_OBJECT_PURGEABLE; 952 + } 953 + 954 + if (!dma_resv_test_signaled(obj->resv, dma_resv_usage_rw(true))) { 955 + status.active += obj->size; 956 + 957 + /* If still active, don't count as purgeable: */ 958 + s &= ~DRM_GEM_OBJECT_PURGEABLE; 959 + } 960 + 961 + if (s & DRM_GEM_OBJECT_PURGEABLE) 962 + status.purgeable += obj->size; 963 + } 964 + spin_unlock(&file->table_lock); 965 + 966 + drm_print_memory_stats(p, &status, supported_status, "memory"); 967 + } 968 + EXPORT_SYMBOL(drm_show_memory_stats); 969 + 875 970 /** 876 971 * drm_show_fdinfo - helper for drm file fops 877 - * @seq_file: output stream 972 + * @m: output stream 878 973 * @f: the device file instance 879 974 * 880 975 * Helper to implement fdinfo, for userspace to query usage stats, etc, of a
+28
include/drm/drm_file.h
··· 41 41 struct dma_fence; 42 42 struct drm_file; 43 43 struct drm_device; 44 + struct drm_printer; 44 45 struct device; 45 46 struct file; 46 47 ··· 442 441 void drm_send_event_timestamp_locked(struct drm_device *dev, 443 442 struct drm_pending_event *e, 444 443 ktime_t timestamp); 444 + 445 + /** 446 + * struct drm_memory_stats - GEM object stats associated 447 + * @shared: Total size of GEM objects shared between processes 448 + * @private: Total size of GEM objects 449 + * @resident: Total size of GEM objects backing pages 450 + * @purgeable: Total size of GEM objects that can be purged (resident and not active) 451 + * @active: Total size of GEM objects active on one or more engines 452 + * 453 + * Used by drm_print_memory_stats() 454 + */ 455 + struct drm_memory_stats { 456 + u64 shared; 457 + u64 private; 458 + u64 resident; 459 + u64 purgeable; 460 + u64 active; 461 + }; 462 + 463 + enum drm_gem_object_status; 464 + 465 + void drm_print_memory_stats(struct drm_printer *p, 466 + const struct drm_memory_stats *stats, 467 + enum drm_gem_object_status supported_status, 468 + const char *region); 469 + 470 + void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file); 445 471 void drm_show_fdinfo(struct seq_file *m, struct file *f); 446 472 447 473 struct file *mock_drm_getfile(struct drm_minor *minor, unsigned int flags);
+32
include/drm/drm_gem.h
··· 43 43 struct drm_gem_object; 44 44 45 45 /** 46 + * enum drm_gem_object_status - bitmask of object state for fdinfo reporting 47 + * @DRM_GEM_OBJECT_RESIDENT: object is resident in memory (ie. not unpinned) 48 + * @DRM_GEM_OBJECT_PURGEABLE: object marked as purgeable by userspace 49 + * 50 + * Bitmask of status used for fdinfo memory stats, see &drm_gem_object_funcs.status 51 + * and drm_show_fdinfo(). Note that an object can DRM_GEM_OBJECT_PURGEABLE if 52 + * it still active or not resident, in which case drm_show_fdinfo() will not 53 + * account for it as purgeable. So drivers do not need to check if the buffer 54 + * is idle and resident to return this bit. (Ie. userspace can mark a buffer 55 + * as purgeable even while it is still busy on the GPU.. it does not _actually_ 56 + * become puregeable until it becomes idle. The status gem object func does 57 + * not need to consider this.) 58 + */ 59 + enum drm_gem_object_status { 60 + DRM_GEM_OBJECT_RESIDENT = BIT(0), 61 + DRM_GEM_OBJECT_PURGEABLE = BIT(1), 62 + }; 63 + 64 + /** 46 65 * struct drm_gem_object_funcs - GEM object functions 47 66 */ 48 67 struct drm_gem_object_funcs { ··· 192 173 * This callback is optional. 193 174 */ 194 175 int (*evict)(struct drm_gem_object *obj); 176 + 177 + /** 178 + * @status: 179 + * 180 + * The optional status callback can return additional object state 181 + * which determines which stats the object is counted against. The 182 + * callback is called under table_lock. Racing against object status 183 + * change is "harmless", and the callback can expect to not race 184 + * against object destruction. 185 + * 186 + * Called by drm_show_memory_stats(). 187 + */ 188 + enum drm_gem_object_status (*status)(struct drm_gem_object *obj); 195 189 196 190 /** 197 191 * @vm_ops: