[PATCH] convert /proc/devices to use seq_file interface

A Christoph suggested that the /proc/devices file be converted to use the
seq_file interface. This patch does that.

I've obxerved one or two installation that had sufficiently large sans that
they overran the 4k limit on /proc/devices.

Signed-off-by: Neil Horman <nhorman@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Neil Horman and committed by Linus Torvalds 7170be5f faf3a989

+320 -53
+86 -20
block/genhd.c
··· 38 38 return major % MAX_PROBE_HASH; 39 39 } 40 40 41 - #ifdef CONFIG_PROC_FS 42 - /* get block device names in somewhat random order */ 43 - int get_blkdev_list(char *p, int used) 41 + struct blkdev_info { 42 + int index; 43 + struct blk_major_name *bd; 44 + }; 45 + 46 + /* 47 + * iterate over a list of blkdev_info structures. allows 48 + * the major_names array to be iterated over from outside this file 49 + * must be called with the block_subsys_sem held 50 + */ 51 + void *get_next_blkdev(void *dev) 52 + { 53 + struct blkdev_info *info; 54 + 55 + if (dev == NULL) { 56 + info = kmalloc(sizeof(*info), GFP_KERNEL); 57 + if (!info) 58 + goto out; 59 + info->index=0; 60 + info->bd = major_names[info->index]; 61 + if (info->bd) 62 + goto out; 63 + } else { 64 + info = dev; 65 + } 66 + 67 + while (info->index < ARRAY_SIZE(major_names)) { 68 + if (info->bd) 69 + info->bd = info->bd->next; 70 + if (info->bd) 71 + goto out; 72 + /* 73 + * No devices on this chain, move to the next 74 + */ 75 + info->index++; 76 + info->bd = (info->index < ARRAY_SIZE(major_names)) ? 77 + major_names[info->index] : NULL; 78 + if (info->bd) 79 + goto out; 80 + } 81 + 82 + out: 83 + return info; 84 + } 85 + 86 + void *acquire_blkdev_list(void) 87 + { 88 + down(&block_subsys_sem); 89 + return get_next_blkdev(NULL); 90 + } 91 + 92 + void release_blkdev_list(void *dev) 93 + { 94 + up(&block_subsys_sem); 95 + kfree(dev); 96 + } 97 + 98 + 99 + /* 100 + * Count the number of records in the blkdev_list. 101 + * must be called with the block_subsys_sem held 102 + */ 103 + int count_blkdev_list(void) 44 104 { 45 105 struct blk_major_name *n; 46 - int i, len; 106 + int i, count; 47 107 48 - len = snprintf(p, (PAGE_SIZE-used), "\nBlock devices:\n"); 108 + count = 0; 49 109 50 - down(&block_subsys_sem); 51 110 for (i = 0; i < ARRAY_SIZE(major_names); i++) { 52 - for (n = major_names[i]; n; n = n->next) { 53 - /* 54 - * If the curent string plus the 5 extra characters 55 - * in the line would run us off the page, then we're done 56 - */ 57 - if ((len + used + strlen(n->name) + 5) >= PAGE_SIZE) 58 - goto page_full; 59 - len += sprintf(p+len, "%3d %s\n", 60 - n->major, n->name); 61 - } 111 + for (n = major_names[i]; n; n = n->next) 112 + count++; 62 113 } 63 - page_full: 64 - up(&block_subsys_sem); 65 114 66 - return len; 115 + return count; 67 116 } 68 - #endif 117 + 118 + /* 119 + * extract the major and name values from a blkdev_info struct 120 + * passed in as a void to *dev. Must be called with 121 + * block_subsys_sem held 122 + */ 123 + int get_blkdev_info(void *dev, int *major, char **name) 124 + { 125 + struct blkdev_info *info = dev; 126 + 127 + if (info->bd == NULL) 128 + return 1; 129 + 130 + *major = info->bd->major; 131 + *name = info->bd->name; 132 + return 0; 133 + } 134 + 69 135 70 136 int register_blkdev(unsigned int major, const char *name) 71 137 {
+73 -23
fs/char_dev.c
··· 35 35 unsigned int major; 36 36 unsigned int baseminor; 37 37 int minorct; 38 - const char *name; 38 + char name[64]; 39 39 struct file_operations *fops; 40 40 struct cdev *cdev; /* will die */ 41 41 } *chrdevs[MAX_PROBE_HASH]; ··· 46 46 return major % MAX_PROBE_HASH; 47 47 } 48 48 49 - /* get char device names in somewhat random order */ 50 - int get_chrdev_list(char *page) 49 + struct chrdev_info { 50 + int index; 51 + struct char_device_struct *cd; 52 + }; 53 + 54 + void *get_next_chrdev(void *dev) 55 + { 56 + struct chrdev_info *info; 57 + 58 + if (dev == NULL) { 59 + info = kmalloc(sizeof(*info), GFP_KERNEL); 60 + if (!info) 61 + goto out; 62 + info->index=0; 63 + info->cd = chrdevs[info->index]; 64 + if (info->cd) 65 + goto out; 66 + } else { 67 + info = dev; 68 + } 69 + 70 + while (info->index < ARRAY_SIZE(chrdevs)) { 71 + if (info->cd) 72 + info->cd = info->cd->next; 73 + if (info->cd) 74 + goto out; 75 + /* 76 + * No devices on this chain, move to the next 77 + */ 78 + info->index++; 79 + info->cd = (info->index < ARRAY_SIZE(chrdevs)) ? 80 + chrdevs[info->index] : NULL; 81 + if (info->cd) 82 + goto out; 83 + } 84 + 85 + out: 86 + return info; 87 + } 88 + 89 + void *acquire_chrdev_list(void) 90 + { 91 + down(&chrdevs_lock); 92 + return get_next_chrdev(NULL); 93 + } 94 + 95 + void release_chrdev_list(void *dev) 96 + { 97 + up(&chrdevs_lock); 98 + kfree(dev); 99 + } 100 + 101 + 102 + int count_chrdev_list(void) 51 103 { 52 104 struct char_device_struct *cd; 53 - int i, len; 105 + int i, count; 54 106 55 - len = sprintf(page, "Character devices:\n"); 107 + count = 0; 56 108 57 - down(&chrdevs_lock); 58 109 for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { 59 - for (cd = chrdevs[i]; cd; cd = cd->next) { 60 - /* 61 - * if the current name, plus the 5 extra characters 62 - * in the device line for this entry 63 - * would run us off the page, we're done 64 - */ 65 - if ((len+strlen(cd->name) + 5) >= PAGE_SIZE) 66 - goto page_full; 67 - 68 - 69 - len += sprintf(page+len, "%3d %s\n", 70 - cd->major, cd->name); 71 - } 110 + for (cd = chrdevs[i]; cd; cd = cd->next) 111 + count++; 72 112 } 73 - page_full: 74 - up(&chrdevs_lock); 75 113 76 - return len; 114 + return count; 115 + } 116 + 117 + int get_chrdev_info(void *dev, int *major, char **name) 118 + { 119 + struct chrdev_info *info = dev; 120 + 121 + if (info->cd == NULL) 122 + return 1; 123 + 124 + *major = info->cd->major; 125 + *name = info->cd->name; 126 + return 0; 77 127 } 78 128 79 129 /* ··· 171 121 cd->major = major; 172 122 cd->baseminor = baseminor; 173 123 cd->minorct = minorct; 174 - cd->name = name; 124 + strncpy(cd->name,name, 64); 175 125 176 126 i = major_to_index(major); 177 127
+150 -10
fs/proc/proc_misc.c
··· 20 20 #include <linux/time.h> 21 21 #include <linux/kernel.h> 22 22 #include <linux/kernel_stat.h> 23 + #include <linux/fs.h> 23 24 #include <linux/tty.h> 24 25 #include <linux/string.h> 25 26 #include <linux/mman.h> ··· 63 62 */ 64 63 extern int get_hardware_list(char *); 65 64 extern int get_stram_list(char *); 66 - extern int get_chrdev_list(char *); 67 65 extern int get_filesystem_list(char *); 68 66 extern int get_exec_domain_list(char *); 69 67 extern int get_dma_list(char *); ··· 248 248 { 249 249 return seq_open(file, &cpuinfo_op); 250 250 } 251 + 252 + enum devinfo_states { 253 + CHR_HDR, 254 + CHR_LIST, 255 + BLK_HDR, 256 + BLK_LIST, 257 + DEVINFO_DONE 258 + }; 259 + 260 + struct devinfo_state { 261 + void *chrdev; 262 + void *blkdev; 263 + unsigned int num_records; 264 + unsigned int cur_record; 265 + enum devinfo_states state; 266 + }; 267 + 268 + static void *devinfo_start(struct seq_file *f, loff_t *pos) 269 + { 270 + struct devinfo_state *info = f->private; 271 + 272 + if (*pos) { 273 + if ((info) && (*pos <= info->num_records)) 274 + return info; 275 + return NULL; 276 + } 277 + info = kmalloc(sizeof(*info), GFP_KERNEL); 278 + f->private = info; 279 + info->chrdev = acquire_chrdev_list(); 280 + info->blkdev = acquire_blkdev_list(); 281 + info->state = CHR_HDR; 282 + info->num_records = count_chrdev_list(); 283 + info->num_records += count_blkdev_list(); 284 + info->num_records += 2; /* Character and Block headers */ 285 + *pos = 1; 286 + info->cur_record = *pos; 287 + return info; 288 + } 289 + 290 + static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos) 291 + { 292 + int idummy; 293 + char *ndummy; 294 + struct devinfo_state *info = f->private; 295 + 296 + switch (info->state) { 297 + case CHR_HDR: 298 + info->state = CHR_LIST; 299 + (*pos)++; 300 + /*fallthrough*/ 301 + case CHR_LIST: 302 + if (get_chrdev_info(info->chrdev,&idummy,&ndummy)) { 303 + /* 304 + * The character dev list is complete 305 + */ 306 + info->state = BLK_HDR; 307 + } else { 308 + info->chrdev = get_next_chrdev(info->chrdev); 309 + } 310 + (*pos)++; 311 + break; 312 + case BLK_HDR: 313 + info->state = BLK_LIST; 314 + (*pos)++; 315 + break; 316 + case BLK_LIST: 317 + if (get_blkdev_info(info->blkdev,&idummy,&ndummy)) { 318 + /* 319 + * The block dev list is complete 320 + */ 321 + info->state = DEVINFO_DONE; 322 + } else { 323 + info->blkdev = get_next_blkdev(info->blkdev); 324 + } 325 + (*pos)++; 326 + break; 327 + case DEVINFO_DONE: 328 + (*pos)++; 329 + info->cur_record = *pos; 330 + info = NULL; 331 + break; 332 + default: 333 + break; 334 + } 335 + if (info) 336 + info->cur_record = *pos; 337 + return info; 338 + } 339 + 340 + static void devinfo_stop(struct seq_file *f, void *v) 341 + { 342 + struct devinfo_state *info = f->private; 343 + 344 + if (info) { 345 + release_chrdev_list(info->chrdev); 346 + release_blkdev_list(info->blkdev); 347 + f->private = NULL; 348 + kfree(info); 349 + } 350 + } 351 + 352 + static int devinfo_show(struct seq_file *f, void *arg) 353 + { 354 + int major; 355 + char *name; 356 + struct devinfo_state *info = f->private; 357 + 358 + switch(info->state) { 359 + case CHR_HDR: 360 + seq_printf(f,"Character devices:\n"); 361 + /* fallthrough */ 362 + case CHR_LIST: 363 + if (!get_chrdev_info(info->chrdev,&major,&name)) 364 + seq_printf(f,"%3d %s\n",major,name); 365 + break; 366 + case BLK_HDR: 367 + seq_printf(f,"\nBlock devices:\n"); 368 + /* fallthrough */ 369 + case BLK_LIST: 370 + if (!get_blkdev_info(info->blkdev,&major,&name)) 371 + seq_printf(f,"%3d %s\n",major,name); 372 + break; 373 + default: 374 + break; 375 + } 376 + 377 + return 0; 378 + } 379 + 380 + static struct seq_operations devinfo_op = { 381 + .start = devinfo_start, 382 + .next = devinfo_next, 383 + .stop = devinfo_stop, 384 + .show = devinfo_show, 385 + }; 386 + 387 + static int devinfo_open(struct inode *inode, struct file *file) 388 + { 389 + return seq_open(file, &devinfo_op); 390 + } 391 + 392 + static struct file_operations proc_devinfo_operations = { 393 + .open = devinfo_open, 394 + .read = seq_read, 395 + .llseek = seq_lseek, 396 + .release = seq_release, 397 + }; 398 + 251 399 static struct file_operations proc_cpuinfo_operations = { 252 400 .open = cpuinfo_open, 253 401 .read = seq_read, ··· 598 450 .release = single_release, 599 451 }; 600 452 601 - static int devices_read_proc(char *page, char **start, off_t off, 602 - int count, int *eof, void *data) 603 - { 604 - int len = get_chrdev_list(page); 605 - len += get_blkdev_list(page+len, len); 606 - return proc_calc_metrics(page, start, off, count, eof, len); 607 - } 608 - 609 453 /* 610 454 * /proc/interrupts 611 455 */ ··· 722 582 #ifdef CONFIG_STRAM_PROC 723 583 {"stram", stram_read_proc}, 724 584 #endif 725 - {"devices", devices_read_proc}, 726 585 {"filesystems", filesystems_read_proc}, 727 586 {"cmdline", cmdline_read_proc}, 728 587 {"locks", locks_read_proc}, ··· 737 598 entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); 738 599 if (entry) 739 600 entry->proc_fops = &proc_kmsg_operations; 601 + create_seq_entry("devices", 0, &proc_devinfo_operations); 740 602 create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations); 741 603 create_seq_entry("partitions", 0, &proc_partitions_operations); 742 604 create_seq_entry("stat", 0, &proc_stat_operations);
+11
include/linux/fs.h
··· 1383 1383 extern int unregister_chrdev(unsigned int, const char *); 1384 1384 extern void unregister_chrdev_region(dev_t, unsigned); 1385 1385 extern int chrdev_open(struct inode *, struct file *); 1386 + extern int get_chrdev_list(char *); 1387 + extern void *acquire_chrdev_list(void); 1388 + extern int count_chrdev_list(void); 1389 + extern void *get_next_chrdev(void *); 1390 + extern int get_chrdev_info(void *, int *, char **); 1391 + extern void release_chrdev_list(void *); 1386 1392 1387 1393 /* fs/block_dev.c */ 1388 1394 #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ ··· 1397 1391 extern struct block_device *lookup_bdev(const char *); 1398 1392 extern struct block_device *open_bdev_excl(const char *, int, void *); 1399 1393 extern void close_bdev_excl(struct block_device *); 1394 + extern void *acquire_blkdev_list(void); 1395 + extern int count_blkdev_list(void); 1396 + extern void *get_next_blkdev(void *); 1397 + extern int get_blkdev_info(void *, int *, char **); 1398 + extern void release_blkdev_list(void *); 1400 1399 1401 1400 extern void init_special_inode(struct inode *, umode_t, dev_t); 1402 1401