diff options
author | Neil Horman <nhorman@redhat.com> | 2006-01-14 16:20:38 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-14 21:25:19 -0500 |
commit | 7170be5f586b59bdcdab082778a5d9203ba7b667 (patch) | |
tree | 00d34a062c5bbab83315232078296567037bd3f4 | |
parent | faf3a98918aa5f14a29e0d246e194be58b9357f0 (diff) |
[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>
-rw-r--r-- | block/genhd.c | 106 | ||||
-rw-r--r-- | fs/char_dev.c | 96 | ||||
-rw-r--r-- | fs/proc/proc_misc.c | 160 | ||||
-rw-r--r-- | include/linux/fs.h | 11 |
4 files changed, 320 insertions, 53 deletions
diff --git a/block/genhd.c b/block/genhd.c index f1ed83f3f083..db57546a709d 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -38,34 +38,100 @@ static inline int major_to_index(int major) | |||
38 | return major % MAX_PROBE_HASH; | 38 | return major % MAX_PROBE_HASH; |
39 | } | 39 | } |
40 | 40 | ||
41 | #ifdef CONFIG_PROC_FS | 41 | struct blkdev_info { |
42 | /* get block device names in somewhat random order */ | 42 | int index; |
43 | int get_blkdev_list(char *p, int used) | 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 | struct blk_major_name *n; | 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 | for (i = 0; i < ARRAY_SIZE(major_names); i++) { | 110 | for (i = 0; i < ARRAY_SIZE(major_names); i++) { |
52 | for (n = major_names[i]; n; n = n->next) { | 111 | for (n = major_names[i]; n; n = n->next) |
53 | /* | 112 | count++; |
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 | } | ||
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 | int register_blkdev(unsigned int major, const char *name) | 136 | int register_blkdev(unsigned int major, const char *name) |
71 | { | 137 | { |
diff --git a/fs/char_dev.c b/fs/char_dev.c index 3b1b1eefdbb0..21195c481637 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c | |||
@@ -35,7 +35,7 @@ static struct char_device_struct { | |||
35 | unsigned int major; | 35 | unsigned int major; |
36 | unsigned int baseminor; | 36 | unsigned int baseminor; |
37 | int minorct; | 37 | int minorct; |
38 | const char *name; | 38 | char name[64]; |
39 | struct file_operations *fops; | 39 | struct file_operations *fops; |
40 | struct cdev *cdev; /* will die */ | 40 | struct cdev *cdev; /* will die */ |
41 | } *chrdevs[MAX_PROBE_HASH]; | 41 | } *chrdevs[MAX_PROBE_HASH]; |
@@ -46,34 +46,84 @@ static inline int major_to_index(int major) | |||
46 | return major % MAX_PROBE_HASH; | 46 | return major % MAX_PROBE_HASH; |
47 | } | 47 | } |
48 | 48 | ||
49 | /* get char device names in somewhat random order */ | 49 | struct chrdev_info { |
50 | int get_chrdev_list(char *page) | 50 | int index; |
51 | { | ||
52 | struct char_device_struct *cd; | 51 | struct char_device_struct *cd; |
53 | int i, len; | 52 | }; |
54 | 53 | ||
55 | len = sprintf(page, "Character devices:\n"); | 54 | void *get_next_chrdev(void *dev) |
55 | { | ||
56 | struct chrdev_info *info; | ||
56 | 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 | { | ||
57 | down(&chrdevs_lock); | 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) | ||
103 | { | ||
104 | struct char_device_struct *cd; | ||
105 | int i, count; | ||
106 | |||
107 | count = 0; | ||
108 | |||
58 | for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { | 109 | for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { |
59 | for (cd = chrdevs[i]; cd; cd = cd->next) { | 110 | for (cd = chrdevs[i]; cd; cd = cd->next) |
60 | /* | 111 | count++; |
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 | } | ||
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 | /* |
@@ -121,7 +171,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, | |||
121 | cd->major = major; | 171 | cd->major = major; |
122 | cd->baseminor = baseminor; | 172 | cd->baseminor = baseminor; |
123 | cd->minorct = minorct; | 173 | cd->minorct = minorct; |
124 | cd->name = name; | 174 | strncpy(cd->name,name, 64); |
125 | 175 | ||
126 | i = major_to_index(major); | 176 | i = major_to_index(major); |
127 | 177 | ||
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 63bf6c00fa0c..8f8014285a34 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/time.h> | 20 | #include <linux/time.h> |
21 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
22 | #include <linux/kernel_stat.h> | 22 | #include <linux/kernel_stat.h> |
23 | #include <linux/fs.h> | ||
23 | #include <linux/tty.h> | 24 | #include <linux/tty.h> |
24 | #include <linux/string.h> | 25 | #include <linux/string.h> |
25 | #include <linux/mman.h> | 26 | #include <linux/mman.h> |
@@ -62,7 +63,6 @@ | |||
62 | */ | 63 | */ |
63 | extern int get_hardware_list(char *); | 64 | extern int get_hardware_list(char *); |
64 | extern int get_stram_list(char *); | 65 | extern int get_stram_list(char *); |
65 | extern int get_chrdev_list(char *); | ||
66 | extern int get_filesystem_list(char *); | 66 | extern int get_filesystem_list(char *); |
67 | extern int get_exec_domain_list(char *); | 67 | extern int get_exec_domain_list(char *); |
68 | extern int get_dma_list(char *); | 68 | extern int get_dma_list(char *); |
@@ -248,6 +248,154 @@ static int cpuinfo_open(struct inode *inode, struct file *file) | |||
248 | { | 248 | { |
249 | return seq_open(file, &cpuinfo_op); | 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 | static struct file_operations proc_cpuinfo_operations = { | 399 | static struct file_operations proc_cpuinfo_operations = { |
252 | .open = cpuinfo_open, | 400 | .open = cpuinfo_open, |
253 | .read = seq_read, | 401 | .read = seq_read, |
@@ -450,14 +598,6 @@ static struct file_operations proc_stat_operations = { | |||
450 | .release = single_release, | 598 | .release = single_release, |
451 | }; | 599 | }; |
452 | 600 | ||
453 | static int devices_read_proc(char *page, char **start, off_t off, | ||
454 | int count, int *eof, void *data) | ||
455 | { | ||
456 | int len = get_chrdev_list(page); | ||
457 | len += get_blkdev_list(page+len, len); | ||
458 | return proc_calc_metrics(page, start, off, count, eof, len); | ||
459 | } | ||
460 | |||
461 | /* | 601 | /* |
462 | * /proc/interrupts | 602 | * /proc/interrupts |
463 | */ | 603 | */ |
@@ -582,7 +722,6 @@ void __init proc_misc_init(void) | |||
582 | #ifdef CONFIG_STRAM_PROC | 722 | #ifdef CONFIG_STRAM_PROC |
583 | {"stram", stram_read_proc}, | 723 | {"stram", stram_read_proc}, |
584 | #endif | 724 | #endif |
585 | {"devices", devices_read_proc}, | ||
586 | {"filesystems", filesystems_read_proc}, | 725 | {"filesystems", filesystems_read_proc}, |
587 | {"cmdline", cmdline_read_proc}, | 726 | {"cmdline", cmdline_read_proc}, |
588 | {"locks", locks_read_proc}, | 727 | {"locks", locks_read_proc}, |
@@ -598,6 +737,7 @@ void __init proc_misc_init(void) | |||
598 | entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); | 737 | entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); |
599 | if (entry) | 738 | if (entry) |
600 | entry->proc_fops = &proc_kmsg_operations; | 739 | entry->proc_fops = &proc_kmsg_operations; |
740 | create_seq_entry("devices", 0, &proc_devinfo_operations); | ||
601 | create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations); | 741 | create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations); |
602 | create_seq_entry("partitions", 0, &proc_partitions_operations); | 742 | create_seq_entry("partitions", 0, &proc_partitions_operations); |
603 | create_seq_entry("stat", 0, &proc_stat_operations); | 743 | create_seq_entry("stat", 0, &proc_stat_operations); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index d1e370d25f7b..552cedfa6064 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1383,6 +1383,12 @@ extern int register_chrdev(unsigned int, const char *, | |||
1383 | extern int unregister_chrdev(unsigned int, const char *); | 1383 | extern int unregister_chrdev(unsigned int, const char *); |
1384 | extern void unregister_chrdev_region(dev_t, unsigned); | 1384 | extern void unregister_chrdev_region(dev_t, unsigned); |
1385 | extern int chrdev_open(struct inode *, struct file *); | 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 | /* fs/block_dev.c */ | 1393 | /* fs/block_dev.c */ |
1388 | #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ | 1394 | #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ |
@@ -1391,6 +1397,11 @@ extern const char *bdevname(struct block_device *bdev, char *buffer); | |||
1391 | extern struct block_device *lookup_bdev(const char *); | 1397 | extern struct block_device *lookup_bdev(const char *); |
1392 | extern struct block_device *open_bdev_excl(const char *, int, void *); | 1398 | extern struct block_device *open_bdev_excl(const char *, int, void *); |
1393 | extern void close_bdev_excl(struct block_device *); | 1399 | extern void close_bdev_excl(struct block_device *); |
1400 | extern void *acquire_blkdev_list(void); | ||
1401 | extern int count_blkdev_list(void); | ||
1402 | extern void *get_next_blkdev(void *); | ||
1403 | extern int get_blkdev_info(void *, int *, char **); | ||
1404 | extern void release_blkdev_list(void *); | ||
1394 | 1405 | ||
1395 | extern void init_special_inode(struct inode *, umode_t, dev_t); | 1406 | extern void init_special_inode(struct inode *, umode_t, dev_t); |
1396 | 1407 | ||