162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 462306a36Sopenharmony_ci * Horst Hummel <Horst.Hummel@de.ibm.com> 562306a36Sopenharmony_ci * Carsten Otte <Cotte@de.ibm.com> 662306a36Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 762306a36Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com> 862306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2002 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * /proc interface for the dasd driver. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/ctype.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/string.h> 1962306a36Sopenharmony_ci#include <linux/seq_file.h> 2062306a36Sopenharmony_ci#include <linux/vmalloc.h> 2162306a36Sopenharmony_ci#include <linux/proc_fs.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/debug.h> 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* This is ugly... */ 2762306a36Sopenharmony_ci#define PRINTK_HEADER "dasd_proc:" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "dasd_int.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct proc_dir_entry *dasd_proc_root_entry = NULL; 3262306a36Sopenharmony_cistatic struct proc_dir_entry *dasd_devices_entry = NULL; 3362306a36Sopenharmony_cistatic struct proc_dir_entry *dasd_statistics_entry = NULL; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int 3662306a36Sopenharmony_cidasd_devices_show(struct seq_file *m, void *v) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct dasd_device *device; 3962306a36Sopenharmony_ci struct dasd_block *block; 4062306a36Sopenharmony_ci char *substr; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci device = dasd_device_from_devindex((unsigned long) v - 1); 4362306a36Sopenharmony_ci if (IS_ERR(device)) 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci if (device->block) 4662306a36Sopenharmony_ci block = device->block; 4762306a36Sopenharmony_ci else { 4862306a36Sopenharmony_ci dasd_put_device(device); 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci /* Print device number. */ 5262306a36Sopenharmony_ci seq_printf(m, "%s", dev_name(&device->cdev->dev)); 5362306a36Sopenharmony_ci /* Print discipline string. */ 5462306a36Sopenharmony_ci if (device->discipline != NULL) 5562306a36Sopenharmony_ci seq_printf(m, "(%s)", device->discipline->name); 5662306a36Sopenharmony_ci else 5762306a36Sopenharmony_ci seq_printf(m, "(none)"); 5862306a36Sopenharmony_ci /* Print kdev. */ 5962306a36Sopenharmony_ci if (block->gdp) 6062306a36Sopenharmony_ci seq_printf(m, " at (%3d:%6d)", 6162306a36Sopenharmony_ci MAJOR(disk_devt(block->gdp)), 6262306a36Sopenharmony_ci MINOR(disk_devt(block->gdp))); 6362306a36Sopenharmony_ci else 6462306a36Sopenharmony_ci seq_printf(m, " at (???:??????)"); 6562306a36Sopenharmony_ci /* Print device name. */ 6662306a36Sopenharmony_ci if (block->gdp) 6762306a36Sopenharmony_ci seq_printf(m, " is %-8s", block->gdp->disk_name); 6862306a36Sopenharmony_ci else 6962306a36Sopenharmony_ci seq_printf(m, " is ????????"); 7062306a36Sopenharmony_ci /* Print devices features. */ 7162306a36Sopenharmony_ci substr = (device->features & DASD_FEATURE_READONLY) ? "(ro)" : " "; 7262306a36Sopenharmony_ci seq_printf(m, "%4s: ", substr); 7362306a36Sopenharmony_ci /* Print device status information. */ 7462306a36Sopenharmony_ci switch (device->state) { 7562306a36Sopenharmony_ci case DASD_STATE_NEW: 7662306a36Sopenharmony_ci seq_printf(m, "new"); 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci case DASD_STATE_KNOWN: 7962306a36Sopenharmony_ci seq_printf(m, "detected"); 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case DASD_STATE_BASIC: 8262306a36Sopenharmony_ci seq_printf(m, "basic"); 8362306a36Sopenharmony_ci break; 8462306a36Sopenharmony_ci case DASD_STATE_UNFMT: 8562306a36Sopenharmony_ci seq_printf(m, "unformatted"); 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci case DASD_STATE_READY: 8862306a36Sopenharmony_ci case DASD_STATE_ONLINE: 8962306a36Sopenharmony_ci seq_printf(m, "active "); 9062306a36Sopenharmony_ci if (dasd_check_blocksize(block->bp_block)) 9162306a36Sopenharmony_ci seq_printf(m, "n/f "); 9262306a36Sopenharmony_ci else 9362306a36Sopenharmony_ci seq_printf(m, 9462306a36Sopenharmony_ci "at blocksize: %u, %lu blocks, %lu MB", 9562306a36Sopenharmony_ci block->bp_block, block->blocks, 9662306a36Sopenharmony_ci ((block->bp_block >> 9) * 9762306a36Sopenharmony_ci block->blocks) >> 11); 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci default: 10062306a36Sopenharmony_ci seq_printf(m, "no stat"); 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci dasd_put_device(device); 10462306a36Sopenharmony_ci if (dasd_probeonly) 10562306a36Sopenharmony_ci seq_printf(m, "(probeonly)"); 10662306a36Sopenharmony_ci seq_printf(m, "\n"); 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void *dasd_devices_start(struct seq_file *m, loff_t *pos) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci if (*pos >= dasd_max_devindex) 11362306a36Sopenharmony_ci return NULL; 11462306a36Sopenharmony_ci return (void *)((unsigned long) *pos + 1); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void *dasd_devices_next(struct seq_file *m, void *v, loff_t *pos) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci ++*pos; 12062306a36Sopenharmony_ci return dasd_devices_start(m, pos); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void dasd_devices_stop(struct seq_file *m, void *v) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct seq_operations dasd_devices_seq_ops = { 12862306a36Sopenharmony_ci .start = dasd_devices_start, 12962306a36Sopenharmony_ci .next = dasd_devices_next, 13062306a36Sopenharmony_ci .stop = dasd_devices_stop, 13162306a36Sopenharmony_ci .show = dasd_devices_show, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#ifdef CONFIG_DASD_PROFILE 13562306a36Sopenharmony_cistatic int dasd_stats_all_block_on(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int i, rc; 13862306a36Sopenharmony_ci struct dasd_device *device; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci rc = 0; 14162306a36Sopenharmony_ci for (i = 0; i < dasd_max_devindex; ++i) { 14262306a36Sopenharmony_ci device = dasd_device_from_devindex(i); 14362306a36Sopenharmony_ci if (IS_ERR(device)) 14462306a36Sopenharmony_ci continue; 14562306a36Sopenharmony_ci if (device->block) 14662306a36Sopenharmony_ci rc = dasd_profile_on(&device->block->profile); 14762306a36Sopenharmony_ci dasd_put_device(device); 14862306a36Sopenharmony_ci if (rc) 14962306a36Sopenharmony_ci return rc; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void dasd_stats_all_block_off(void) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci int i; 15762306a36Sopenharmony_ci struct dasd_device *device; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci for (i = 0; i < dasd_max_devindex; ++i) { 16062306a36Sopenharmony_ci device = dasd_device_from_devindex(i); 16162306a36Sopenharmony_ci if (IS_ERR(device)) 16262306a36Sopenharmony_ci continue; 16362306a36Sopenharmony_ci if (device->block) 16462306a36Sopenharmony_ci dasd_profile_off(&device->block->profile); 16562306a36Sopenharmony_ci dasd_put_device(device); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void dasd_stats_all_block_reset(void) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int i; 17262306a36Sopenharmony_ci struct dasd_device *device; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (i = 0; i < dasd_max_devindex; ++i) { 17562306a36Sopenharmony_ci device = dasd_device_from_devindex(i); 17662306a36Sopenharmony_ci if (IS_ERR(device)) 17762306a36Sopenharmony_ci continue; 17862306a36Sopenharmony_ci if (device->block) 17962306a36Sopenharmony_ci dasd_profile_reset(&device->block->profile); 18062306a36Sopenharmony_ci dasd_put_device(device); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 18962306a36Sopenharmony_ci seq_printf(m, "%7d ", array[i] / factor); 19062306a36Sopenharmony_ci if (i == 15) 19162306a36Sopenharmony_ci seq_putc(m, '\n'); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci seq_putc(m, '\n'); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci#endif /* CONFIG_DASD_PROFILE */ 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int dasd_stats_proc_show(struct seq_file *m, void *v) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci#ifdef CONFIG_DASD_PROFILE 20062306a36Sopenharmony_ci struct dasd_profile_info *prof; 20162306a36Sopenharmony_ci int factor; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci spin_lock_bh(&dasd_global_profile.lock); 20462306a36Sopenharmony_ci prof = dasd_global_profile.data; 20562306a36Sopenharmony_ci if (!prof) { 20662306a36Sopenharmony_ci spin_unlock_bh(&dasd_global_profile.lock); 20762306a36Sopenharmony_ci seq_printf(m, "Statistics are off - they might be " 20862306a36Sopenharmony_ci "switched on using 'echo set on > " 20962306a36Sopenharmony_ci "/proc/dasd/statistics'\n"); 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* prevent counter 'overflow' on output */ 21462306a36Sopenharmony_ci for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999; 21562306a36Sopenharmony_ci factor *= 10); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci seq_printf(m, "%d dasd I/O requests\n", prof->dasd_io_reqs); 21862306a36Sopenharmony_ci seq_printf(m, "with %u sectors(512B each)\n", 21962306a36Sopenharmony_ci prof->dasd_io_sects); 22062306a36Sopenharmony_ci seq_printf(m, "Scale Factor is %d\n", factor); 22162306a36Sopenharmony_ci seq_printf(m, 22262306a36Sopenharmony_ci " __<4 ___8 __16 __32 __64 _128 " 22362306a36Sopenharmony_ci " _256 _512 __1k __2k __4k __8k " 22462306a36Sopenharmony_ci " _16k _32k _64k 128k\n"); 22562306a36Sopenharmony_ci seq_printf(m, 22662306a36Sopenharmony_ci " _256 _512 __1M __2M __4M __8M " 22762306a36Sopenharmony_ci " _16M _32M _64M 128M 256M 512M " 22862306a36Sopenharmony_ci " __1G __2G __4G " " _>4G\n"); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci seq_printf(m, "Histogram of sizes (512B secs)\n"); 23162306a36Sopenharmony_ci dasd_statistics_array(m, prof->dasd_io_secs, factor); 23262306a36Sopenharmony_ci seq_printf(m, "Histogram of I/O times (microseconds)\n"); 23362306a36Sopenharmony_ci dasd_statistics_array(m, prof->dasd_io_times, factor); 23462306a36Sopenharmony_ci seq_printf(m, "Histogram of I/O times per sector\n"); 23562306a36Sopenharmony_ci dasd_statistics_array(m, prof->dasd_io_timps, factor); 23662306a36Sopenharmony_ci seq_printf(m, "Histogram of I/O time till ssch\n"); 23762306a36Sopenharmony_ci dasd_statistics_array(m, prof->dasd_io_time1, factor); 23862306a36Sopenharmony_ci seq_printf(m, "Histogram of I/O time between ssch and irq\n"); 23962306a36Sopenharmony_ci dasd_statistics_array(m, prof->dasd_io_time2, factor); 24062306a36Sopenharmony_ci seq_printf(m, "Histogram of I/O time between ssch " 24162306a36Sopenharmony_ci "and irq per sector\n"); 24262306a36Sopenharmony_ci dasd_statistics_array(m, prof->dasd_io_time2ps, factor); 24362306a36Sopenharmony_ci seq_printf(m, "Histogram of I/O time between irq and end\n"); 24462306a36Sopenharmony_ci dasd_statistics_array(m, prof->dasd_io_time3, factor); 24562306a36Sopenharmony_ci seq_printf(m, "# of req in chanq at enqueuing (1..32) \n"); 24662306a36Sopenharmony_ci dasd_statistics_array(m, prof->dasd_io_nr_req, factor); 24762306a36Sopenharmony_ci spin_unlock_bh(&dasd_global_profile.lock); 24862306a36Sopenharmony_ci#else 24962306a36Sopenharmony_ci seq_printf(m, "Statistics are not activated in this kernel\n"); 25062306a36Sopenharmony_ci#endif 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int dasd_stats_proc_open(struct inode *inode, struct file *file) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci return single_open(file, dasd_stats_proc_show, NULL); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic ssize_t dasd_stats_proc_write(struct file *file, 26062306a36Sopenharmony_ci const char __user *user_buf, size_t user_len, loff_t *pos) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci#ifdef CONFIG_DASD_PROFILE 26362306a36Sopenharmony_ci char *buffer, *str; 26462306a36Sopenharmony_ci int rc; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (user_len > 65536) 26762306a36Sopenharmony_ci user_len = 65536; 26862306a36Sopenharmony_ci buffer = dasd_get_user_string(user_buf, user_len); 26962306a36Sopenharmony_ci if (IS_ERR(buffer)) 27062306a36Sopenharmony_ci return PTR_ERR(buffer); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* check for valid verbs */ 27362306a36Sopenharmony_ci str = skip_spaces(buffer); 27462306a36Sopenharmony_ci if (strncmp(str, "set", 3) == 0 && isspace(str[3])) { 27562306a36Sopenharmony_ci /* 'set xxx' was given */ 27662306a36Sopenharmony_ci str = skip_spaces(str + 4); 27762306a36Sopenharmony_ci if (strcmp(str, "on") == 0) { 27862306a36Sopenharmony_ci /* switch on statistics profiling */ 27962306a36Sopenharmony_ci rc = dasd_stats_all_block_on(); 28062306a36Sopenharmony_ci if (rc) { 28162306a36Sopenharmony_ci dasd_stats_all_block_off(); 28262306a36Sopenharmony_ci goto out_error; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci rc = dasd_profile_on(&dasd_global_profile); 28562306a36Sopenharmony_ci if (rc) { 28662306a36Sopenharmony_ci dasd_stats_all_block_off(); 28762306a36Sopenharmony_ci goto out_error; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci dasd_profile_reset(&dasd_global_profile); 29062306a36Sopenharmony_ci dasd_global_profile_level = DASD_PROFILE_ON; 29162306a36Sopenharmony_ci pr_info("The statistics feature has been switched " 29262306a36Sopenharmony_ci "on\n"); 29362306a36Sopenharmony_ci } else if (strcmp(str, "off") == 0) { 29462306a36Sopenharmony_ci /* switch off statistics profiling */ 29562306a36Sopenharmony_ci dasd_global_profile_level = DASD_PROFILE_OFF; 29662306a36Sopenharmony_ci dasd_profile_off(&dasd_global_profile); 29762306a36Sopenharmony_ci dasd_stats_all_block_off(); 29862306a36Sopenharmony_ci pr_info("The statistics feature has been switched " 29962306a36Sopenharmony_ci "off\n"); 30062306a36Sopenharmony_ci } else 30162306a36Sopenharmony_ci goto out_parse_error; 30262306a36Sopenharmony_ci } else if (strncmp(str, "reset", 5) == 0) { 30362306a36Sopenharmony_ci /* reset the statistics */ 30462306a36Sopenharmony_ci dasd_profile_reset(&dasd_global_profile); 30562306a36Sopenharmony_ci dasd_stats_all_block_reset(); 30662306a36Sopenharmony_ci pr_info("The statistics have been reset\n"); 30762306a36Sopenharmony_ci } else 30862306a36Sopenharmony_ci goto out_parse_error; 30962306a36Sopenharmony_ci vfree(buffer); 31062306a36Sopenharmony_ci return user_len; 31162306a36Sopenharmony_ciout_parse_error: 31262306a36Sopenharmony_ci rc = -EINVAL; 31362306a36Sopenharmony_ci pr_warn("%s is not a supported value for /proc/dasd/statistics\n", str); 31462306a36Sopenharmony_ciout_error: 31562306a36Sopenharmony_ci vfree(buffer); 31662306a36Sopenharmony_ci return rc; 31762306a36Sopenharmony_ci#else 31862306a36Sopenharmony_ci pr_warn("/proc/dasd/statistics: is not activated in this kernel\n"); 31962306a36Sopenharmony_ci return user_len; 32062306a36Sopenharmony_ci#endif /* CONFIG_DASD_PROFILE */ 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic const struct proc_ops dasd_stats_proc_ops = { 32462306a36Sopenharmony_ci .proc_open = dasd_stats_proc_open, 32562306a36Sopenharmony_ci .proc_read = seq_read, 32662306a36Sopenharmony_ci .proc_lseek = seq_lseek, 32762306a36Sopenharmony_ci .proc_release = single_release, 32862306a36Sopenharmony_ci .proc_write = dasd_stats_proc_write, 32962306a36Sopenharmony_ci}; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/* 33262306a36Sopenharmony_ci * Create dasd proc-fs entries. 33362306a36Sopenharmony_ci * In case creation failed, cleanup and return -ENOENT. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ciint 33662306a36Sopenharmony_cidasd_proc_init(void) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci dasd_proc_root_entry = proc_mkdir("dasd", NULL); 33962306a36Sopenharmony_ci if (!dasd_proc_root_entry) 34062306a36Sopenharmony_ci goto out_nodasd; 34162306a36Sopenharmony_ci dasd_devices_entry = proc_create_seq("devices", 0444, 34262306a36Sopenharmony_ci dasd_proc_root_entry, 34362306a36Sopenharmony_ci &dasd_devices_seq_ops); 34462306a36Sopenharmony_ci if (!dasd_devices_entry) 34562306a36Sopenharmony_ci goto out_nodevices; 34662306a36Sopenharmony_ci dasd_statistics_entry = proc_create("statistics", 34762306a36Sopenharmony_ci S_IFREG | S_IRUGO | S_IWUSR, 34862306a36Sopenharmony_ci dasd_proc_root_entry, 34962306a36Sopenharmony_ci &dasd_stats_proc_ops); 35062306a36Sopenharmony_ci if (!dasd_statistics_entry) 35162306a36Sopenharmony_ci goto out_nostatistics; 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci out_nostatistics: 35562306a36Sopenharmony_ci remove_proc_entry("devices", dasd_proc_root_entry); 35662306a36Sopenharmony_ci out_nodevices: 35762306a36Sopenharmony_ci remove_proc_entry("dasd", NULL); 35862306a36Sopenharmony_ci out_nodasd: 35962306a36Sopenharmony_ci return -ENOENT; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid 36362306a36Sopenharmony_cidasd_proc_exit(void) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci remove_proc_entry("devices", dasd_proc_root_entry); 36662306a36Sopenharmony_ci remove_proc_entry("statistics", dasd_proc_root_entry); 36762306a36Sopenharmony_ci remove_proc_entry("dasd", NULL); 36862306a36Sopenharmony_ci} 369