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