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