diff options
author | Joe Korty <joe.korty@ccur.com> | 2006-03-31 05:30:32 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-31 15:18:53 -0500 |
commit | 68eef3b4791572ecb70249c7fb145bb3742dd899 (patch) | |
tree | 1f61fce839cec8d672ae06a423d46f0a6fcd924d | |
parent | a2c348fe0117adced11e374329a5ea3f7c43cb41 (diff) |
[PATCH] Simplify proc/devices and fix early termination regression
Make baby-simple the code for /proc/devices. Based on the proven design
for /proc/interrupts.
This also fixes the early-termination regression 2.6.16 introduced, as
demonstrated by:
# dd if=/proc/devices bs=1
Character devices:
1 mem
27+0 records in
27+0 records out
This should also work (but is untested) when /proc/devices >4096 bytes,
which I believe is what the original 2.6.16 rewrite fixed.
[akpm@osdl.org: cleanups, simplifications]
Signed-off-by: Joe Korty <joe.korty@ccur.com>
Cc: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | block/genhd.c | 103 | ||||
-rw-r--r-- | fs/char_dev.c | 87 | ||||
-rw-r--r-- | fs/proc/proc_misc.c | 163 | ||||
-rw-r--r-- | include/linux/fs.h | 15 |
4 files changed, 62 insertions, 306 deletions
diff --git a/block/genhd.c b/block/genhd.c index db4c60c802d6..5a8d3bf02f17 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -17,8 +17,6 @@ | |||
17 | #include <linux/buffer_head.h> | 17 | #include <linux/buffer_head.h> |
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | 19 | ||
20 | #define MAX_PROBE_HASH 255 /* random */ | ||
21 | |||
22 | static struct subsystem block_subsys; | 20 | static struct subsystem block_subsys; |
23 | 21 | ||
24 | static DEFINE_MUTEX(block_subsys_lock); | 22 | static DEFINE_MUTEX(block_subsys_lock); |
@@ -31,108 +29,29 @@ static struct blk_major_name { | |||
31 | struct blk_major_name *next; | 29 | struct blk_major_name *next; |
32 | int major; | 30 | int major; |
33 | char name[16]; | 31 | char name[16]; |
34 | } *major_names[MAX_PROBE_HASH]; | 32 | } *major_names[BLKDEV_MAJOR_HASH_SIZE]; |
35 | 33 | ||
36 | /* index in the above - for now: assume no multimajor ranges */ | 34 | /* index in the above - for now: assume no multimajor ranges */ |
37 | static inline int major_to_index(int major) | 35 | static inline int major_to_index(int major) |
38 | { | 36 | { |
39 | return major % MAX_PROBE_HASH; | 37 | return major % BLKDEV_MAJOR_HASH_SIZE; |
40 | } | ||
41 | |||
42 | struct blkdev_info { | ||
43 | int index; | ||
44 | struct blk_major_name *bd; | ||
45 | }; | ||
46 | |||
47 | /* | ||
48 | * iterate over a list of blkdev_info structures. allows | ||
49 | * the major_names array to be iterated over from outside this file | ||
50 | * must be called with the block_subsys_lock held | ||
51 | */ | ||
52 | void *get_next_blkdev(void *dev) | ||
53 | { | ||
54 | struct blkdev_info *info; | ||
55 | |||
56 | if (dev == NULL) { | ||
57 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
58 | if (!info) | ||
59 | goto out; | ||
60 | info->index=0; | ||
61 | info->bd = major_names[info->index]; | ||
62 | if (info->bd) | ||
63 | goto out; | ||
64 | } else { | ||
65 | info = dev; | ||
66 | } | ||
67 | |||
68 | while (info->index < ARRAY_SIZE(major_names)) { | ||
69 | if (info->bd) | ||
70 | info->bd = info->bd->next; | ||
71 | if (info->bd) | ||
72 | goto out; | ||
73 | /* | ||
74 | * No devices on this chain, move to the next | ||
75 | */ | ||
76 | info->index++; | ||
77 | info->bd = (info->index < ARRAY_SIZE(major_names)) ? | ||
78 | major_names[info->index] : NULL; | ||
79 | if (info->bd) | ||
80 | goto out; | ||
81 | } | ||
82 | |||
83 | out: | ||
84 | return info; | ||
85 | } | ||
86 | |||
87 | void *acquire_blkdev_list(void) | ||
88 | { | ||
89 | mutex_lock(&block_subsys_lock); | ||
90 | return get_next_blkdev(NULL); | ||
91 | } | ||
92 | |||
93 | void release_blkdev_list(void *dev) | ||
94 | { | ||
95 | mutex_unlock(&block_subsys_lock); | ||
96 | kfree(dev); | ||
97 | } | 38 | } |
98 | 39 | ||
40 | #ifdef CONFIG_PROC_FS | ||
99 | 41 | ||
100 | /* | 42 | void blkdev_show(struct seq_file *f, off_t offset) |
101 | * Count the number of records in the blkdev_list. | ||
102 | * must be called with the block_subsys_lock held | ||
103 | */ | ||
104 | int count_blkdev_list(void) | ||
105 | { | 43 | { |
106 | struct blk_major_name *n; | 44 | struct blk_major_name *dp; |
107 | int i, count; | ||
108 | 45 | ||
109 | count = 0; | 46 | if (offset < BLKDEV_MAJOR_HASH_SIZE) { |
110 | 47 | mutex_lock(&block_subsys_lock); | |
111 | for (i = 0; i < ARRAY_SIZE(major_names); i++) { | 48 | for (dp = major_names[offset]; dp; dp = dp->next) |
112 | for (n = major_names[i]; n; n = n->next) | 49 | seq_printf(f, "%3d %s\n", dp->major, dp->name); |
113 | count++; | 50 | mutex_unlock(&block_subsys_lock); |
114 | } | 51 | } |
115 | |||
116 | return count; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * extract the major and name values from a blkdev_info struct | ||
121 | * passed in as a void to *dev. Must be called with | ||
122 | * block_subsys_lock held | ||
123 | */ | ||
124 | int get_blkdev_info(void *dev, int *major, char **name) | ||
125 | { | ||
126 | struct blkdev_info *info = dev; | ||
127 | |||
128 | if (info->bd == NULL) | ||
129 | return 1; | ||
130 | |||
131 | *major = info->bd->major; | ||
132 | *name = info->bd->name; | ||
133 | return 0; | ||
134 | } | 52 | } |
135 | 53 | ||
54 | #endif /* CONFIG_PROC_FS */ | ||
136 | 55 | ||
137 | int register_blkdev(unsigned int major, const char *name) | 56 | int register_blkdev(unsigned int major, const char *name) |
138 | { | 57 | { |
diff --git a/fs/char_dev.c b/fs/char_dev.c index 4e1b849f912f..f3418f7a6e9d 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/smp_lock.h> | 16 | #include <linux/smp_lock.h> |
17 | #include <linux/devfs_fs_kernel.h> | 17 | #include <linux/devfs_fs_kernel.h> |
18 | #include <linux/seq_file.h> | ||
18 | 19 | ||
19 | #include <linux/kobject.h> | 20 | #include <linux/kobject.h> |
20 | #include <linux/kobj_map.h> | 21 | #include <linux/kobj_map.h> |
@@ -27,8 +28,6 @@ | |||
27 | 28 | ||
28 | static struct kobj_map *cdev_map; | 29 | static struct kobj_map *cdev_map; |
29 | 30 | ||
30 | #define MAX_PROBE_HASH 255 /* random */ | ||
31 | |||
32 | static DEFINE_MUTEX(chrdevs_lock); | 31 | static DEFINE_MUTEX(chrdevs_lock); |
33 | 32 | ||
34 | static struct char_device_struct { | 33 | static struct char_device_struct { |
@@ -39,93 +38,29 @@ static struct char_device_struct { | |||
39 | char name[64]; | 38 | char name[64]; |
40 | struct file_operations *fops; | 39 | struct file_operations *fops; |
41 | struct cdev *cdev; /* will die */ | 40 | struct cdev *cdev; /* will die */ |
42 | } *chrdevs[MAX_PROBE_HASH]; | 41 | } *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; |
43 | 42 | ||
44 | /* index in the above */ | 43 | /* index in the above */ |
45 | static inline int major_to_index(int major) | 44 | static inline int major_to_index(int major) |
46 | { | 45 | { |
47 | return major % MAX_PROBE_HASH; | 46 | return major % CHRDEV_MAJOR_HASH_SIZE; |
48 | } | ||
49 | |||
50 | struct chrdev_info { | ||
51 | int index; | ||
52 | struct char_device_struct *cd; | ||
53 | }; | ||
54 | |||
55 | void *get_next_chrdev(void *dev) | ||
56 | { | ||
57 | struct chrdev_info *info; | ||
58 | |||
59 | if (dev == NULL) { | ||
60 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
61 | if (!info) | ||
62 | goto out; | ||
63 | info->index=0; | ||
64 | info->cd = chrdevs[info->index]; | ||
65 | if (info->cd) | ||
66 | goto out; | ||
67 | } else { | ||
68 | info = dev; | ||
69 | } | ||
70 | |||
71 | while (info->index < ARRAY_SIZE(chrdevs)) { | ||
72 | if (info->cd) | ||
73 | info->cd = info->cd->next; | ||
74 | if (info->cd) | ||
75 | goto out; | ||
76 | /* | ||
77 | * No devices on this chain, move to the next | ||
78 | */ | ||
79 | info->index++; | ||
80 | info->cd = (info->index < ARRAY_SIZE(chrdevs)) ? | ||
81 | chrdevs[info->index] : NULL; | ||
82 | if (info->cd) | ||
83 | goto out; | ||
84 | } | ||
85 | |||
86 | out: | ||
87 | return info; | ||
88 | } | ||
89 | |||
90 | void *acquire_chrdev_list(void) | ||
91 | { | ||
92 | mutex_lock(&chrdevs_lock); | ||
93 | return get_next_chrdev(NULL); | ||
94 | } | ||
95 | |||
96 | void release_chrdev_list(void *dev) | ||
97 | { | ||
98 | mutex_unlock(&chrdevs_lock); | ||
99 | kfree(dev); | ||
100 | } | 47 | } |
101 | 48 | ||
49 | #ifdef CONFIG_PROC_FS | ||
102 | 50 | ||
103 | int count_chrdev_list(void) | 51 | void chrdev_show(struct seq_file *f, off_t offset) |
104 | { | 52 | { |
105 | struct char_device_struct *cd; | 53 | struct char_device_struct *cd; |
106 | int i, count; | ||
107 | |||
108 | count = 0; | ||
109 | 54 | ||
110 | for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { | 55 | if (offset < CHRDEV_MAJOR_HASH_SIZE) { |
111 | for (cd = chrdevs[i]; cd; cd = cd->next) | 56 | mutex_lock(&chrdevs_lock); |
112 | count++; | 57 | for (cd = chrdevs[offset]; cd; cd = cd->next) |
58 | seq_printf(f, "%3d %s\n", cd->major, cd->name); | ||
59 | mutex_unlock(&chrdevs_lock); | ||
113 | } | 60 | } |
114 | |||
115 | return count; | ||
116 | } | 61 | } |
117 | 62 | ||
118 | int get_chrdev_info(void *dev, int *major, char **name) | 63 | #endif /* CONFIG_PROC_FS */ |
119 | { | ||
120 | struct chrdev_info *info = dev; | ||
121 | |||
122 | if (info->cd == NULL) | ||
123 | return 1; | ||
124 | |||
125 | *major = info->cd->major; | ||
126 | *name = info->cd->name; | ||
127 | return 0; | ||
128 | } | ||
129 | 64 | ||
130 | /* | 65 | /* |
131 | * Register a single major with a specified minor range. | 66 | * Register a single major with a specified minor range. |
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index ef5a3323f4b5..5c10ea157425 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c | |||
@@ -249,144 +249,60 @@ static int cpuinfo_open(struct inode *inode, struct file *file) | |||
249 | return seq_open(file, &cpuinfo_op); | 249 | return seq_open(file, &cpuinfo_op); |
250 | } | 250 | } |
251 | 251 | ||
252 | enum devinfo_states { | 252 | static struct file_operations proc_cpuinfo_operations = { |
253 | CHR_HDR, | 253 | .open = cpuinfo_open, |
254 | CHR_LIST, | 254 | .read = seq_read, |
255 | BLK_HDR, | 255 | .llseek = seq_lseek, |
256 | BLK_LIST, | 256 | .release = seq_release, |
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 | }; | 257 | }; |
267 | 258 | ||
268 | static void *devinfo_start(struct seq_file *f, loff_t *pos) | 259 | static int devinfo_show(struct seq_file *f, void *v) |
269 | { | 260 | { |
270 | struct devinfo_state *info = f->private; | 261 | int i = *(loff_t *) v; |
271 | 262 | ||
272 | if (*pos) { | 263 | if (i < CHRDEV_MAJOR_HASH_SIZE) { |
273 | if ((info) && (*pos <= info->num_records)) | 264 | if (i == 0) |
274 | return info; | 265 | seq_printf(f, "Character devices:\n"); |
275 | return NULL; | 266 | chrdev_show(f, i); |
267 | } else { | ||
268 | i -= CHRDEV_MAJOR_HASH_SIZE; | ||
269 | if (i == 0) | ||
270 | seq_printf(f, "\nBlock devices:\n"); | ||
271 | blkdev_show(f, i); | ||
276 | } | 272 | } |
277 | info = kmalloc(sizeof(*info), GFP_KERNEL); | 273 | return 0; |
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 | } | 274 | } |
289 | 275 | ||
290 | static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos) | 276 | static void *devinfo_start(struct seq_file *f, loff_t *pos) |
291 | { | 277 | { |
292 | int idummy; | 278 | if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE)) |
293 | char *ndummy; | 279 | return pos; |
294 | struct devinfo_state *info = f->private; | 280 | return NULL; |
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 | /*fallthrough*/ | ||
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 | } | 281 | } |
339 | 282 | ||
340 | static void devinfo_stop(struct seq_file *f, void *v) | 283 | static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos) |
341 | { | 284 | { |
342 | struct devinfo_state *info = f->private; | 285 | (*pos)++; |
343 | 286 | if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE)) | |
344 | if (info) { | 287 | return NULL; |
345 | release_chrdev_list(info->chrdev); | 288 | return pos; |
346 | release_blkdev_list(info->blkdev); | ||
347 | f->private = NULL; | ||
348 | kfree(info); | ||
349 | } | ||
350 | } | 289 | } |
351 | 290 | ||
352 | static int devinfo_show(struct seq_file *f, void *arg) | 291 | static void devinfo_stop(struct seq_file *f, void *v) |
353 | { | 292 | { |
354 | int major; | 293 | /* Nothing to do */ |
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 | } | 294 | } |
379 | 295 | ||
380 | static struct seq_operations devinfo_op = { | 296 | static struct seq_operations devinfo_ops = { |
381 | .start = devinfo_start, | 297 | .start = devinfo_start, |
382 | .next = devinfo_next, | 298 | .next = devinfo_next, |
383 | .stop = devinfo_stop, | 299 | .stop = devinfo_stop, |
384 | .show = devinfo_show, | 300 | .show = devinfo_show |
385 | }; | 301 | }; |
386 | 302 | ||
387 | static int devinfo_open(struct inode *inode, struct file *file) | 303 | static int devinfo_open(struct inode *inode, struct file *filp) |
388 | { | 304 | { |
389 | return seq_open(file, &devinfo_op); | 305 | return seq_open(filp, &devinfo_ops); |
390 | } | 306 | } |
391 | 307 | ||
392 | static struct file_operations proc_devinfo_operations = { | 308 | static struct file_operations proc_devinfo_operations = { |
@@ -396,13 +312,6 @@ static struct file_operations proc_devinfo_operations = { | |||
396 | .release = seq_release, | 312 | .release = seq_release, |
397 | }; | 313 | }; |
398 | 314 | ||
399 | static struct file_operations proc_cpuinfo_operations = { | ||
400 | .open = cpuinfo_open, | ||
401 | .read = seq_read, | ||
402 | .llseek = seq_lseek, | ||
403 | .release = seq_release, | ||
404 | }; | ||
405 | |||
406 | extern struct seq_operations vmstat_op; | 315 | extern struct seq_operations vmstat_op; |
407 | static int vmstat_open(struct inode *inode, struct file *file) | 316 | static int vmstat_open(struct inode *inode, struct file *file) |
408 | { | 317 | { |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 20fa5f6d7269..20a7afd4590c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1413,6 +1413,7 @@ extern void bd_release_from_disk(struct block_device *, struct gendisk *); | |||
1413 | #endif | 1413 | #endif |
1414 | 1414 | ||
1415 | /* fs/char_dev.c */ | 1415 | /* fs/char_dev.c */ |
1416 | #define CHRDEV_MAJOR_HASH_SIZE 255 | ||
1416 | extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); | 1417 | extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); |
1417 | extern int register_chrdev_region(dev_t, unsigned, const char *); | 1418 | extern int register_chrdev_region(dev_t, unsigned, const char *); |
1418 | extern int register_chrdev(unsigned int, const char *, | 1419 | extern int register_chrdev(unsigned int, const char *, |
@@ -1420,25 +1421,17 @@ extern int register_chrdev(unsigned int, const char *, | |||
1420 | extern int unregister_chrdev(unsigned int, const char *); | 1421 | extern int unregister_chrdev(unsigned int, const char *); |
1421 | extern void unregister_chrdev_region(dev_t, unsigned); | 1422 | extern void unregister_chrdev_region(dev_t, unsigned); |
1422 | extern int chrdev_open(struct inode *, struct file *); | 1423 | extern int chrdev_open(struct inode *, struct file *); |
1423 | extern int get_chrdev_list(char *); | 1424 | extern void chrdev_show(struct seq_file *,off_t); |
1424 | extern void *acquire_chrdev_list(void); | ||
1425 | extern int count_chrdev_list(void); | ||
1426 | extern void *get_next_chrdev(void *); | ||
1427 | extern int get_chrdev_info(void *, int *, char **); | ||
1428 | extern void release_chrdev_list(void *); | ||
1429 | 1425 | ||
1430 | /* fs/block_dev.c */ | 1426 | /* fs/block_dev.c */ |
1427 | #define BLKDEV_MAJOR_HASH_SIZE 255 | ||
1431 | #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ | 1428 | #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ |
1432 | extern const char *__bdevname(dev_t, char *buffer); | 1429 | extern const char *__bdevname(dev_t, char *buffer); |
1433 | extern const char *bdevname(struct block_device *bdev, char *buffer); | 1430 | extern const char *bdevname(struct block_device *bdev, char *buffer); |
1434 | extern struct block_device *lookup_bdev(const char *); | 1431 | extern struct block_device *lookup_bdev(const char *); |
1435 | extern struct block_device *open_bdev_excl(const char *, int, void *); | 1432 | extern struct block_device *open_bdev_excl(const char *, int, void *); |
1436 | extern void close_bdev_excl(struct block_device *); | 1433 | extern void close_bdev_excl(struct block_device *); |
1437 | extern void *acquire_blkdev_list(void); | 1434 | extern void blkdev_show(struct seq_file *,off_t); |
1438 | extern int count_blkdev_list(void); | ||
1439 | extern void *get_next_blkdev(void *); | ||
1440 | extern int get_blkdev_info(void *, int *, char **); | ||
1441 | extern void release_blkdev_list(void *); | ||
1442 | 1435 | ||
1443 | extern void init_special_inode(struct inode *, umode_t, dev_t); | 1436 | extern void init_special_inode(struct inode *, umode_t, dev_t); |
1444 | 1437 | ||