aboutsummaryrefslogtreecommitdiffstats
path: root/device_info_procfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'device_info_procfs.c')
-rw-r--r--device_info_procfs.c126
1 files changed, 126 insertions, 0 deletions
diff --git a/device_info_procfs.c b/device_info_procfs.c
new file mode 100644
index 0000000..cd6c53c
--- /dev/null
+++ b/device_info_procfs.c
@@ -0,0 +1,126 @@
1#include "nvdebug.h"
2#include <linux/seq_file.h> // For seq_* functions and types
3#include <linux/uaccess.h> // For copy_to_user()
4
5// Generic register printing function, used for PTOP_*_NUM registers (+more)
6// @param f File being read from. `data` field is register offset to read.
7// @param buf User buffer for result
8// @param size Length of user buffer
9// @param off Requested offset. Updated by number of characters written.
10// @return -errno on error, otherwise number of bytes written to *buf
11// Note: Parent `data` field MUST be the GPU index
12static ssize_t nvdebug_reg32_read(struct file *f, char __user *buf, size_t size, loff_t *off) {
13 char out[16];
14 int chars_written;
15 struct nvdebug_state *g = &g_nvdebug_state[file2parentgpuidx(f)];
16 if (size < 16 || *off != 0)
17 return 0;
18 // 32 bit register will always take less than 16 characters to print
19 chars_written = scnprintf(out, 16, "%#0x\n", nvdebug_readl(g, (uintptr_t)PDE_DATA(file_inode(f))));
20 if (copy_to_user(buf, out, chars_written))
21 printk(KERN_WARNING "Unable to copy all data for %s\n", file_dentry(f)->d_name.name);
22 *off += chars_written;
23 return chars_written;
24}
25const struct file_operations nvdebug_read_reg32_file_ops = {
26 .read = nvdebug_reg32_read,
27};
28
29//// ==v== PTOP_DEVICE_INFO ==v== ////
30
31// Called to start or resume a sequence. Prior to 4.19, *pos is unreliable.
32// Initializes iterator `idx` state and returns it. Ends sequence on NULL.
33static void* device_info_file_seq_start(struct seq_file *s, loff_t *pos) {
34 static int idx;
35 // If start of sequence, reset `idx`
36 if (*pos == 0)
37 idx = 0;
38 // Number of possible info entries is fixed, and list is sparse
39 if (idx >= NV_PTOP_DEVICE_INFO__SIZE_1)
40 return NULL;
41 return &idx;
42}
43
44// Steps to next record. Returns new value of `idx`.
45// Calls show() on non-NULL return
46static void* device_info_file_seq_next(struct seq_file *s, void *idx,
47 loff_t *pos) {
48 (*pos)++; // Required by seq interface
49 // Number of possible info entries is fixed, and list is sparse
50 if ((*(int*)idx)++ >= NV_PTOP_DEVICE_INFO__SIZE_1)
51 return NULL;
52 return idx;
53}
54
55// Print info at index *idx. Returns non-zero on error.
56static int device_info_file_seq_show(struct seq_file *s, void *idx) {
57 ptop_device_info_t curr_info;
58 struct nvdebug_state *g = &g_nvdebug_state[seq2gpuidx(s)];
59
60 curr_info.raw = nvdebug_readl(g, NV_PTOP_DEVICE_INFO(*(int*)idx));
61 // Check for read errors
62 if (curr_info.raw == -1)
63 return -EIO;
64
65 // Parse and print the data
66 switch(curr_info.info_type) {
67 case INFO_TYPE_DATA:
68 // As of early 2022, only the ENUM2 format of this entry exists
69 if (curr_info.is_not_enum2)
70 break;
71 seq_printf(s, "| BAR0 Base %#.8x\n"
72 "| instance %d\n",
73 curr_info.pri_base << 12, curr_info.inst_id);
74 if (curr_info.fault_id_is_valid)
75 seq_printf(s, "| Fault ID: %3d\n", curr_info.fault_id);
76 break;
77 case INFO_TYPE_ENUM:
78 if (curr_info.engine_is_valid)
79 seq_printf(s, "| Host's Engine ID: %2d\n", curr_info.engine_enum);
80 if (curr_info.runlist_is_valid)
81 seq_printf(s, "| Runlist ID: %2d\n", curr_info.runlist_enum);
82 if (curr_info.intr_is_valid)
83 seq_printf(s, "| Interrupt ID: %2d\n", curr_info.intr_enum);
84 if (curr_info.reset_is_valid)
85 seq_printf(s, "| Reset ID: %2d\n", curr_info.reset_enum);
86 break;
87 case INFO_TYPE_ENGINE_TYPE:
88 seq_printf(s, "| Engine Type: %2d (", curr_info.engine_type);
89 if (curr_info.engine_type < ENGINE_TYPES_LEN)
90 seq_printf(s, "%s)\n", ENGINE_TYPES_NAMES[curr_info.engine_type]);
91 else
92 seq_printf(s, "Unknown Engine, introduced post-Ampere)\n");
93 break;
94 case INFO_TYPE_NOT_VALID:
95 default:
96 // Device info records are sparse, so skip unset or unknown ones
97 return 0;
98 }
99
100 // Draw a line between each device entry
101 if (!curr_info.has_next_entry)
102 seq_printf(s, "+---------------------+\n");
103 return 0;
104}
105
106static void device_info_file_seq_stop(struct seq_file *s, void *idx) {
107 // No cleanup needed
108}
109
110static const struct seq_operations device_info_file_seq_ops = {
111 .start = device_info_file_seq_start,
112 .next = device_info_file_seq_next,
113 .stop = device_info_file_seq_stop,
114 .show = device_info_file_seq_show,
115};
116
117static int device_info_file_open(struct inode *inode, struct file *f) {
118 return seq_open(f, &device_info_file_seq_ops);
119}
120
121const struct file_operations device_info_file_ops = {
122 .open = device_info_file_open,
123 .read = seq_read,
124 .llseek = seq_lseek,
125 .release = seq_release,
126};