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