diff options
| author | Joshua Bakita <jbakita@cs.unc.edu> | 2024-04-13 14:19:10 -0400 |
|---|---|---|
| committer | Joshua Bakita <jbakita@cs.unc.edu> | 2024-04-13 14:19:10 -0400 |
| commit | 6c8c5c70a885395afae4e7dd49d6abd9c470bc9e (patch) | |
| tree | dd0440345208df6f1d3c35e5c9813ec296358ad5 | |
| parent | 7cfa24cebeaf144b446d07e15fb25e78bb14841e (diff) | |
Add /proc/gpu#/local_memory API for getting VRAM size
| -rw-r--r-- | device_info_procfs.c | 24 | ||||
| -rw-r--r-- | nvdebug.h | 27 | ||||
| -rw-r--r-- | nvdebug_entry.c | 8 |
3 files changed, 59 insertions, 0 deletions
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 = { | |||
| 61 | .llseek = default_llseek, | 61 | .llseek = default_llseek, |
| 62 | }; | 62 | }; |
| 63 | 63 | ||
| 64 | static ssize_t local_memory_read(struct file *f, char __user *buf, size_t size, loff_t *off) { | ||
| 65 | struct nvdebug_state *g = &g_nvdebug_state[file2parentgpuidx(f)]; | ||
| 66 | char out[30]; | ||
| 67 | int chars_written; | ||
| 68 | memory_range_t mem_range; | ||
| 69 | if (size < 30 || *off != 0) | ||
| 70 | return 0; | ||
| 71 | mem_range.raw = nvdebug_readl(g, NV_FB_MMU_LOCAL_MEMORY_RANGE); | ||
| 72 | if (mem_range.raw == -1) | ||
| 73 | return -EIO; | ||
| 74 | // 64-bit size has at most 19 characters + 8 for text and termination | ||
| 75 | chars_written = scnprintf(out, 30, "%lld bytes\n", memory_range_to_bytes(mem_range)); | ||
| 76 | if (copy_to_user(buf, out, chars_written)) | ||
| 77 | printk(KERN_WARNING "Unable to copy all data for %s\n", file_dentry(f)->d_name.name); | ||
| 78 | *off += chars_written; | ||
| 79 | return chars_written; | ||
| 80 | } | ||
| 81 | |||
| 82 | // Read out size of on-device VRAM | ||
| 83 | struct file_operations local_memory_file_ops = { | ||
| 84 | .read = local_memory_read, | ||
| 85 | .llseek = default_llseek, | ||
| 86 | }; | ||
| 87 | |||
| 64 | typedef struct { | 88 | typedef struct { |
| 65 | int idx; // Current index in the device_info table | 89 | int idx; // Current index in the device_info table |
| 66 | int length; // Length of device_info table (including unpopulated entries) | 90 | int length; // Length of device_info table (including unpopulated entries) |
| @@ -1145,7 +1145,34 @@ typedef union { | |||
| 1145 | } page_tbl_entry_v0_t; | 1145 | } page_tbl_entry_v0_t; |
| 1146 | */ | 1146 | */ |
| 1147 | 1147 | ||
| 1148 | /* VRAM Information | ||
| 1148 | 1149 | ||
| 1150 | If ECC is disabled: | ||
| 1151 | bytes = (magnitude << scale) * 1024 * 1024 | ||
| 1152 | If ECC is enabled: | ||
| 1153 | bytes = ((magnitude << scale) * 1024 * 1024) / 16 * 15 | ||
| 1154 | |||
| 1155 | Support: Pascal, Volta, Turing, [more?] | ||
| 1156 | */ | ||
| 1157 | #define NV_FB_MMU_LOCAL_MEMORY_RANGE 0x00100ce0 | ||
| 1158 | typedef union { | ||
| 1159 | struct { | ||
| 1160 | uint32_t scale:4; | ||
| 1161 | uint32_t mag:6; | ||
| 1162 | uint32_t:20; | ||
| 1163 | bool is_ecc:1; | ||
| 1164 | uint32_t:1; | ||
| 1165 | } __attribute__((packed)); | ||
| 1166 | uint32_t raw; | ||
| 1167 | } memory_range_t; | ||
| 1168 | |||
| 1169 | static inline uint64_t memory_range_to_bytes(memory_range_t range) { | ||
| 1170 | // ECC takes a byte out of available memory for parity data | ||
| 1171 | if (range.is_ecc) | ||
| 1172 | return ((range.mag << range.scale) * 1024ull * 1024ull) / 16 * 15; | ||
| 1173 | else | ||
| 1174 | return (range.mag << range.scale) * 1024ull * 1024ull; | ||
| 1175 | } | ||
| 1149 | 1176 | ||
| 1150 | /* Begin nvdebug types and functions */ | 1177 | /* Begin nvdebug types and functions */ |
| 1151 | 1178 | ||
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; | |||
| 31 | extern struct file_operations copy_topology_file_ops; | 31 | extern struct file_operations copy_topology_file_ops; |
| 32 | extern struct file_operations nvdebug_read_reg32_file_ops; | 32 | extern struct file_operations nvdebug_read_reg32_file_ops; |
| 33 | extern struct file_operations nvdebug_read_reg_range_file_ops; | 33 | extern struct file_operations nvdebug_read_reg_range_file_ops; |
| 34 | extern struct file_operations local_memory_file_ops; | ||
| 34 | 35 | ||
| 35 | struct nvdebug_state g_nvdebug_state[NVDEBUG_MAX_DEVICES]; | 36 | struct nvdebug_state g_nvdebug_state[NVDEBUG_MAX_DEVICES]; |
| 36 | unsigned int g_nvdebug_devices = 0; | 37 | unsigned int g_nvdebug_devices = 0; |
| @@ -303,6 +304,13 @@ int __init nvdebug_init(void) { | |||
| 303 | (void*)NV_FUSE_GPC_GM107)) | 304 | (void*)NV_FUSE_GPC_GM107)) |
| 304 | goto out_nomem; | 305 | goto out_nomem; |
| 305 | } | 306 | } |
| 307 | // Create file `/proc/gpu#/local_memory`, world readable (Pascal+) | ||
| 308 | if (g_nvdebug_state[res].chip_id >= NV_CHIP_ID_PASCAL) { | ||
| 309 | if (!proc_create_data( | ||
| 310 | "local_memory", 0444, dir, compat_ops(&local_memory_file_ops), | ||
| 311 | (void*)0x00100ce0)) | ||
| 312 | goto out_nomem; | ||
| 313 | } | ||
| 306 | // Create files exposing LCE and PCE configuration (Pascal+) | 314 | // Create files exposing LCE and PCE configuration (Pascal+) |
| 307 | if (g_nvdebug_state[res].chip_id >= NV_CHIP_ID_PASCAL) { | 315 | if (g_nvdebug_state[res].chip_id >= NV_CHIP_ID_PASCAL) { |
| 308 | // Create file `/proc/gpu#/copy_topology`, world readable | 316 | // Create file `/proc/gpu#/copy_topology`, world readable |
