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