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

From: Mohamed Ahmed

Date: Tue Jul 14 2026 - 17:16:42 EST


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 <mohamedahmedegypt2001@xxxxxxxxx>
---
.../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 <nouveau@xxxxxxxxxxxxxxxxxxxxx>
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 <nouveau@xxxxxxxxxxxxxxxxxxxxx>
+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