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