linux/drivers/s390/cio/qdio_debug.c
<<
>>
Prefs
   1/*
   2 *  Copyright IBM Corp. 2008, 2009
   3 *
   4 *  Author: Jan Glauber (jang@linux.vnet.ibm.com)
   5 */
   6#include <linux/seq_file.h>
   7#include <linux/debugfs.h>
   8#include <linux/uaccess.h>
   9#include <linux/export.h>
  10#include <linux/slab.h>
  11#include <asm/debug.h>
  12#include "qdio_debug.h"
  13#include "qdio.h"
  14
  15debug_info_t *qdio_dbf_setup;
  16debug_info_t *qdio_dbf_error;
  17
  18static struct dentry *debugfs_root;
  19#define QDIO_DEBUGFS_NAME_LEN   10
  20#define QDIO_DBF_NAME_LEN       20
  21
  22struct qdio_dbf_entry {
  23        char dbf_name[QDIO_DBF_NAME_LEN];
  24        debug_info_t *dbf_info;
  25        struct list_head dbf_list;
  26};
  27
  28static LIST_HEAD(qdio_dbf_list);
  29static DEFINE_MUTEX(qdio_dbf_list_mutex);
  30
  31static debug_info_t *qdio_get_dbf_entry(char *name)
  32{
  33        struct qdio_dbf_entry *entry;
  34        debug_info_t *rc = NULL;
  35
  36        mutex_lock(&qdio_dbf_list_mutex);
  37        list_for_each_entry(entry, &qdio_dbf_list, dbf_list) {
  38                if (strcmp(entry->dbf_name, name) == 0) {
  39                        rc = entry->dbf_info;
  40                        break;
  41                }
  42        }
  43        mutex_unlock(&qdio_dbf_list_mutex);
  44        return rc;
  45}
  46
  47static void qdio_clear_dbf_list(void)
  48{
  49        struct qdio_dbf_entry *entry, *tmp;
  50
  51        mutex_lock(&qdio_dbf_list_mutex);
  52        list_for_each_entry_safe(entry, tmp, &qdio_dbf_list, dbf_list) {
  53                list_del(&entry->dbf_list);
  54                debug_unregister(entry->dbf_info);
  55                kfree(entry);
  56        }
  57        mutex_unlock(&qdio_dbf_list_mutex);
  58}
  59
  60int qdio_allocate_dbf(struct qdio_initialize *init_data,
  61                       struct qdio_irq *irq_ptr)
  62{
  63        char text[QDIO_DBF_NAME_LEN];
  64        struct qdio_dbf_entry *new_entry;
  65
  66        DBF_EVENT("qfmt:%1d", init_data->q_format);
  67        DBF_HEX(init_data->adapter_name, 8);
  68        DBF_EVENT("qpff%4x", init_data->qib_param_field_format);
  69        DBF_HEX(&init_data->qib_param_field, sizeof(void *));
  70        DBF_HEX(&init_data->input_slib_elements, sizeof(void *));
  71        DBF_HEX(&init_data->output_slib_elements, sizeof(void *));
  72        DBF_EVENT("niq:%1d noq:%1d", init_data->no_input_qs,
  73                  init_data->no_output_qs);
  74        DBF_HEX(&init_data->input_handler, sizeof(void *));
  75        DBF_HEX(&init_data->output_handler, sizeof(void *));
  76        DBF_HEX(&init_data->int_parm, sizeof(long));
  77        DBF_HEX(&init_data->input_sbal_addr_array, sizeof(void *));
  78        DBF_HEX(&init_data->output_sbal_addr_array, sizeof(void *));
  79        DBF_EVENT("irq:%8lx", (unsigned long)irq_ptr);
  80
  81        /* allocate trace view for the interface */
  82        snprintf(text, QDIO_DBF_NAME_LEN, "qdio_%s",
  83                                        dev_name(&init_data->cdev->dev));
  84        irq_ptr->debug_area = qdio_get_dbf_entry(text);
  85        if (irq_ptr->debug_area)
  86                DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf reused");
  87        else {
  88                irq_ptr->debug_area = debug_register(text, 2, 1, 16);
  89                if (!irq_ptr->debug_area)
  90                        return -ENOMEM;
  91                if (debug_register_view(irq_ptr->debug_area,
  92                                                &debug_hex_ascii_view)) {
  93                        debug_unregister(irq_ptr->debug_area);
  94                        return -ENOMEM;
  95                }
  96                debug_set_level(irq_ptr->debug_area, DBF_WARN);
  97                DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf created");
  98                new_entry = kzalloc(sizeof(struct qdio_dbf_entry), GFP_KERNEL);
  99                if (!new_entry) {
 100                        debug_unregister(irq_ptr->debug_area);
 101                        return -ENOMEM;
 102                }
 103                strlcpy(new_entry->dbf_name, text, QDIO_DBF_NAME_LEN);
 104                new_entry->dbf_info = irq_ptr->debug_area;
 105                mutex_lock(&qdio_dbf_list_mutex);
 106                list_add(&new_entry->dbf_list, &qdio_dbf_list);
 107                mutex_unlock(&qdio_dbf_list_mutex);
 108        }
 109        return 0;
 110}
 111
 112static int qstat_show(struct seq_file *m, void *v)
 113{
 114        unsigned char state;
 115        struct qdio_q *q = m->private;
 116        int i;
 117
 118        if (!q)
 119                return 0;
 120
 121        seq_printf(m, "Timestamp: %Lx  Last AI: %Lx\n",
 122                   q->timestamp, last_ai_time);
 123        seq_printf(m, "nr_used: %d  ftc: %d  last_move: %d\n",
 124                   atomic_read(&q->nr_buf_used),
 125                   q->first_to_check, q->last_move);
 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: %d   IRQs disabled: %u\n",
 131                           *(u32 *)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
 192static int qstat_seq_open(struct inode *inode, struct file *filp)
 193{
 194        return single_open(filp, qstat_show,
 195                           file_inode(filp)->i_private);
 196}
 197
 198static const struct file_operations debugfs_fops = {
 199        .owner   = THIS_MODULE,
 200        .open    = qstat_seq_open,
 201        .read    = seq_read,
 202        .llseek  = seq_lseek,
 203        .release = single_release,
 204};
 205
 206static char *qperf_names[] = {
 207        "Assumed adapter interrupts",
 208        "QDIO interrupts",
 209        "Requested PCIs",
 210        "Inbound tasklet runs",
 211        "Inbound tasklet resched",
 212        "Inbound tasklet resched2",
 213        "Outbound tasklet runs",
 214        "SIGA read",
 215        "SIGA write",
 216        "SIGA sync",
 217        "Inbound calls",
 218        "Inbound handler",
 219        "Inbound stop_polling",
 220        "Inbound queue full",
 221        "Outbound calls",
 222        "Outbound handler",
 223        "Outbound queue full",
 224        "Outbound fast_requeue",
 225        "Outbound target_full",
 226        "QEBSM eqbs",
 227        "QEBSM eqbs partial",
 228        "QEBSM sqbs",
 229        "QEBSM sqbs partial",
 230        "Discarded interrupts"
 231};
 232
 233static int qperf_show(struct seq_file *m, void *v)
 234{
 235        struct qdio_irq *irq_ptr = m->private;
 236        unsigned int *stat;
 237        int i;
 238
 239        if (!irq_ptr)
 240                return 0;
 241        if (!irq_ptr->perf_stat_enabled) {
 242                seq_printf(m, "disabled\n");
 243                return 0;
 244        }
 245        stat = (unsigned int *)&irq_ptr->perf_stat;
 246
 247        for (i = 0; i < ARRAY_SIZE(qperf_names); i++)
 248                seq_printf(m, "%26s:\t%u\n",
 249                           qperf_names[i], *(stat + i));
 250        return 0;
 251}
 252
 253static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf,
 254                               size_t count, loff_t *off)
 255{
 256        struct seq_file *seq = file->private_data;
 257        struct qdio_irq *irq_ptr = seq->private;
 258        struct qdio_q *q;
 259        unsigned long val;
 260        int ret, i;
 261
 262        if (!irq_ptr)
 263                return 0;
 264
 265        ret = kstrtoul_from_user(ubuf, count, 10, &val);
 266        if (ret)
 267                return ret;
 268
 269        switch (val) {
 270        case 0:
 271                irq_ptr->perf_stat_enabled = 0;
 272                memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat));
 273                for_each_input_queue(irq_ptr, q, i)
 274                        memset(&q->q_stats, 0, sizeof(q->q_stats));
 275                for_each_output_queue(irq_ptr, q, i)
 276                        memset(&q->q_stats, 0, sizeof(q->q_stats));
 277                break;
 278        case 1:
 279                irq_ptr->perf_stat_enabled = 1;
 280                break;
 281        }
 282        return count;
 283}
 284
 285static int qperf_seq_open(struct inode *inode, struct file *filp)
 286{
 287        return single_open(filp, qperf_show,
 288                           file_inode(filp)->i_private);
 289}
 290
 291static const struct file_operations debugfs_perf_fops = {
 292        .owner   = THIS_MODULE,
 293        .open    = qperf_seq_open,
 294        .read    = seq_read,
 295        .write   = qperf_seq_write,
 296        .llseek  = seq_lseek,
 297        .release = single_release,
 298};
 299
 300static void setup_debugfs_entry(struct qdio_q *q)
 301{
 302        char name[QDIO_DEBUGFS_NAME_LEN];
 303
 304        snprintf(name, QDIO_DEBUGFS_NAME_LEN, "%s_%d",
 305                 q->is_input_q ? "input" : "output",
 306                 q->nr);
 307        q->debugfs_q = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR,
 308                                q->irq_ptr->debugfs_dev, q, &debugfs_fops);
 309        if (IS_ERR(q->debugfs_q))
 310                q->debugfs_q = NULL;
 311}
 312
 313void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev)
 314{
 315        struct qdio_q *q;
 316        int i;
 317
 318        irq_ptr->debugfs_dev = debugfs_create_dir(dev_name(&cdev->dev),
 319                                                  debugfs_root);
 320        if (IS_ERR(irq_ptr->debugfs_dev))
 321                irq_ptr->debugfs_dev = NULL;
 322
 323        irq_ptr->debugfs_perf = debugfs_create_file("statistics",
 324                                S_IFREG | S_IRUGO | S_IWUSR,
 325                                irq_ptr->debugfs_dev, irq_ptr,
 326                                &debugfs_perf_fops);
 327        if (IS_ERR(irq_ptr->debugfs_perf))
 328                irq_ptr->debugfs_perf = NULL;
 329
 330        for_each_input_queue(irq_ptr, q, i)
 331                setup_debugfs_entry(q);
 332        for_each_output_queue(irq_ptr, q, i)
 333                setup_debugfs_entry(q);
 334}
 335
 336void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr)
 337{
 338        struct qdio_q *q;
 339        int i;
 340
 341        for_each_input_queue(irq_ptr, q, i)
 342                debugfs_remove(q->debugfs_q);
 343        for_each_output_queue(irq_ptr, q, i)
 344                debugfs_remove(q->debugfs_q);
 345        debugfs_remove(irq_ptr->debugfs_perf);
 346        debugfs_remove(irq_ptr->debugfs_dev);
 347}
 348
 349int __init qdio_debug_init(void)
 350{
 351        debugfs_root = debugfs_create_dir("qdio", NULL);
 352
 353        qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16);
 354        debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
 355        debug_set_level(qdio_dbf_setup, DBF_INFO);
 356        DBF_EVENT("dbf created\n");
 357
 358        qdio_dbf_error = debug_register("qdio_error", 4, 1, 16);
 359        debug_register_view(qdio_dbf_error, &debug_hex_ascii_view);
 360        debug_set_level(qdio_dbf_error, DBF_INFO);
 361        DBF_ERROR("dbf created\n");
 362        return 0;
 363}
 364
 365void qdio_debug_exit(void)
 366{
 367        qdio_clear_dbf_list();
 368        debugfs_remove(debugfs_root);
 369        debug_unregister(qdio_dbf_setup);
 370        debug_unregister(qdio_dbf_error);
 371}
 372