xref: /linux/drivers/s390/cio/qdio_debug.c (revision 06bd48b6cd97ef3889b68c8e09014d81dbc463f1)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  Copyright IBM Corp. 2008, 2009
4  *
5  *  Author: Jan Glauber (jang@linux.vnet.ibm.com)
6  */
7 #include <linux/seq_file.h>
8 #include <linux/debugfs.h>
9 #include <linux/uaccess.h>
10 #include <linux/export.h>
11 #include <linux/slab.h>
12 #include <asm/debug.h>
13 #include "qdio_debug.h"
14 #include "qdio.h"
15 
16 debug_info_t *qdio_dbf_setup;
17 debug_info_t *qdio_dbf_error;
18 
19 static struct dentry *debugfs_root;
20 #define QDIO_DEBUGFS_NAME_LEN	10
21 #define QDIO_DBF_NAME_LEN	20
22 
23 struct qdio_dbf_entry {
24 	char dbf_name[QDIO_DBF_NAME_LEN];
25 	debug_info_t *dbf_info;
26 	struct list_head dbf_list;
27 };
28 
29 static LIST_HEAD(qdio_dbf_list);
30 static DEFINE_MUTEX(qdio_dbf_list_mutex);
31 
32 static debug_info_t *qdio_get_dbf_entry(char *name)
33 {
34 	struct qdio_dbf_entry *entry;
35 	debug_info_t *rc = NULL;
36 
37 	mutex_lock(&qdio_dbf_list_mutex);
38 	list_for_each_entry(entry, &qdio_dbf_list, dbf_list) {
39 		if (strcmp(entry->dbf_name, name) == 0) {
40 			rc = entry->dbf_info;
41 			break;
42 		}
43 	}
44 	mutex_unlock(&qdio_dbf_list_mutex);
45 	return rc;
46 }
47 
48 static void qdio_clear_dbf_list(void)
49 {
50 	struct qdio_dbf_entry *entry, *tmp;
51 
52 	mutex_lock(&qdio_dbf_list_mutex);
53 	list_for_each_entry_safe(entry, tmp, &qdio_dbf_list, dbf_list) {
54 		list_del(&entry->dbf_list);
55 		debug_unregister(entry->dbf_info);
56 		kfree(entry);
57 	}
58 	mutex_unlock(&qdio_dbf_list_mutex);
59 }
60 
61 int qdio_allocate_dbf(struct qdio_initialize *init_data,
62 		       struct qdio_irq *irq_ptr)
63 {
64 	char text[QDIO_DBF_NAME_LEN];
65 	struct qdio_dbf_entry *new_entry;
66 
67 	DBF_EVENT("qfmt:%1d", init_data->q_format);
68 	DBF_HEX(init_data->adapter_name, 8);
69 	DBF_EVENT("qpff%4x", init_data->qib_param_field_format);
70 	DBF_HEX(&init_data->qib_param_field, sizeof(void *));
71 	DBF_HEX(&init_data->input_slib_elements, sizeof(void *));
72 	DBF_HEX(&init_data->output_slib_elements, sizeof(void *));
73 	DBF_EVENT("niq:%1d noq:%1d", init_data->no_input_qs,
74 		  init_data->no_output_qs);
75 	DBF_HEX(&init_data->input_handler, sizeof(void *));
76 	DBF_HEX(&init_data->output_handler, sizeof(void *));
77 	DBF_HEX(&init_data->int_parm, sizeof(long));
78 	DBF_HEX(&init_data->input_sbal_addr_array, sizeof(void *));
79 	DBF_HEX(&init_data->output_sbal_addr_array, sizeof(void *));
80 	DBF_EVENT("irq:%8lx", (unsigned long)irq_ptr);
81 
82 	/* allocate trace view for the interface */
83 	snprintf(text, QDIO_DBF_NAME_LEN, "qdio_%s",
84 		 dev_name(&irq_ptr->cdev->dev));
85 	irq_ptr->debug_area = qdio_get_dbf_entry(text);
86 	if (irq_ptr->debug_area)
87 		DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf reused");
88 	else {
89 		irq_ptr->debug_area = debug_register(text, 2, 1, 16);
90 		if (!irq_ptr->debug_area)
91 			return -ENOMEM;
92 		if (debug_register_view(irq_ptr->debug_area,
93 						&debug_hex_ascii_view)) {
94 			debug_unregister(irq_ptr->debug_area);
95 			return -ENOMEM;
96 		}
97 		debug_set_level(irq_ptr->debug_area, DBF_WARN);
98 		DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf created");
99 		new_entry = kzalloc(sizeof(struct qdio_dbf_entry), GFP_KERNEL);
100 		if (!new_entry) {
101 			debug_unregister(irq_ptr->debug_area);
102 			return -ENOMEM;
103 		}
104 		strlcpy(new_entry->dbf_name, text, QDIO_DBF_NAME_LEN);
105 		new_entry->dbf_info = irq_ptr->debug_area;
106 		mutex_lock(&qdio_dbf_list_mutex);
107 		list_add(&new_entry->dbf_list, &qdio_dbf_list);
108 		mutex_unlock(&qdio_dbf_list_mutex);
109 	}
110 	return 0;
111 }
112 
113 static int qstat_show(struct seq_file *m, void *v)
114 {
115 	unsigned char state;
116 	struct qdio_q *q = m->private;
117 	int i;
118 
119 	if (!q)
120 		return 0;
121 
122 	seq_printf(m, "Timestamp: %Lx  Last AI: %Lx\n",
123 		   q->timestamp, last_ai_time);
124 	seq_printf(m, "nr_used: %d  ftc: %d\n",
125 		   atomic_read(&q->nr_buf_used), q->first_to_check);
126 	if (q->is_input_q) {
127 		seq_printf(m, "ack start: %d  ack count: %d\n",
128 			   q->u.in.ack_start, q->u.in.ack_count);
129 		seq_printf(m, "DSCI: %x   IRQs disabled: %u\n",
130 			   *(u8 *)q->irq_ptr->dsci,
131 			   test_bit(QDIO_IRQ_DISABLED,
132 				    &q->irq_ptr->poll_state));
133 	}
134 	seq_printf(m, "SBAL states:\n");
135 	seq_printf(m, "|0      |8      |16     |24     |32     |40     |48     |56  63|\n");
136 
137 	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
138 		debug_get_buf_state(q, i, &state);
139 		switch (state) {
140 		case SLSB_P_INPUT_NOT_INIT:
141 		case SLSB_P_OUTPUT_NOT_INIT:
142 			seq_printf(m, "N");
143 			break;
144 		case SLSB_P_OUTPUT_PENDING:
145 			seq_printf(m, "P");
146 			break;
147 		case SLSB_P_INPUT_PRIMED:
148 		case SLSB_CU_OUTPUT_PRIMED:
149 			seq_printf(m, "+");
150 			break;
151 		case SLSB_P_INPUT_ACK:
152 			seq_printf(m, "A");
153 			break;
154 		case SLSB_P_INPUT_ERROR:
155 		case SLSB_P_OUTPUT_ERROR:
156 			seq_printf(m, "x");
157 			break;
158 		case SLSB_CU_INPUT_EMPTY:
159 		case SLSB_P_OUTPUT_EMPTY:
160 			seq_printf(m, "-");
161 			break;
162 		case SLSB_P_INPUT_HALTED:
163 		case SLSB_P_OUTPUT_HALTED:
164 			seq_printf(m, ".");
165 			break;
166 		default:
167 			seq_printf(m, "?");
168 		}
169 		if (i == 63)
170 			seq_printf(m, "\n");
171 	}
172 	seq_printf(m, "\n");
173 	seq_printf(m, "|64     |72     |80     |88     |96     |104    |112    |   127|\n");
174 
175 	seq_printf(m, "\nSBAL statistics:");
176 	if (!q->irq_ptr->perf_stat_enabled) {
177 		seq_printf(m, " disabled\n");
178 		return 0;
179 	}
180 
181 	seq_printf(m, "\n1          2..        4..        8..        "
182 		   "16..       32..       64..       127\n");
183 	for (i = 0; i < ARRAY_SIZE(q->q_stats.nr_sbals); i++)
184 		seq_printf(m, "%-10u ", q->q_stats.nr_sbals[i]);
185 	seq_printf(m, "\nError      NOP        Total\n%-10u %-10u %-10u\n\n",
186 		   q->q_stats.nr_sbal_error, q->q_stats.nr_sbal_nop,
187 		   q->q_stats.nr_sbal_total);
188 	return 0;
189 }
190 
191 DEFINE_SHOW_ATTRIBUTE(qstat);
192 
193 static int ssqd_show(struct seq_file *m, void *v)
194 {
195 	struct ccw_device *cdev = m->private;
196 	struct qdio_ssqd_desc ssqd;
197 	int rc;
198 
199 	rc = qdio_get_ssqd_desc(cdev, &ssqd);
200 	if (rc)
201 		return rc;
202 
203 	seq_hex_dump(m, "", DUMP_PREFIX_NONE, 16, 4, &ssqd, sizeof(ssqd),
204 		     false);
205 	return 0;
206 }
207 
208 DEFINE_SHOW_ATTRIBUTE(ssqd);
209 
210 static char *qperf_names[] = {
211 	"Assumed adapter interrupts",
212 	"QDIO interrupts",
213 	"Requested PCIs",
214 	"Inbound tasklet runs",
215 	"Inbound tasklet resched",
216 	"Inbound tasklet resched2",
217 	"Outbound tasklet runs",
218 	"SIGA read",
219 	"SIGA write",
220 	"SIGA sync",
221 	"Inbound calls",
222 	"Inbound handler",
223 	"Inbound stop_polling",
224 	"Inbound queue full",
225 	"Outbound calls",
226 	"Outbound handler",
227 	"Outbound queue full",
228 	"Outbound fast_requeue",
229 	"Outbound target_full",
230 	"QEBSM eqbs",
231 	"QEBSM eqbs partial",
232 	"QEBSM sqbs",
233 	"QEBSM sqbs partial",
234 	"Discarded interrupts"
235 };
236 
237 static int qperf_show(struct seq_file *m, void *v)
238 {
239 	struct qdio_irq *irq_ptr = m->private;
240 	unsigned int *stat;
241 	int i;
242 
243 	if (!irq_ptr)
244 		return 0;
245 	if (!irq_ptr->perf_stat_enabled) {
246 		seq_printf(m, "disabled\n");
247 		return 0;
248 	}
249 	stat = (unsigned int *)&irq_ptr->perf_stat;
250 
251 	for (i = 0; i < ARRAY_SIZE(qperf_names); i++)
252 		seq_printf(m, "%26s:\t%u\n",
253 			   qperf_names[i], *(stat + i));
254 	return 0;
255 }
256 
257 static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf,
258 			       size_t count, loff_t *off)
259 {
260 	struct seq_file *seq = file->private_data;
261 	struct qdio_irq *irq_ptr = seq->private;
262 	struct qdio_q *q;
263 	unsigned long val;
264 	int ret, i;
265 
266 	if (!irq_ptr)
267 		return 0;
268 
269 	ret = kstrtoul_from_user(ubuf, count, 10, &val);
270 	if (ret)
271 		return ret;
272 
273 	switch (val) {
274 	case 0:
275 		irq_ptr->perf_stat_enabled = 0;
276 		memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat));
277 		for_each_input_queue(irq_ptr, q, i)
278 			memset(&q->q_stats, 0, sizeof(q->q_stats));
279 		for_each_output_queue(irq_ptr, q, i)
280 			memset(&q->q_stats, 0, sizeof(q->q_stats));
281 		break;
282 	case 1:
283 		irq_ptr->perf_stat_enabled = 1;
284 		break;
285 	}
286 	return count;
287 }
288 
289 static int qperf_seq_open(struct inode *inode, struct file *filp)
290 {
291 	return single_open(filp, qperf_show,
292 			   file_inode(filp)->i_private);
293 }
294 
295 static const struct file_operations debugfs_perf_fops = {
296 	.owner	 = THIS_MODULE,
297 	.open	 = qperf_seq_open,
298 	.read	 = seq_read,
299 	.write	 = qperf_seq_write,
300 	.llseek  = seq_lseek,
301 	.release = single_release,
302 };
303 
304 static void setup_debugfs_entry(struct dentry *parent, struct qdio_q *q)
305 {
306 	char name[QDIO_DEBUGFS_NAME_LEN];
307 
308 	snprintf(name, QDIO_DEBUGFS_NAME_LEN, "%s_%d",
309 		 q->is_input_q ? "input" : "output",
310 		 q->nr);
311 	debugfs_create_file(name, 0444, parent, q, &qstat_fops);
312 }
313 
314 void qdio_setup_debug_entries(struct qdio_irq *irq_ptr)
315 {
316 	struct qdio_q *q;
317 	int i;
318 
319 	irq_ptr->debugfs_dev = debugfs_create_dir(dev_name(&irq_ptr->cdev->dev),
320 						  debugfs_root);
321 	debugfs_create_file("statistics", S_IFREG | S_IRUGO | S_IWUSR,
322 			    irq_ptr->debugfs_dev, irq_ptr, &debugfs_perf_fops);
323 	debugfs_create_file("ssqd", 0444, irq_ptr->debugfs_dev, irq_ptr->cdev,
324 			    &ssqd_fops);
325 
326 	for_each_input_queue(irq_ptr, q, i)
327 		setup_debugfs_entry(irq_ptr->debugfs_dev, q);
328 	for_each_output_queue(irq_ptr, q, i)
329 		setup_debugfs_entry(irq_ptr->debugfs_dev, q);
330 }
331 
332 void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr)
333 {
334 	debugfs_remove_recursive(irq_ptr->debugfs_dev);
335 }
336 
337 int __init qdio_debug_init(void)
338 {
339 	debugfs_root = debugfs_create_dir("qdio", NULL);
340 
341 	qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16);
342 	debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
343 	debug_set_level(qdio_dbf_setup, DBF_INFO);
344 	DBF_EVENT("dbf created\n");
345 
346 	qdio_dbf_error = debug_register("qdio_error", 4, 1, 16);
347 	debug_register_view(qdio_dbf_error, &debug_hex_ascii_view);
348 	debug_set_level(qdio_dbf_error, DBF_INFO);
349 	DBF_ERROR("dbf created\n");
350 	return 0;
351 }
352 
353 void qdio_debug_exit(void)
354 {
355 	qdio_clear_dbf_list();
356 	debugfs_remove_recursive(debugfs_root);
357 	debug_unregister(qdio_dbf_setup);
358 	debug_unregister(qdio_dbf_error);
359 }
360