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 <asm/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 /* check for active profiling */ 216 if (!dasd_global_profile_level) { 217 seq_printf(m, "Statistics are off - they might be " 218 "switched on using 'echo set on > " 219 "/proc/dasd/statistics'\n"); 220 return 0; 221 } 222 prof = &dasd_global_profile_data; 223 224 /* prevent counter 'overflow' on output */ 225 for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999; 226 factor *= 10); 227 228 seq_printf(m, "%d dasd I/O requests\n", prof->dasd_io_reqs); 229 seq_printf(m, "with %u sectors(512B each)\n", 230 prof->dasd_io_sects); 231 seq_printf(m, "Scale Factor is %d\n", factor); 232 seq_printf(m, 233 " __<4 ___8 __16 __32 __64 _128 " 234 " _256 _512 __1k __2k __4k __8k " 235 " _16k _32k _64k 128k\n"); 236 seq_printf(m, 237 " _256 _512 __1M __2M __4M __8M " 238 " _16M _32M _64M 128M 256M 512M " 239 " __1G __2G __4G " " _>4G\n"); 240 241 seq_printf(m, "Histogram of sizes (512B secs)\n"); 242 dasd_statistics_array(m, prof->dasd_io_secs, factor); 243 seq_printf(m, "Histogram of I/O times (microseconds)\n"); 244 dasd_statistics_array(m, prof->dasd_io_times, factor); 245 seq_printf(m, "Histogram of I/O times per sector\n"); 246 dasd_statistics_array(m, prof->dasd_io_timps, factor); 247 seq_printf(m, "Histogram of I/O time till ssch\n"); 248 dasd_statistics_array(m, prof->dasd_io_time1, factor); 249 seq_printf(m, "Histogram of I/O time between ssch and irq\n"); 250 dasd_statistics_array(m, prof->dasd_io_time2, factor); 251 seq_printf(m, "Histogram of I/O time between ssch " 252 "and irq per sector\n"); 253 dasd_statistics_array(m, prof->dasd_io_time2ps, factor); 254 seq_printf(m, "Histogram of I/O time between irq and end\n"); 255 dasd_statistics_array(m, prof->dasd_io_time3, factor); 256 seq_printf(m, "# of req in chanq at enqueuing (1..32) \n"); 257 dasd_statistics_array(m, prof->dasd_io_nr_req, factor); 258 #else 259 seq_printf(m, "Statistics are not activated in this kernel\n"); 260 #endif 261 return 0; 262 } 263 264 static int dasd_stats_proc_open(struct inode *inode, struct file *file) 265 { 266 return single_open(file, dasd_stats_proc_show, NULL); 267 } 268 269 static ssize_t dasd_stats_proc_write(struct file *file, 270 const char __user *user_buf, size_t user_len, loff_t *pos) 271 { 272 #ifdef CONFIG_DASD_PROFILE 273 char *buffer, *str; 274 int rc; 275 276 if (user_len > 65536) 277 user_len = 65536; 278 buffer = dasd_get_user_string(user_buf, user_len); 279 if (IS_ERR(buffer)) 280 return PTR_ERR(buffer); 281 282 /* check for valid verbs */ 283 str = skip_spaces(buffer); 284 if (strncmp(str, "set", 3) == 0 && isspace(str[3])) { 285 /* 'set xxx' was given */ 286 str = skip_spaces(str + 4); 287 if (strcmp(str, "on") == 0) { 288 /* switch on statistics profiling */ 289 rc = dasd_stats_all_block_on(); 290 if (rc) { 291 dasd_stats_all_block_off(); 292 goto out_error; 293 } 294 dasd_global_profile_reset(); 295 dasd_global_profile_level = DASD_PROFILE_ON; 296 pr_info("The statistics feature has been switched " 297 "on\n"); 298 } else if (strcmp(str, "off") == 0) { 299 /* switch off and reset statistics profiling */ 300 dasd_global_profile_level = DASD_PROFILE_OFF; 301 dasd_global_profile_reset(); 302 dasd_stats_all_block_off(); 303 pr_info("The statistics feature has been switched " 304 "off\n"); 305 } else 306 goto out_parse_error; 307 } else if (strncmp(str, "reset", 5) == 0) { 308 /* reset the statistics */ 309 dasd_global_profile_reset(); 310 dasd_stats_all_block_reset(); 311 pr_info("The statistics have been reset\n"); 312 } else 313 goto out_parse_error; 314 vfree(buffer); 315 return user_len; 316 out_parse_error: 317 rc = -EINVAL; 318 pr_warning("%s is not a supported value for /proc/dasd/statistics\n", 319 str); 320 out_error: 321 vfree(buffer); 322 return rc; 323 #else 324 pr_warning("/proc/dasd/statistics: is not activated in this kernel\n"); 325 return user_len; 326 #endif /* CONFIG_DASD_PROFILE */ 327 } 328 329 static const struct file_operations dasd_stats_proc_fops = { 330 .owner = THIS_MODULE, 331 .open = dasd_stats_proc_open, 332 .read = seq_read, 333 .llseek = seq_lseek, 334 .release = single_release, 335 .write = dasd_stats_proc_write, 336 }; 337 338 /* 339 * Create dasd proc-fs entries. 340 * In case creation failed, cleanup and return -ENOENT. 341 */ 342 int 343 dasd_proc_init(void) 344 { 345 dasd_proc_root_entry = proc_mkdir("dasd", NULL); 346 if (!dasd_proc_root_entry) 347 goto out_nodasd; 348 dasd_devices_entry = proc_create("devices", 349 S_IFREG | S_IRUGO | S_IWUSR, 350 dasd_proc_root_entry, 351 &dasd_devices_file_ops); 352 if (!dasd_devices_entry) 353 goto out_nodevices; 354 dasd_statistics_entry = proc_create("statistics", 355 S_IFREG | S_IRUGO | S_IWUSR, 356 dasd_proc_root_entry, 357 &dasd_stats_proc_fops); 358 if (!dasd_statistics_entry) 359 goto out_nostatistics; 360 return 0; 361 362 out_nostatistics: 363 remove_proc_entry("devices", dasd_proc_root_entry); 364 out_nodevices: 365 remove_proc_entry("dasd", NULL); 366 out_nodasd: 367 return -ENOENT; 368 } 369 370 void 371 dasd_proc_exit(void) 372 { 373 remove_proc_entry("devices", dasd_proc_root_entry); 374 remove_proc_entry("statistics", dasd_proc_root_entry); 375 remove_proc_entry("dasd", NULL); 376 } 377