From 6c8c5c70a885395afae4e7dd49d6abd9c470bc9e Mon Sep 17 00:00:00 2001 From: Joshua Bakita Date: Sat, 13 Apr 2024 14:19:10 -0400 Subject: Add /proc/gpu#/local_memory API for getting VRAM size --- device_info_procfs.c | 24 ++++++++++++++++++++++++ nvdebug.h | 27 +++++++++++++++++++++++++++ nvdebug_entry.c | 8 ++++++++ 3 files changed, 59 insertions(+) diff --git a/device_info_procfs.c b/device_info_procfs.c index 8fe9709..c8903fc 100644 --- a/device_info_procfs.c +++ b/device_info_procfs.c @@ -61,6 +61,30 @@ struct file_operations nvdebug_read_reg_range_file_ops = { .llseek = default_llseek, }; +static ssize_t local_memory_read(struct file *f, char __user *buf, size_t size, loff_t *off) { + struct nvdebug_state *g = &g_nvdebug_state[file2parentgpuidx(f)]; + char out[30]; + int chars_written; + memory_range_t mem_range; + if (size < 30 || *off != 0) + return 0; + mem_range.raw = nvdebug_readl(g, NV_FB_MMU_LOCAL_MEMORY_RANGE); + if (mem_range.raw == -1) + return -EIO; + // 64-bit size has at most 19 characters + 8 for text and termination + chars_written = scnprintf(out, 30, "%lld bytes\n", memory_range_to_bytes(mem_range)); + if (copy_to_user(buf, out, chars_written)) + printk(KERN_WARNING "Unable to copy all data for %s\n", file_dentry(f)->d_name.name); + *off += chars_written; + return chars_written; +} + +// Read out size of on-device VRAM +struct file_operations local_memory_file_ops = { + .read = local_memory_read, + .llseek = default_llseek, +}; + typedef struct { int idx; // Current index in the device_info table int length; // Length of device_info table (including unpopulated entries) diff --git a/nvdebug.h b/nvdebug.h index ff35f70..b13d311 100644 --- a/nvdebug.h +++ b/nvdebug.h @@ -1145,7 +1145,34 @@ typedef union { } page_tbl_entry_v0_t; */ +/* VRAM Information + If ECC is disabled: + bytes = (magnitude << scale) * 1024 * 1024 + If ECC is enabled: + bytes = ((magnitude << scale) * 1024 * 1024) / 16 * 15 + + Support: Pascal, Volta, Turing, [more?] + */ +#define NV_FB_MMU_LOCAL_MEMORY_RANGE 0x00100ce0 +typedef union { + struct { + uint32_t scale:4; + uint32_t mag:6; + uint32_t:20; + bool is_ecc:1; + uint32_t:1; + } __attribute__((packed)); + uint32_t raw; +} memory_range_t; + +static inline uint64_t memory_range_to_bytes(memory_range_t range) { + // ECC takes a byte out of available memory for parity data + if (range.is_ecc) + return ((range.mag << range.scale) * 1024ull * 1024ull) / 16 * 15; + else + return (range.mag << range.scale) * 1024ull * 1024ull; +} /* Begin nvdebug types and functions */ diff --git a/nvdebug_entry.c b/nvdebug_entry.c index 0caa289..f3dcdbb 100644 --- a/nvdebug_entry.c +++ b/nvdebug_entry.c @@ -31,6 +31,7 @@ extern struct file_operations device_info_file_ops; extern struct file_operations copy_topology_file_ops; extern struct file_operations nvdebug_read_reg32_file_ops; extern struct file_operations nvdebug_read_reg_range_file_ops; +extern struct file_operations local_memory_file_ops; struct nvdebug_state g_nvdebug_state[NVDEBUG_MAX_DEVICES]; unsigned int g_nvdebug_devices = 0; @@ -303,6 +304,13 @@ int __init nvdebug_init(void) { (void*)NV_FUSE_GPC_GM107)) goto out_nomem; } + // Create file `/proc/gpu#/local_memory`, world readable (Pascal+) + if (g_nvdebug_state[res].chip_id >= NV_CHIP_ID_PASCAL) { + if (!proc_create_data( + "local_memory", 0444, dir, compat_ops(&local_memory_file_ops), + (void*)0x00100ce0)) + goto out_nomem; + } // Create files exposing LCE and PCE configuration (Pascal+) if (g_nvdebug_state[res].chip_id >= NV_CHIP_ID_PASCAL) { // Create file `/proc/gpu#/copy_topology`, world readable -- cgit v1.2.2