aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe Korty <joe.korty@ccur.com>2006-03-31 05:30:32 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-31 15:18:53 -0500
commit68eef3b4791572ecb70249c7fb145bb3742dd899 (patch)
tree1f61fce839cec8d672ae06a423d46f0a6fcd924d
parenta2c348fe0117adced11e374329a5ea3f7c43cb41 (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.c103
-rw-r--r--fs/char_dev.c87
-rw-r--r--fs/proc/proc_misc.c163
-rw-r--r--include/linux/fs.h15
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
22static struct subsystem block_subsys; 20static struct subsystem block_subsys;
23 21
24static DEFINE_MUTEX(block_subsys_lock); 22static 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 */
37static inline int major_to_index(int major) 35static inline int major_to_index(int major)
38{ 36{
39 return major % MAX_PROBE_HASH; 37 return major % BLKDEV_MAJOR_HASH_SIZE;
40}
41
42struct 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 */
52void *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
83out:
84 return info;
85}
86
87void *acquire_blkdev_list(void)
88{
89 mutex_lock(&block_subsys_lock);
90 return get_next_blkdev(NULL);
91}
92
93void 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/* 42void 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 */
104int 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 */
124int 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
137int register_blkdev(unsigned int major, const char *name) 56int 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
28static struct kobj_map *cdev_map; 29static struct kobj_map *cdev_map;
29 30
30#define MAX_PROBE_HASH 255 /* random */
31
32static DEFINE_MUTEX(chrdevs_lock); 31static DEFINE_MUTEX(chrdevs_lock);
33 32
34static struct char_device_struct { 33static 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 */
45static inline int major_to_index(int major) 44static inline int major_to_index(int major)
46{ 45{
47 return major % MAX_PROBE_HASH; 46 return major % CHRDEV_MAJOR_HASH_SIZE;
48}
49
50struct chrdev_info {
51 int index;
52 struct char_device_struct *cd;
53};
54
55void *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
86out:
87 return info;
88}
89
90void *acquire_chrdev_list(void)
91{
92 mutex_lock(&chrdevs_lock);
93 return get_next_chrdev(NULL);
94}
95
96void 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
103int count_chrdev_list(void) 51void 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
118int 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
252enum devinfo_states { 252static 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
260struct 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
268static void *devinfo_start(struct seq_file *f, loff_t *pos) 259static 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
290static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos) 276static 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
340static void devinfo_stop(struct seq_file *f, void *v) 283static 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
352static int devinfo_show(struct seq_file *f, void *arg) 291static 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
380static struct seq_operations devinfo_op = { 296static 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
387static int devinfo_open(struct inode *inode, struct file *file) 303static 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
392static struct file_operations proc_devinfo_operations = { 308static 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
399static struct file_operations proc_cpuinfo_operations = {
400 .open = cpuinfo_open,
401 .read = seq_read,
402 .llseek = seq_lseek,
403 .release = seq_release,
404};
405
406extern struct seq_operations vmstat_op; 315extern struct seq_operations vmstat_op;
407static int vmstat_open(struct inode *inode, struct file *file) 316static 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
1416extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); 1417extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
1417extern int register_chrdev_region(dev_t, unsigned, const char *); 1418extern int register_chrdev_region(dev_t, unsigned, const char *);
1418extern int register_chrdev(unsigned int, const char *, 1419extern int register_chrdev(unsigned int, const char *,
@@ -1420,25 +1421,17 @@ extern int register_chrdev(unsigned int, const char *,
1420extern int unregister_chrdev(unsigned int, const char *); 1421extern int unregister_chrdev(unsigned int, const char *);
1421extern void unregister_chrdev_region(dev_t, unsigned); 1422extern void unregister_chrdev_region(dev_t, unsigned);
1422extern int chrdev_open(struct inode *, struct file *); 1423extern int chrdev_open(struct inode *, struct file *);
1423extern int get_chrdev_list(char *); 1424extern void chrdev_show(struct seq_file *,off_t);
1424extern void *acquire_chrdev_list(void);
1425extern int count_chrdev_list(void);
1426extern void *get_next_chrdev(void *);
1427extern int get_chrdev_info(void *, int *, char **);
1428extern 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 */
1432extern const char *__bdevname(dev_t, char *buffer); 1429extern const char *__bdevname(dev_t, char *buffer);
1433extern const char *bdevname(struct block_device *bdev, char *buffer); 1430extern const char *bdevname(struct block_device *bdev, char *buffer);
1434extern struct block_device *lookup_bdev(const char *); 1431extern struct block_device *lookup_bdev(const char *);
1435extern struct block_device *open_bdev_excl(const char *, int, void *); 1432extern struct block_device *open_bdev_excl(const char *, int, void *);
1436extern void close_bdev_excl(struct block_device *); 1433extern void close_bdev_excl(struct block_device *);
1437extern void *acquire_blkdev_list(void); 1434extern void blkdev_show(struct seq_file *,off_t);
1438extern int count_blkdev_list(void);
1439extern void *get_next_blkdev(void *);
1440extern int get_blkdev_info(void *, int *, char **);
1441extern void release_blkdev_list(void *);
1442 1435
1443extern void init_special_inode(struct inode *, umode_t, dev_t); 1436extern void init_special_inode(struct inode *, umode_t, dev_t);
1444 1437