aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--device_info_procfs.c179
-rw-r--r--nvdebug.h52
-rw-r--r--nvdebug_entry.c39
3 files changed, 134 insertions, 136 deletions
diff --git a/device_info_procfs.c b/device_info_procfs.c
index b2bcd1a..195b3ff 100644
--- a/device_info_procfs.c
+++ b/device_info_procfs.c
@@ -28,49 +28,64 @@ struct file_operations nvdebug_read_reg32_file_ops = {
28}; 28};
29 29
30typedef struct { 30typedef struct {
31 int total_entries; 31 int idx; // Current index in the device_info table
32 int index; 32 int length; // Length of device_info table (including unpopulated entries)
33 int type_of_next_entry; 33 int type_of_next_entry; // Only used on Ampere+ GPUs
34 bool has_next_entry; // Only used on Ampere+ GPUs for show() idempotence
34} device_info_iter; 35} device_info_iter;
35 36
36//// ==v== PTOP_DEVICE_INFO ==v== //// 37//// ==v== PTOP_DEVICE_INFO ==v== ////
37 38
38// Called to start or resume a sequence. Prior to 4.19, *pos is unreliable. 39// Called to start or resume a sequence. Prior to 4.19, *pos is unreliable.
39// Initializes iterator `idx` state and returns it. Ends sequence on NULL. 40// Initializes iterator `iter` state and returns it. Ends sequence on NULL.
40static void* device_info_file_seq_start(struct seq_file *s, loff_t *pos) { 41static void* device_info_file_seq_start(struct seq_file *s, loff_t *pos) {
41 static device_info_iter idx; 42 static device_info_iter iter;
42 struct nvdebug_state *g = &g_nvdebug_state[seq2gpuidx(s)]; 43 // If freshly starting a sequence, reset the iterator
43 int is_ampere = g->chip_id >= NV_CHIP_ID_AMPERE; 44 if (*pos == 0) {
44 // If start of sequence, reset `idx` 45 struct nvdebug_state *g = &g_nvdebug_state[seq2gpuidx(s)];
45 if (*pos == 0) { 46 iter.idx = 0;
46 idx.index = 0; 47 iter.type_of_next_entry = 0;
47 idx.type_of_next_entry = is_ampere ? 0 : -1; 48 iter.has_next_entry = 0;
49 // On Ampere+, the device_info table length can vary
50 if (g->chip_id > NV_CHIP_ID_AMPERE)
51 iter.length = NV_PTOP_DEVICE_INFO__SIZE_1_GA100(g);
52 else
53 iter.length = NV_PTOP_DEVICE_INFO__SIZE_1_GK104;
48 } 54 }
49 idx.total_entries = is_ampere ? NV_PTOP_DEVICE_INFO__SIZE_1_AMPERE(g) : NV_PTOP_DEVICE_INFO__SIZE_1_PREVIOUS; 55 // Number of possible info entries is fixed, and list is sparse, so stop
50 // Number of possible info entries is fixed, and list is sparse 56 // iterating only when all entries have been checked, rather than on the first
51 if (idx.index >= idx.total_entries) 57 // empty entry.
52 return NULL; 58 if (iter.idx >= iter.length)
53 return &idx; 59 return NULL;
60 return &iter;
54} 61}
55 62
56// Steps to next record. Returns new value of `idx`. 63// Steps to next record. Returns `&iter` (address should not change)
57// Calls show() on non-NULL return 64// Calls show() on non-NULL return
58static void* device_info_file_seq_next(struct seq_file *s, void *idx, 65static void* device_info_file_seq_next(struct seq_file *s, void *iter_raw,
59 loff_t *pos) { 66 loff_t *pos) {
60 device_info_iter *idx_iter = (device_info_iter*)idx; 67 device_info_iter *iter = (device_info_iter*)iter_raw;
61 (*pos)++; // Required by seq interface 68 (*pos)++; // Required by seq interface
62 // Number of possible info entries is fixed, and list is sparse 69 // Number of possible info entries is fixed, and list is sparse, so stop
63 if (idx_iter->index++ >= idx_iter->total_entries) 70 // iterating only when all entries have been checked, rather than on the first
71 // empty entry.
72 if (++iter->idx >= iter->length)
64 return NULL; 73 return NULL;
65 return idx; 74 // The info_type field is not available in the Ampere device_info data, so
75 // it must be inferred. NOP for older devices (cheaper than another branch).
76 // This has to be here (rather than in the show function) to support the
77 // idempotence requirements of show() in the seq_file interface.
78 iter->type_of_next_entry = iter->has_next_entry ? iter->type_of_next_entry + 1 : 0;
79 return iter;
66} 80}
67 81
68// Print info at index *idx. Returns non-zero on error. 82// Print info at iter->idx for Kepler--Turing GPUs. Returns non-zero on error.
69static int device_info_file_seq_show_previous(struct seq_file *s, void *idx) { 83// Implementation of this function must be idempotent
70 device_info_iter *idx_iter = (device_info_iter*)idx; 84static int device_info_file_seq_show_gk104(struct seq_file *s, void *iter_raw) {
71 ptop_device_info_previous_t curr_info; 85 device_info_iter *iter = (device_info_iter*)iter_raw;
86 ptop_device_info_gk104_t curr_info;
72 struct nvdebug_state *g = &g_nvdebug_state[seq2gpuidx(s)]; 87 struct nvdebug_state *g = &g_nvdebug_state[seq2gpuidx(s)];
73 curr_info.raw = nvdebug_readl(g, NV_PTOP_DEVICE_INFO_PREVIOUS(idx_iter->index)); 88 curr_info.raw = nvdebug_readl(g, NV_PTOP_DEVICE_INFO_GK104(iter->idx));
74 // Check for read errors 89 // Check for read errors
75 if (curr_info.raw == -1) 90 if (curr_info.raw == -1)
76 return -EIO; 91 return -EIO;
@@ -102,7 +117,7 @@ static int device_info_file_seq_show_previous(struct seq_file *s, void *idx) {
102 if (curr_info.engine_type < ENGINE_TYPES_LEN) 117 if (curr_info.engine_type < ENGINE_TYPES_LEN)
103 seq_printf(s, "%s)\n", ENGINE_TYPES_NAMES[curr_info.engine_type]); 118 seq_printf(s, "%s)\n", ENGINE_TYPES_NAMES[curr_info.engine_type]);
104 else 119 else
105 seq_printf(s, "Unknown Engine, introduced post-Ampere)\n"); 120 seq_printf(s, "Unknown Historical)\n");
106 break; 121 break;
107 case INFO_TYPE_NOT_VALID: 122 case INFO_TYPE_NOT_VALID:
108 default: 123 default:
@@ -116,69 +131,75 @@ static int device_info_file_seq_show_previous(struct seq_file *s, void *idx) {
116 return 0; 131 return 0;
117} 132}
118 133
119// Print info at index *idx for Ampere GPUs. Returns non-zero on error. 134// Print info at iter->idx for Ampere+ GPUs. Returns non-zero on error.
120static int device_info_file_seq_show_ampere(struct seq_file *s, void *idx) { 135// Implementation of this function must be idempotent
121 device_info_iter *idx_iter = (device_info_iter*)idx; 136static int device_info_file_seq_show_ga100(struct seq_file *s, void *iter_raw) {
122 ptop_device_info_ampere_t curr_info; 137 device_info_iter *iter = (device_info_iter*)iter_raw;
123 struct nvdebug_state *g = &g_nvdebug_state[seq2gpuidx(s)]; 138 ptop_device_info_ga100_t curr_info;
124 curr_info.raw = nvdebug_readl(g, NV_PTOP_DEVICE_INFO_AMPERE(idx_iter->index)); 139 struct nvdebug_state *g = &g_nvdebug_state[seq2gpuidx(s)];
125 // Check for read errors 140 curr_info.raw = nvdebug_readl(g, NV_PTOP_DEVICE_INFO_GA100(iter->idx));
126 if (curr_info.raw == -1) 141 // Check for read errors
127 return -EIO; 142 if (curr_info.raw == -1)
128 // The info_type field is not available in the Ampere device_info data, so it must be inferred 143 return -EIO;
129 int info_type = curr_info.raw ? idx_iter->type_of_next_entry : -1; 144
130 // Parse and print the data 145 // Update tracking data only used by next(); allows preserving idempotence
131 switch(info_type) { 146 iter->has_next_entry = curr_info.has_next_entry;
132 case 0: 147 // Silently skip empty entries
133 seq_printf(s, "| instance %d\n", curr_info.inst_id); 148 if (curr_info.raw == 0)
134 seq_printf(s, "| Fault ID: %3d\n", curr_info.fault_id); 149 return 0;
150 // In nvdebug, an entry is considered invalid if it does not consist of at
151 // least two rows. So, if this is the first row of an entry, but another row
152 // is not indicated, this entry is invalid and should be skipped.
153 if (iter->type_of_next_entry == 0 && !curr_info.has_next_entry) {
154 printk(KERN_WARNING "[nvdebug] Skipping seemingly-invalid device_info entry (idx: %d, raw: %#0x)\n", iter->idx, curr_info.raw);
155 return 0;
156 }
157
158 // Parse and print the data
159 switch(iter->type_of_next_entry) {
160 case 0:
161 seq_printf(s, "| instance %d\n", curr_info.inst_id);
162 seq_printf(s, "| Fault ID: %3d\n", curr_info.fault_id);
135 seq_printf(s, "| Engine Type: %2d (", curr_info.engine_type); 163 seq_printf(s, "| Engine Type: %2d (", curr_info.engine_type);
136 if (curr_info.engine_type < ENGINE_TYPES_LEN) 164 if (curr_info.engine_type < ENGINE_TYPES_LEN)
137 seq_printf(s, "%s)\n", ENGINE_TYPES_NAMES[curr_info.engine_type]); 165 seq_printf(s, "%s)\n", ENGINE_TYPES_NAMES[curr_info.engine_type]);
138 else 166 else
139 seq_printf(s, "Unknown Engine, introduced post-Ampere)\n"); 167 seq_printf(s, "Unknown, introduced post-Lovelace)\n");
140 break; 168 break;
141 case 1: 169 case 1:
142 seq_printf(s, "| BAR0 Base %#.8x\n", curr_info.pri_base << 12); 170 seq_printf(s, "| BAR0 Base %#.8x\n", curr_info.pri_base << 12);
143 seq_printf(s, "| Reset ID: %2d\n", curr_info.reset_enum); 171 seq_printf(s, "| Reset ID: %2d\n", curr_info.reset_enum);
144 break; 172 break;
145 case 2: 173 case 2:
146 seq_printf(s, "| Host's Engine ID: %2d\n", curr_info.engine_enum); 174 seq_printf(s, "| Host's Engine ID: %2d\n", curr_info.engine_enum);
147 seq_printf(s, "| Runlist ID: %2d\n", curr_info.runlist_enum); 175 seq_printf(s, "| Runlist ID: %2d\n", curr_info.runlist_enum);
148 break; 176 break;
149 default: 177 default:
150 // Device info records are sparse, so skip unset or unknown ones 178 printk(KERN_WARNING "[nvdebug] Skipping unexpected continuation of device_info entry (idx: %d, raw: %#0x)\n", iter->idx, curr_info.raw);
151 return 0;
152 }
153 if(info_type != -1) idx_iter->type_of_next_entry++;
154 // Draw a line between each device entry
155 if (!curr_info.has_next_entry) {
156 idx_iter->type_of_next_entry = 0;
157 seq_printf(s, "+---------------------+\n");
158 } 179 }
159 return 0; 180
181 // Draw a line between each device entry
182 if (!curr_info.has_next_entry)
183 seq_printf(s, "+---------------------+\n");
184 return 0;
160} 185}
161 186
162static void device_info_file_seq_stop(struct seq_file *s, void *idx) { 187static void device_info_file_seq_stop(struct seq_file *s, void *idx) {
163 // No cleanup needed 188 // No cleanup needed
164} 189}
165 190
166static const struct seq_operations device_info_file_seq_ops = { 191static struct seq_operations device_info_file_seq_ops = {
167 .start = device_info_file_seq_start, 192 .start = device_info_file_seq_start,
168 .next = device_info_file_seq_next, 193 .next = device_info_file_seq_next,
169 .stop = device_info_file_seq_stop 194 .stop = device_info_file_seq_stop,
170}; 195};
171 196
172static int device_info_file_open(struct inode *inode, struct file *f) { 197static int device_info_file_open(struct inode *inode, struct file *f) {
173 if(g_nvdebug_state[file2parentgpuidx(f)].chip_id >= NV_CHIP_ID_AMPERE) { 198 if (g_nvdebug_state[file2parentgpuidx(f)].chip_id >= NV_CHIP_ID_AMPERE)
174 static struct seq_operations file_seq_ops_ampere = device_info_file_seq_ops; 199 device_info_file_seq_ops.show = device_info_file_seq_show_ga100;