[PATCH 5/6] drm/nouveau: expose global VRAM size and usage via sysfs

Mohamed Ahmed <[email protected]> Wed, 15 Jul 2026 01:14:28 +0400
Newsgroups org.freedesktop.lists.nouveau,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Monitoring tools want a device-wide VRAM total and current usage, but
nouveau exposed neither outside of NOUVEAU_GETPARAM ioctls, which are
awkward to use for some tools as they rely on pulling libdrm.

Add two read-only files in a device attribute group:

  mem_info_vram_total  user-accessible VRAM, i.e. NOUVEAU_GETPARAM_FB_SIZE
  mem_info_vram_used   bytes currently allocated from the VRAM TTM manager

Both read nouveau's own framebuffer and TTM accounting, the exact source
behind the FB_SIZE/VRAM_USED getparams, so they need no GSP and are
present on every board, unlike the GSP-only RUSD group. total is the size
of the pool used draws from, so used never exceeds total. used counts only
memory nouveau itself allocated and can read below firmware-reserved usage.

Signed-off-by: Mohamed Ahmed <[email protected]>
---
 .../ABI/testing/sysfs-driver-nouveau          | 17 +++++-
 drivers/gpu/drm/nouveau/nouveau_drv.h         |  1 +
 drivers/gpu/drm/nouveau/nouveau_sysfs.c       | 61 +++++++++++++++++++
 3 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/sysfs-driver-nouveau b/Documentation/ABI/testing/sysfs-driver-nouveau
index 947474b9955d..968c3ac0c464 100644
--- a/Documentation/ABI/testing/sysfs-driver-nouveau
+++ b/Documentation/ABI/testing/sysfs-driver-nouveau
@@ -149,4 +149,19 @@ Date:		June 2026
 Contact:	nouveau <[email protected]>
 Description:
 		PCIe link telemetry. pcie_gen is a packed gen/width bitfield;
-		the remaining files are link error and event counters.
\ No newline at end of file
+		the remaining files are link error and event counters.
+
+What:		/sys/class/drm/card<n>/device/mem_info_vram_total
+What:		/sys/class/drm/card<n>/device/mem_info_vram_used
+Date:		June 2026
+Contact:	nouveau <[email protected]>
+Description:
+		The amount of VRAM usable for allocations and the amount currently
+		in use, in bytes, world-readable and read-only. mem_info_vram_total
+		is the installed VRAM minus the region firmware reserves. This is the
+		same value as NOUVEAU_GETPARAM_FB_SIZE, so it can read slightly below
+		the card's marketed size, and it is the size of the pool that
+		mem_info_vram_used draws from (so used never exceeds total). Both
+		come from nouveau's own framebuffer and TTM accounting (the same
+		source as the FB_SIZE/VRAM_USED ioctls) and are present on every
+		nouveau board, independent of GSP-RM.
\ No newline at end of file
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index a3e343085cc1..a8ca86556e2f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -297,6 +297,7 @@ struct nouveau_drm {
 	struct nouveau_hwmon *hwmon;
 	struct nouveau_debugfs *debugfs;
 	bool rusd_sysfs;
+	bool mem_sysfs;
 
 	/* led management */
 	struct nouveau_led *led;
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.c b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
index f3d9462deaba..789b3781bedc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sysfs.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
@@ -7,11 +7,18 @@
  * file is one decoded value; nvkm owns the units and conversions. A section
  * that is not supported on this board, or has not been polled yet, reads
  * -ENODATA. See Documentation/ABI/testing/sysfs-driver-nouveau for units.
+ *
+ * Also exposes global VRAM size and usage (mem_info_vram_total/used, bytes)
+ * from nouveau's own FB/TTM accounting. Unlike RUSD these do not need GSP and
+ * are present on every board.
  */
 #include <linux/device.h>
 #include <linux/hwmon-sysfs.h>
 #include <linux/sysfs.h>
 
+#include <drm/ttm/ttm_placement.h>
+#include <drm/ttm/ttm_resource.h>
+
 #include "nouveau_drv.h"
 #include "nouveau_sysfs.h"
 
@@ -214,11 +221,60 @@ static const struct attribute_group rusd_group = {
 	.attrs = rusd_attrs,
 };
 
+/*
+ * Global VRAM size and current usage, in bytes. These use the same source as the
+ * NOUVEAU_GETPARAM_FB_SIZE/VRAM_USED ioctls, and are exposed here as a more
+ * convenient way for monitoring tools to access the data.
+ *
+ * Unlike the RUSD group these need no GSP and are present on every board.
+ */
+static ssize_t
+mem_info_vram_total_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct nouveau_drm *drm = dev_get_drvdata(dev);
+
+	return sysfs_emit(buf, "%llu\n", drm->client.device.info.ram_user);
+}
+static DEVICE_ATTR_RO(mem_info_vram_total);
+
+static ssize_t
+mem_info_vram_used_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct nouveau_drm *drm = dev_get_drvdata(dev);
+	struct ttm_resource_manager *vram = ttm_manager_type(&drm->ttm.bdev, TTM_PL_VRAM);
+
+	return sysfs_emit(buf, "%llu\n", ttm_resource_manager_usage(vram));
+}
+static DEVICE_ATTR_RO(mem_info_vram_used);
+
+static struct attribute *nouveau_mem_attrs[] = {
+	&dev_attr_mem_info_vram_total.attr,
+	&dev_attr_mem_info_vram_used.attr,
+	NULL,
+};
+
+static const struct attribute_group nouveau_mem_group = {
+	.attrs = nouveau_mem_attrs,
+};
+
 int
 nouveau_sysfs_init(struct nouveau_drm *drm)
 {
 	int ret;
 
+	/*
+	 * VRAM size/usage comes from nouveau's own accounting, so this group is
+	 * present on every board (GSP or legacy).
+	 *
+	 * The group is NOT devm-managed on drm->dev->dev: that device's devres
+	 * outlives nouveau's remove path (which frees the drm objects the files
+	 * read), so it is torn down explicitly in nouveau_sysfs_fini() before that.
+	 */
+	ret = device_add_group(drm->dev->dev, &nouveau_mem_group);
+	if (ret)
+		return ret;
+	drm->mem_sysfs = true;
+
 	/*
 	 * Only GPUs running GSP with RUSD registered (r570+) get the group. On
 	 * everything else there is nothing to expose. All files appear when RUSD
@@ -247,4 +303,9 @@ nouveau_sysfs_fini(struct nouveau_drm *drm)
 		device_remove_group(drm->dev->dev, &rusd_group);
 		drm->rusd_sysfs = false;
 	}
+
+	if (drm->mem_sysfs) {
+		device_remove_group(drm->dev->dev, &nouveau_mem_group);
+		drm->mem_sysfs = false;
+	}
 }
-- 
2.55.0