Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
0004  *          Horst Hummel <Horst.Hummel@de.ibm.com>
0005  *          Carsten Otte <Cotte@de.ibm.com>
0006  *          Martin Schwidefsky <schwidefsky@de.ibm.com>
0007  * Bugreports.to..: <Linux390@de.ibm.com>
0008  * Copyright IBM Corp. 1999, 2002
0009  *
0010  * /proc interface for the dasd driver.
0011  *
0012  */
0013 
0014 #define KMSG_COMPONENT "dasd"
0015 
0016 #include <linux/ctype.h>
0017 #include <linux/slab.h>
0018 #include <linux/string.h>
0019 #include <linux/seq_file.h>
0020 #include <linux/vmalloc.h>
0021 #include <linux/proc_fs.h>
0022 
0023 #include <asm/debug.h>
0024 #include <linux/uaccess.h>
0025 
0026 /* This is ugly... */
0027 #define PRINTK_HEADER "dasd_proc:"
0028 
0029 #include "dasd_int.h"
0030 
0031 static struct proc_dir_entry *dasd_proc_root_entry = NULL;
0032 static struct proc_dir_entry *dasd_devices_entry = NULL;
0033 static struct proc_dir_entry *dasd_statistics_entry = NULL;
0034 
0035 static int
0036 dasd_devices_show(struct seq_file *m, void *v)
0037 {
0038     struct dasd_device *device;
0039     struct dasd_block *block;
0040     char *substr;
0041 
0042     device = dasd_device_from_devindex((unsigned long) v - 1);
0043     if (IS_ERR(device))
0044         return 0;
0045     if (device->block)
0046         block = device->block;
0047     else {
0048         dasd_put_device(device);
0049         return 0;
0050     }
0051     /* Print device number. */
0052     seq_printf(m, "%s", dev_name(&device->cdev->dev));
0053     /* Print discipline string. */
0054     if (device->discipline != NULL)
0055         seq_printf(m, "(%s)", device->discipline->name);
0056     else
0057         seq_printf(m, "(none)");
0058     /* Print kdev. */
0059     if (block->gdp)
0060         seq_printf(m, " at (%3d:%6d)",
0061                MAJOR(disk_devt(block->gdp)),
0062                MINOR(disk_devt(block->gdp)));
0063     else
0064         seq_printf(m, "  at (???:??????)");
0065     /* Print device name. */
0066     if (block->gdp)
0067         seq_printf(m, " is %-8s", block->gdp->disk_name);
0068     else
0069         seq_printf(m, " is ????????");
0070     /* Print devices features. */
0071     substr = (device->features & DASD_FEATURE_READONLY) ? "(ro)" : " ";
0072     seq_printf(m, "%4s: ", substr);
0073     /* Print device status information. */
0074     switch (device->state) {
0075     case DASD_STATE_NEW:
0076         seq_printf(m, "new");
0077         break;
0078     case DASD_STATE_KNOWN:
0079         seq_printf(m, "detected");
0080         break;
0081     case DASD_STATE_BASIC:
0082         seq_printf(m, "basic");
0083         break;
0084     case DASD_STATE_UNFMT:
0085         seq_printf(m, "unformatted");
0086         break;
0087     case DASD_STATE_READY:
0088     case DASD_STATE_ONLINE:
0089         seq_printf(m, "active ");
0090         if (dasd_check_blocksize(block->bp_block))
0091             seq_printf(m, "n/f   ");
0092         else
0093             seq_printf(m,
0094                    "at blocksize: %u, %lu blocks, %lu MB",
0095                    block->bp_block, block->blocks,
0096                    ((block->bp_block >> 9) *
0097                     block->blocks) >> 11);
0098         break;
0099     default:
0100         seq_printf(m, "no stat");
0101         break;
0102     }
0103     dasd_put_device(device);
0104     if (dasd_probeonly)
0105         seq_printf(m, "(probeonly)");
0106     seq_printf(m, "\n");
0107     return 0;
0108 }
0109 
0110 static void *dasd_devices_start(struct seq_file *m, loff_t *pos)
0111 {
0112     if (*pos >= dasd_max_devindex)
0113         return NULL;
0114     return (void *)((unsigned long) *pos + 1);
0115 }
0116 
0117 static void *dasd_devices_next(struct seq_file *m, void *v, loff_t *pos)
0118 {
0119     ++*pos;
0120     return dasd_devices_start(m, pos);
0121 }
0122 
0123 static void dasd_devices_stop(struct seq_file *m, void *v)
0124 {
0125 }
0126 
0127 static const struct seq_operations dasd_devices_seq_ops = {
0128     .start      = dasd_devices_start,
0129     .next       = dasd_devices_next,
0130     .stop       = dasd_devices_stop,
0131     .show       = dasd_devices_show,
0132 };
0133 
0134 #ifdef CONFIG_DASD_PROFILE
0135 static int dasd_stats_all_block_on(void)
0136 {
0137     int i, rc;
0138     struct dasd_device *device;
0139 
0140     rc = 0;
0141     for (i = 0; i < dasd_max_devindex; ++i) {
0142         device = dasd_device_from_devindex(i);
0143         if (IS_ERR(device))
0144             continue;
0145         if (device->block)
0146             rc = dasd_profile_on(&device->block->profile);
0147         dasd_put_device(device);
0148         if (rc)
0149             return rc;
0150     }
0151     return 0;
0152 }
0153 
0154 static void dasd_stats_all_block_off(void)
0155 {
0156     int i;
0157     struct dasd_device *device;
0158 
0159     for (i = 0; i < dasd_max_devindex; ++i) {
0160         device = dasd_device_from_devindex(i);
0161         if (IS_ERR(device))
0162             continue;
0163         if (device->block)
0164             dasd_profile_off(&device->block->profile);
0165         dasd_put_device(device);
0166     }
0167 }
0168 
0169 static void dasd_stats_all_block_reset(void)
0170 {
0171     int i;
0172     struct dasd_device *device;
0173 
0174     for (i = 0; i < dasd_max_devindex; ++i) {
0175         device = dasd_device_from_devindex(i);
0176         if (IS_ERR(device))
0177             continue;
0178         if (device->block)
0179             dasd_profile_reset(&device->block->profile);
0180         dasd_put_device(device);
0181     }
0182 }
0183 
0184 static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor)
0185 {
0186     int i;
0187 
0188     for (i = 0; i < 32; i++) {
0189         seq_printf(m, "%7d ", array[i] / factor);
0190         if (i == 15)
0191             seq_putc(m, '\n');
0192     }
0193     seq_putc(m, '\n');
0194 }
0195 #endif /* CONFIG_DASD_PROFILE */
0196 
0197 static int dasd_stats_proc_show(struct seq_file *m, void *v)
0198 {
0199 #ifdef CONFIG_DASD_PROFILE
0200     struct dasd_profile_info *prof;
0201     int factor;
0202 
0203     spin_lock_bh(&dasd_global_profile.lock);
0204     prof = dasd_global_profile.data;
0205     if (!prof) {
0206         spin_unlock_bh(&dasd_global_profile.lock);
0207         seq_printf(m, "Statistics are off - they might be "
0208                     "switched on using 'echo set on > "
0209                     "/proc/dasd/statistics'\n");
0210         return 0;
0211     }
0212 
0213     /* prevent counter 'overflow' on output */
0214     for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
0215          factor *= 10);
0216 
0217     seq_printf(m, "%d dasd I/O requests\n", prof->dasd_io_reqs);
0218     seq_printf(m, "with %u sectors(512B each)\n",
0219                prof->dasd_io_sects);
0220     seq_printf(m, "Scale Factor is  %d\n", factor);
0221     seq_printf(m,
0222                "   __<4    ___8    __16    __32    __64    _128 "
0223                "   _256    _512    __1k    __2k    __4k    __8k "
0224                "   _16k    _32k    _64k    128k\n");
0225     seq_printf(m,
0226                "   _256    _512    __1M    __2M    __4M    __8M "
0227                "   _16M    _32M    _64M    128M    256M    512M "
0228                "   __1G    __2G    __4G " "   _>4G\n");
0229 
0230     seq_printf(m, "Histogram of sizes (512B secs)\n");
0231     dasd_statistics_array(m, prof->dasd_io_secs, factor);
0232     seq_printf(m, "Histogram of I/O times (microseconds)\n");
0233     dasd_statistics_array(m, prof->dasd_io_times, factor);
0234     seq_printf(m, "Histogram of I/O times per sector\n");
0235     dasd_statistics_array(m, prof->dasd_io_timps, factor);
0236     seq_printf(m, "Histogram of I/O time till ssch\n");
0237     dasd_statistics_array(m, prof->dasd_io_time1, factor);
0238     seq_printf(m, "Histogram of I/O time between ssch and irq\n");
0239     dasd_statistics_array(m, prof->dasd_io_time2, factor);
0240     seq_printf(m, "Histogram of I/O time between ssch "
0241                 "and irq per sector\n");
0242     dasd_statistics_array(m, prof->dasd_io_time2ps, factor);
0243     seq_printf(m, "Histogram of I/O time between irq and end\n");
0244     dasd_statistics_array(m, prof->dasd_io_time3, factor);
0245     seq_printf(m, "# of req in chanq at enqueuing (1..32) \n");
0246     dasd_statistics_array(m, prof->dasd_io_nr_req, factor);
0247     spin_unlock_bh(&dasd_global_profile.lock);
0248 #else
0249     seq_printf(m, "Statistics are not activated in this kernel\n");
0250 #endif
0251     return 0;
0252 }
0253 
0254 static int dasd_stats_proc_open(struct inode *inode, struct file *file)
0255 {
0256     return single_open(file, dasd_stats_proc_show, NULL);
0257 }
0258 
0259 static ssize_t dasd_stats_proc_write(struct file *file,
0260         const char __user *user_buf, size_t user_len, loff_t *pos)
0261 {
0262 #ifdef CONFIG_DASD_PROFILE
0263     char *buffer, *str;
0264     int rc;
0265 
0266     if (user_len > 65536)
0267         user_len = 65536;
0268     buffer = dasd_get_user_string(user_buf, user_len);
0269     if (IS_ERR(buffer))
0270         return PTR_ERR(buffer);
0271 
0272     /* check for valid verbs */
0273     str = skip_spaces(buffer);
0274     if (strncmp(str, "set", 3) == 0 && isspace(str[3])) {
0275         /* 'set xxx' was given */
0276         str = skip_spaces(str + 4);
0277         if (strcmp(str, "on") == 0) {
0278             /* switch on statistics profiling */
0279             rc = dasd_stats_all_block_on();
0280             if (rc) {
0281                 dasd_stats_all_block_off();
0282                 goto out_error;
0283             }
0284             rc = dasd_profile_on(&dasd_global_profile);
0285             if (rc) {
0286                 dasd_stats_all_block_off();
0287                 goto out_error;
0288             }
0289             dasd_profile_reset(&dasd_global_profile);
0290             dasd_global_profile_level = DASD_PROFILE_ON;
0291             pr_info("The statistics feature has been switched "
0292                 "on\n");
0293         } else if (strcmp(str, "off") == 0) {
0294             /* switch off statistics profiling */
0295             dasd_global_profile_level = DASD_PROFILE_OFF;
0296             dasd_profile_off(&dasd_global_profile);
0297             dasd_stats_all_block_off();
0298             pr_info("The statistics feature has been switched "
0299                 "off\n");
0300         } else
0301             goto out_parse_error;
0302     } else if (strncmp(str, "reset", 5) == 0) {
0303         /* reset the statistics */
0304         dasd_profile_reset(&dasd_global_profile);
0305         dasd_stats_all_block_reset();
0306         pr_info("The statistics have been reset\n");
0307     } else
0308         goto out_parse_error;
0309     vfree(buffer);
0310     return user_len;
0311 out_parse_error:
0312     rc = -EINVAL;
0313     pr_warn("%s is not a supported value for /proc/dasd/statistics\n", str);
0314 out_error:
0315     vfree(buffer);
0316     return rc;
0317 #else
0318     pr_warn("/proc/dasd/statistics: is not activated in this kernel\n");
0319     return user_len;
0320 #endif              /* CONFIG_DASD_PROFILE */
0321 }
0322 
0323 static const struct proc_ops dasd_stats_proc_ops = {
0324     .proc_open  = dasd_stats_proc_open,
0325     .proc_read  = seq_read,
0326     .proc_lseek = seq_lseek,
0327     .proc_release   = single_release,
0328     .proc_write = dasd_stats_proc_write,
0329 };
0330 
0331 /*
0332  * Create dasd proc-fs entries.
0333  * In case creation failed, cleanup and return -ENOENT.
0334  */
0335 int
0336 dasd_proc_init(void)
0337 {
0338     dasd_proc_root_entry = proc_mkdir("dasd", NULL);
0339     if (!dasd_proc_root_entry)
0340         goto out_nodasd;
0341     dasd_devices_entry = proc_create_seq("devices", 0444,
0342                      dasd_proc_root_entry,
0343                      &dasd_devices_seq_ops);
0344     if (!dasd_devices_entry)
0345         goto out_nodevices;
0346     dasd_statistics_entry = proc_create("statistics",
0347                         S_IFREG | S_IRUGO | S_IWUSR,
0348                         dasd_proc_root_entry,
0349                         &dasd_stats_proc_ops);
0350     if (!dasd_statistics_entry)
0351         goto out_nostatistics;
0352     return 0;
0353 
0354  out_nostatistics:
0355     remove_proc_entry("devices", dasd_proc_root_entry);
0356  out_nodevices:
0357     remove_proc_entry("dasd", NULL);
0358  out_nodasd:
0359     return -ENOENT;
0360 }
0361 
0362 void
0363 dasd_proc_exit(void)
0364 {
0365     remove_proc_entry("devices", dasd_proc_root_entry);
0366     remove_proc_entry("statistics", dasd_proc_root_entry);
0367     remove_proc_entry("dasd", NULL);
0368 }