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