1 // SPDX-License-Identifier: GPL-2.0 2 /* 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, 2002 9 * 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 26 static struct proc_dir_entry *dasd_proc_root_entry = NULL; 27 static struct proc_dir_entry *dasd_devices_entry = NULL; 28 static struct proc_dir_entry *dasd_statistics_entry = NULL; 29 30 static int 31 dasd_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 else 52 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 else 59 seq_printf(m, " at (???:??????)"); 60 /* Print device name. */ 61 if (block->gdp) 62 seq_printf(m, " is %-8s", block->gdp->disk_name); 63 else 64 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 else 88 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 105 static 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 112 static 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 118 static void dasd_devices_stop(struct seq_file *m, void *v) 119 { 120 } 121 122 static 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_PROFILE 130 static 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 149 static 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 164 static 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 179 static 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 192 static int dasd_stats_proc_show(struct seq_file *m, void *v) 193 { 194 #ifdef CONFIG_DASD_PROFILE 195 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 #else 244 seq_printf(m, "Statistics are not activated in this kernel\n"); 245 #endif 246 return 0; 247 } 248 249 static int dasd_stats_proc_open(struct inode *inode, struct file *file) 250 { 251 return single_open(file, dasd_stats_proc_show, NULL); 252 } 253 254 static 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_PROFILE 258 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 } else 296 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 } else 303 goto out_parse_error; 304 vfree(buffer); 305 return user_len; 306 out_parse_error: 307 rc = -EINVAL; 308 pr_warn("%s is not a supported value for /proc/dasd/statistics\n", str); 309 out_error: 310 vfree(buffer); 311 return rc; 312 #else 313 pr_warn("/proc/dasd/statistics: is not activated in this kernel\n"); 314 return user_len; 315 #endif /* CONFIG_DASD_PROFILE */ 316 } 317 318 static 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 */ 330 int 331 dasd_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 dasd_proc_root_entry = NULL; 354 out_nodasd: 355 return -ENOENT; 356 } 357 358 void 359 dasd_proc_exit(void) 360 { 361 if (!dasd_proc_root_entry) 362 return; 363 364 remove_proc_entry("devices", dasd_proc_root_entry); 365 remove_proc_entry("statistics", dasd_proc_root_entry); 366 remove_proc_entry("dasd", NULL); 367 dasd_proc_root_entry = NULL; 368 } 369