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