linux/drivers/s390/cio/qdio_debug.c
<<
>>
Prefs
   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
  16debug_info_t *qdio_dbf_setup;
  17debug_info_t *qdio_dbf_error;
  18
  19static struct dentry *debugfs_root;
  20#define QDIO_DEBUGFS_NAME_LEN   10
  21#define QDIO_DBF_NAME_LEN       20
  22
  23struct 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
  29static LIST_HEAD(qdio_dbf_list);
  30static DEFINE_MUTEX(qdio_dbf_list_mutex);
  31
  32static 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
  48static 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
  61int 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(&init_data->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
 113static 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  last_move: %d\n",
 125                   atomic_read(&q->nr_buf_used),
 126                   q->first_to_check, q->last_move);
 127        if (q->is_input_q) {
 128                seq_printf(m, "polling: %d  ack start: %d  ack count: %d\n",
 129                           q->u.in.polling, q->u.in.ack_start,
 130                           q->u.in.ack_count);
 131                seq_printf(m, "DSCI: %d   IRQs disabled: %u\n",
 132                           *(u32 *)q->irq_ptr->dsci,
 133                           test_bit(QDIO_QUEUE_IRQS_DISABLED,
 134                           &q->u.in.queue_irq_state));
 135        }
 136        seq_printf(m, "SBAL states:\n");
 137        seq_printf(m, "|0      |8      |16     |24     |32     |40     |48     |56  63|\n");
 138
 139        for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
 140                debug_get_buf_state(q, i, &state);
 141                switch (state) {
 142                case SLSB_P_INPUT_NOT_INIT:
 143                case SLSB_P_OUTPUT_NOT_INIT:
 144                        seq_printf(m, "N");
 145                        break;
 146                case SLSB_P_OUTPUT_PENDING:
 147                        seq_printf(m, "P");
 148                        break;
 149                case SLSB_P_INPUT_PRIMED:
 150                case SLSB_CU_OUTPUT_PRIMED:
 151                        seq_printf(m, "+");
 152                        break;
 153                case SLSB_P_INPUT_ACK:
 154                        seq_printf(m, "A");
 155                        break;
 156                case SLSB_P_INPUT_ERROR:
 157                case SLSB_P_OUTPUT_ERROR:
 158                        seq_printf(m, "x");
 159                        break;
 160                case SLSB_CU_INPUT_EMPTY:
 161                case SLSB_P_OUTPUT_EMPTY:
 162                        seq_printf(m, "-");
 163                        break;
 164                case SLSB_P_INPUT_HALTED:
 165                case SLSB_P_OUTPUT_HALTED:
 166                        seq_printf(m, ".");
 167                        break;
 168                default:
 169                        seq_printf(m, "?");
 170                }
 171                if (i == 63)
 172                        seq_printf(m, "\n");
 173        }
 174        seq_printf(m, "\n");
 175        seq_printf(m, "|64     |72     |80     |88     |96     |104    |112    |   127|\n");
 176
 177        seq_printf(m, "\nSBAL statistics:");
 178        if (!q->irq_ptr->perf_stat_enabled) {
 179                seq_printf(m, " disabled\n");
 180                return 0;
 181        }
 182
 183        seq_printf(m, "\n1          2..        4..        8..        "
 184                   "16..       32..       64..       127\n");
 185        for (i = 0; i < ARRAY_SIZE(q->q_stats.nr_sbals); i++)
 186                seq_printf(m, "%-10u ", q->q_stats.nr_sbals[i]);
 187        seq_printf(m, "\nError      NOP        Total\n%-10u %-10u %-10u\n\n",
 188                   q->q_stats.nr_sbal_error, q->q_stats.nr_sbal_nop,
 189                   q->q_stats.nr_sbal_total);
 190        return 0;
 191}
 192
 193static int qstat_seq_open(struct inode *inode, struct file *filp)
 194{
 195        return single_open(filp, qstat_show,
 196                           file_inode(filp)->i_private);
 197}
 198
 199static const struct file_operations debugfs_fops = {
 200        .owner   = THIS_MODULE,
 201        .open    = qstat_seq_open,
 202        .read    = seq_read,
 203        .llseek  = seq_lseek,
 204        .release = single_release,
 205};
 206
 207static char *qperf_names[] = {
 208        "Assumed adapter interrupts",
 209        "QDIO interrupts",
 210        "Requested PCIs",
 211        "Inbound tasklet runs",
 212        "Inbound tasklet resched",
 213        "Inbound tasklet resched2",
 214        "Outbound tasklet runs",
 215        "SIGA read",
 216        "SIGA write",
 217        "SIGA sync",
 218        "Inbound calls",
 219        "Inbound handler",
 220        "Inbound stop_polling",
 221        "Inbound queue full",
 222        "Outbound calls",
 223        "Outbound handler",
 224        "Outbound queue full",
 225        "Outbound fast_requeue",
 226        "Outbound target_full",
 227        "QEBSM eqbs",
 228        "QEBSM eqbs partial",
 229        "QEBSM sqbs",
 230        "QEBSM sqbs partial",
 231        "Discarded interrupts"
 232};
 233
 234static int qperf_show(struct seq_file *m, void *v)
 235{
 236        struct qdio_irq *irq_ptr = m->private;
 237        unsigned int *stat;
 238        int i;
 239
 240        if (!irq_ptr)
 241                return 0;
 242        if (!irq_ptr->perf_stat_enabled) {
 243                seq_printf(m, "disabled\n");
 244                return 0;
 245        }
 246        stat = (unsigned int *)&irq_ptr->perf_stat;
 247
 248        for (i = 0; i < ARRAY_SIZE(qperf_names); i++)
 249                seq_printf(m, "%26s:\t%u\n",
 250                           qperf_names[i], *(stat + i));
 251        return 0;
 252}
 253
 254static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf,
 255                               size_t count, loff_t *off)
 256{
 257        struct seq_file *seq = file->private_data;
 258        struct qdio_irq *irq_ptr = seq->private;
 259        struct qdio_q *q;
 260        unsigned long val;
 261        int ret, i;
 262
 263        if (!irq_ptr)
 264                return 0;
 265
 266        ret = kstrtoul_from_user(ubuf, count, 10, &val);
 267        if (ret)
 268                return ret;
 269
 270        switch (val) {
 271        case 0:
 272                irq_ptr->perf_stat_enabled = 0;
 273                memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat));
 274                for_each_input_queue(irq_ptr, q, i)
 275                        memset(&q->q_stats, 0, sizeof(q->q_stats));
 276                for_each_output_queue(irq_ptr, q, i)
 277                        memset(&q->q_stats, 0, sizeof(q->q_stats));
 278                break;
 279        case 1:
 280                irq_ptr->perf_stat_enabled = 1;
 281                break;
 282        }
 283        return count;
 284}
 285
 286static int qperf_seq_open(struct inode *inode, struct file *filp)
 287{
 288        return single_open(filp, qperf_show,
 289                           file_inode(filp)->i_private);
 290}
 291
 292static const struct file_operations debugfs_perf_fops = {
 293        .owner   = THIS_MODULE,
 294        .open    = qperf_seq_open,
 295        .read    = seq_read,
 296        .write   = qperf_seq_write,
 297        .llseek  = seq_lseek,
 298        .release = single_release,
 299};
 300
 301static void setup_debugfs_entry(struct qdio_q *q)
 302{
 303        char name[QDIO_DEBUGFS_NAME_LEN];
 304
 305        snprintf(name, QDIO_DEBUGFS_NAME_LEN, "%s_%d",
 306                 q->is_input_q ? "input" : "output",
 307                 q->nr);
 308        q->debugfs_q = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR,
 309                                q->irq_ptr->debugfs_dev, q, &debugfs_fops);
 310        if (IS_ERR(q->debugfs_q))
 311                q->debugfs_q = NULL;
 312}
 313
 314void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev)
 315{
 316        struct qdio_q *q;
 317        int i;
 318
 319        irq_ptr->debugfs_dev = debugfs_create_dir(dev_name(&cdev->dev),
 320                                                  debugfs_root);
 321        if (IS_ERR(irq_ptr->debugfs_dev))
 322                irq_ptr->debugfs_dev = NULL;
 323
 324        irq_ptr->debugfs_perf = debugfs_create_file("statistics",
 325                                S_IFREG | S_IRUGO | S_IWUSR,
 326                                irq_ptr->debugfs_dev, irq_ptr,
 327                                &debugfs_perf_fops);
 328        if (IS_ERR(irq_ptr->debugfs_perf))
 329                irq_ptr->debugfs_perf = NULL;
 330
 331        for_each_input_queue(irq_ptr, q, i)
 332                setup_debugfs_entry(q);
 333        for_each_output_queue(irq_ptr, q, i)
 334                setup_debugfs_entry(q);
 335}
 336
 337void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr)
 338{
 339        struct qdio_q *q;
 340        int i;
 341
 342        for_each_input_queue(irq_ptr, q, i)
 343                debugfs_remove(q->debugfs_q);
 344        for_each_output_queue(irq_ptr, q, i)
 345                debugfs_remove(q->debugfs_q);
 346        debugfs_remove(irq_ptr->debugfs_perf);
 347        debugfs_remove(irq_ptr->debugfs_dev);
 348}
 349
 350int __init qdio_debug_init(void)
 351{
 352        debugfs_root = debugfs_create_dir("qdio", NULL);
 353
 354        qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16);
 355        debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
 356        debug_set_level(qdio_dbf_setup, DBF_INFO);
 357        DBF_EVENT("dbf created\n");
 358
 359        qdio_dbf_error = debug_register("qdio_error", 4, 1, 16);
 360        debug_register_view(qdio_dbf_error, &debug_hex_ascii_view);
 361        debug_set_level(qdio_dbf_error, DBF_INFO);
 362        DBF_ERROR("dbf created\n");
 363        return 0;
 364}
 365
 366void qdio_debug_exit(void)
 367{
 368        qdio_clear_dbf_list();
 369        debugfs_remove(debugfs_root);
 370        debug_unregister(qdio_dbf_setup);
 371        debug_unregister(qdio_dbf_error);
 372}
 373