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