linux/drivers/s390/block/dasd_proc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
   4 *                  Horst Hummel <Horst.Hummel@de.ibm.com>
   5 *                  Carsten Otte <Cotte@de.ibm.com>
   6 *                  Martin Schwidefsky <schwidefsky@de.ibm.com>
   7 * Bugreports.to..: <Linux390@de.ibm.com>
   8 * Copyright IBM Corp. 1999, 2002
   9 *
  10 * /proc interface for the dasd driver.
  11 *
  12 */
  13
  14#define KMSG_COMPONENT "dasd"
  15
  16#include <linux/ctype.h>
  17#include <linux/slab.h>
  18#include <linux/string.h>
  19#include <linux/seq_file.h>
  20#include <linux/vmalloc.h>
  21#include <linux/proc_fs.h>
  22
  23#include <asm/debug.h>
  24#include <linux/uaccess.h>
  25
  26/* This is ugly... */
  27#define PRINTK_HEADER "dasd_proc:"
  28
  29#include "dasd_int.h"
  30
  31static struct proc_dir_entry *dasd_proc_root_entry = NULL;
  32static struct proc_dir_entry *dasd_devices_entry = NULL;
  33static struct proc_dir_entry *dasd_statistics_entry = NULL;
  34
  35static int
  36dasd_devices_show(struct seq_file *m, void *v)
  37{
  38        struct dasd_device *device;
  39        struct dasd_block *block;
  40        char *substr;
  41
  42        device = dasd_device_from_devindex((unsigned long) v - 1);
  43        if (IS_ERR(device))
  44                return 0;
  45        if (device->block)
  46                block = device->block;
  47        else {
  48                dasd_put_device(device);
  49                return 0;
  50        }
  51        /* Print device number. */
  52        seq_printf(m, "%s", dev_name(&device->cdev->dev));
  53        /* Print discipline string. */
  54        if (device->discipline != NULL)
  55                seq_printf(m, "(%s)", device->discipline->name);
  56        else
  57                seq_printf(m, "(none)");
  58        /* Print kdev. */
  59        if (block->gdp)
  60                seq_printf(m, " at (%3d:%6d)",
  61                           MAJOR(disk_devt(block->gdp)),
  62                           MINOR(disk_devt(block->gdp)));
  63        else
  64                seq_printf(m, "  at (???:??????)");
  65        /* Print device name. */
  66        if (block->gdp)
  67                seq_printf(m, " is %-8s", block->gdp->disk_name);
  68        else
  69                seq_printf(m, " is ????????");
  70        /* Print devices features. */
  71        substr = (device->features & DASD_FEATURE_READONLY) ? "(ro)" : " ";
  72        seq_printf(m, "%4s: ", substr);
  73        /* Print device status information. */
  74        switch (device->state) {
  75        case DASD_STATE_NEW:
  76                seq_printf(m, "new");
  77                break;
  78        case DASD_STATE_KNOWN:
  79                seq_printf(m, "detected");
  80                break;
  81        case DASD_STATE_BASIC:
  82                seq_printf(m, "basic");
  83                break;
  84        case DASD_STATE_UNFMT:
  85                seq_printf(m, "unformatted");
  86                break;
  87        case DASD_STATE_READY:
  88        case DASD_STATE_ONLINE:
  89                seq_printf(m, "active ");
  90                if (dasd_check_blocksize(block->bp_block))
  91                        seq_printf(m, "n/f       ");
  92                else
  93                        seq_printf(m,
  94                                   "at blocksize: %u, %lu blocks, %lu MB",
  95                                   block->bp_block, block->blocks,
  96                                   ((block->bp_block >> 9) *
  97                                    block->blocks) >> 11);
  98                break;
  99        default:
 100                seq_printf(m, "no stat");
 101                break;
 102        }
 103        dasd_put_device(device);
 104        if (dasd_probeonly)
 105                seq_printf(m, "(probeonly)");
 106        seq_printf(m, "\n");
 107        return 0;
 108}
 109
 110static void *dasd_devices_start(struct seq_file *m, loff_t *pos)
 111{
 112        if (*pos >= dasd_max_devindex)
 113                return NULL;
 114        return (void *)((unsigned long) *pos + 1);
 115}
 116
 117static void *dasd_devices_next(struct seq_file *m, void *v, loff_t *pos)
 118{
 119        ++*pos;
 120        return dasd_devices_start(m, pos);
 121}
 122
 123static void dasd_devices_stop(struct seq_file *m, void *v)
 124{
 125}
 126
 127static const struct seq_operations dasd_devices_seq_ops = {
 128        .start          = dasd_devices_start,
 129        .next           = dasd_devices_next,
 130        .stop           = dasd_devices_stop,
 131        .show           = dasd_devices_show,
 132};
 133
 134#ifdef CONFIG_DASD_PROFILE
 135static int dasd_stats_all_block_on(void)
 136{
 137        int i, rc;
 138        struct dasd_device *device;
 139
 140        rc = 0;
 141        for (i = 0; i < dasd_max_devindex; ++i) {
 142                device = dasd_device_from_devindex(i);
 143                if (IS_ERR(device))
 144                        continue;
 145                if (device->block)
 146                        rc = dasd_profile_on(&device->block->profile);
 147                dasd_put_device(device);
 148                if (rc)
 149                        return rc;
 150        }
 151        return 0;
 152}
 153
 154static void dasd_stats_all_block_off(void)
 155{
 156        int i;
 157        struct dasd_device *device;
 158
 159        for (i = 0; i < dasd_max_devindex; ++i) {
 160                device = dasd_device_from_devindex(i);
 161                if (IS_ERR(device))
 162                        continue;
 163                if (device->block)
 164                        dasd_profile_off(&device->block->profile);
 165                dasd_put_device(device);
 166        }
 167}
 168
 169static void dasd_stats_all_block_reset(void)
 170{
 171        int i;
 172        struct dasd_device *device;
 173
 174        for (i = 0; i < dasd_max_devindex; ++i) {
 175                device = dasd_device_from_devindex(i);
 176                if (IS_ERR(device))
 177                        continue;
 178                if (device->block)
 179                        dasd_profile_reset(&device->block->profile);
 180                dasd_put_device(device);
 181        }
 182}
 183
 184static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor)
 185{
 186        int i;
 187
 188        for (i = 0; i < 32; i++) {
 189                seq_printf(m, "%7d ", array[i] / factor);
 190                if (i == 15)
 191                        seq_putc(m, '\n');
 192        }
 193        seq_putc(m, '\n');
 194}
 195#endif /* CONFIG_DASD_PROFILE */
 196
 197static int dasd_stats_proc_show(struct seq_file *m, void *v)
 198{
 199#ifdef CONFIG_DASD_PROFILE
 200        struct dasd_profile_info *prof;
 201        int factor;
 202
 203        spin_lock_bh(&dasd_global_profile.lock);
 204        prof = dasd_global_profile.data;
 205        if (!prof) {
 206                spin_unlock_bh(&dasd_global_profile.lock);
 207                seq_printf(m, "Statistics are off - they might be "
 208                                    "switched on using 'echo set on > "
 209                                    "/proc/dasd/statistics'\n");
 210                return 0;
 211        }
 212
 213        /* prevent counter 'overflow' on output */
 214        for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
 215             factor *= 10);
 216
 217        seq_printf(m, "%d dasd I/O requests\n", prof->dasd_io_reqs);
 218        seq_printf(m, "with %u sectors(512B each)\n",
 219                       prof->dasd_io_sects);
 220        seq_printf(m, "Scale Factor is  %d\n", factor);
 221        seq_printf(m,
 222                       "   __<4    ___8    __16    __32    __64    _128 "
 223                       "   _256    _512    __1k    __2k    __4k    __8k "
 224                       "   _16k    _32k    _64k    128k\n");
 225        seq_printf(m,
 226                       "   _256    _512    __1M    __2M    __4M    __8M "
 227                       "   _16M    _32M    _64M    128M    256M    512M "
 228                       "   __1G    __2G    __4G " "   _>4G\n");
 229
 230        seq_printf(m, "Histogram of sizes (512B secs)\n");
 231        dasd_statistics_array(m, prof->dasd_io_secs, factor);
 232        seq_printf(m, "Histogram of I/O times (microseconds)\n");
 233        dasd_statistics_array(m, prof->dasd_io_times, factor);
 234        seq_printf(m, "Histogram of I/O times per sector\n");
 235        dasd_statistics_array(m, prof->dasd_io_timps, factor);
 236        seq_printf(m, "Histogram of I/O time till ssch\n");
 237        dasd_statistics_array(m, prof->dasd_io_time1, factor);
 238        seq_printf(m, "Histogram of I/O time between ssch and irq\n");
 239        dasd_statistics_array(m, prof->dasd_io_time2, factor);
 240        seq_printf(m, "Histogram of I/O time between ssch "
 241                            "and irq per sector\n");
 242        dasd_statistics_array(m, prof->dasd_io_time2ps, factor);
 243        seq_printf(m, "Histogram of I/O time between irq and end\n");
 244        dasd_statistics_array(m, prof->dasd_io_time3, factor);
 245        seq_printf(m, "# of req in chanq at enqueuing (1..32) \n");
 246        dasd_statistics_array(m, prof->dasd_io_nr_req, factor);
 247        spin_unlock_bh(&dasd_global_profile.lock);
 248#else
 249        seq_printf(m, "Statistics are not activated in this kernel\n");
 250#endif
 251        return 0;
 252}
 253
 254static int dasd_stats_proc_open(struct inode *inode, struct file *file)
 255{
 256        return single_open(file, dasd_stats_proc_show, NULL);
 257}
 258
 259static ssize_t dasd_stats_proc_write(struct file *file,
 260                const char __user *user_buf, size_t user_len, loff_t *pos)
 261{
 262#ifdef CONFIG_DASD_PROFILE
 263        char *buffer, *str;
 264        int rc;
 265
 266        if (user_len > 65536)
 267                user_len = 65536;
 268        buffer = dasd_get_user_string(user_buf, user_len);
 269        if (IS_ERR(buffer))
 270                return PTR_ERR(buffer);
 271
 272        /* check for valid verbs */
 273        str = skip_spaces(buffer);
 274        if (strncmp(str, "set", 3) == 0 && isspace(str[3])) {
 275                /* 'set xxx' was given */
 276                str = skip_spaces(str + 4);
 277                if (strcmp(str, "on") == 0) {
 278                        /* switch on statistics profiling */
 279                        rc = dasd_stats_all_block_on();
 280                        if (rc) {
 281                                dasd_stats_all_block_off();
 282                                goto out_error;
 283                        }
 284                        rc = dasd_profile_on(&dasd_global_profile);
 285                        if (rc) {
 286                                dasd_stats_all_block_off();
 287                                goto out_error;
 288                        }
 289                        dasd_profile_reset(&dasd_global_profile);
 290                        dasd_global_profile_level = DASD_PROFILE_ON;
 291                        pr_info("The statistics feature has been switched "
 292                                "on\n");
 293                } else if (strcmp(str, "off") == 0) {
 294                        /* switch off statistics profiling */
 295                        dasd_global_profile_level = DASD_PROFILE_OFF;
 296                        dasd_profile_off(&dasd_global_profile);
 297                        dasd_stats_all_block_off();
 298                        pr_info("The statistics feature has been switched "
 299                                "off\n");
 300                } else
 301                        goto out_parse_error;
 302        } else if (strncmp(str, "reset", 5) == 0) {
 303                /* reset the statistics */
 304                dasd_profile_reset(&dasd_global_profile);
 305                dasd_stats_all_block_reset();
 306                pr_info("The statistics have been reset\n");
 307        } else
 308                goto out_parse_error;
 309        vfree(buffer);
 310        return user_len;
 311out_parse_error:
 312        rc = -EINVAL;
 313        pr_warn("%s is not a supported value for /proc/dasd/statistics\n", str);
 314out_error:
 315        vfree(buffer);
 316        return rc;
 317#else
 318        pr_warn("/proc/dasd/statistics: is not activated in this kernel\n");
 319        return user_len;
 320#endif                          /* CONFIG_DASD_PROFILE */
 321}
 322
 323static const struct proc_ops dasd_stats_proc_ops = {
 324        .proc_open      = dasd_stats_proc_open,
 325        .proc_read      = seq_read,
 326        .proc_lseek     = seq_lseek,
 327        .proc_release   = single_release,
 328        .proc_write     = dasd_stats_proc_write,
 329};
 330
 331/*
 332 * Create dasd proc-fs entries.
 333 * In case creation failed, cleanup and return -ENOENT.
 334 */
 335int
 336dasd_proc_init(void)
 337{
 338        dasd_proc_root_entry = proc_mkdir("dasd", NULL);
 339        if (!dasd_proc_root_entry)
 340                goto out_nodasd;
 341        dasd_devices_entry = proc_create_seq("devices", 0444,
 342                                         dasd_proc_root_entry,
 343                                         &dasd_devices_seq_ops);
 344        if (!dasd_devices_entry)
 345                goto out_nodevices;
 346        dasd_statistics_entry = proc_create("statistics",
 347                                            S_IFREG | S_IRUGO | S_IWUSR,
 348                                            dasd_proc_root_entry,
 349                                            &dasd_stats_proc_ops);
 350        if (!dasd_statistics_entry)
 351                goto out_nostatistics;
 352        return 0;
 353
 354 out_nostatistics:
 355        remove_proc_entry("devices", dasd_proc_root_entry);
 356 out_nodevices:
 357        remove_proc_entry("dasd", NULL);
 358 out_nodasd:
 359        return -ENOENT;
 360}
 361
 362void
 363dasd_proc_exit(void)
 364{
 365        remove_proc_entry("devices", dasd_proc_root_entry);
 366        remove_proc_entry("statistics", dasd_proc_root_entry);
 367        remove_proc_entry("dasd", NULL);
 368}
 369