Magellan Linux

Contents of /trunk/kernel26-magellan/patches-2.6.16-r10/0114-2.6.16.12-proc.patch

Parent Directory Parent Directory | Revision Log Revision Log


Revision 70 - (show annotations) (download)
Thu May 11 19:09:22 2006 UTC (18 years ago) by niro
File size: 13636 byte(s)
import

1 From: Andrew Morton <akpm@osdl.org>
2 Date: Fri, 21 Apr 2006 08:51:36 +0000 (-0700)
3 Subject: [PATCH] Simplify proc/devices and fix early termination regression
4 X-Git-Url: http://www.kernel.org/git/?p=linux/kernel/git/stable/linux-2.6.16.y.git;a=commitdiff;h=692c0509fd0719406f8f781d9a9f2e19aa6b7c0a
5
6 [PATCH] Simplify proc/devices and fix early termination regression
7
8 Repair /proc/devices early-termination regression.
9
10 2.6.16 broke /proc/devices. An application often gets an
11 EOF before the end of data is reached, if that application
12 uses a series of short read(2)s to access the data. I have
13 used read buffers of varying sizes with varying degrees
14 of unsuccess (larger sizes get further into the data than
15 smaller sizes, following a simple pattern). It appears
16 that the only safe way to get the data is to use a single
17 read buffer larger than all the data in /proc/devices.
18
19 The following example demonstates the problem:
20
21 # dd if=/proc/devices bs=1
22 Character devices:
23 1 mem
24 27+0 records in
25 27+0 records out
26
27 This patch is a backport of the fix recently accepted to
28 Linus's tree:
29
30 commit 68eef3b4791572ecb70249c7fb145bb3742dd899
31 [PATCH] Simplify proc/devices and fix early termination regression
32
33 It replaces the complex, state-machine algorithm introduced
34 in 2.6.16 with a simple algorithm, modeled on the implementation
35 of /proc/interrupts.
36
37 [akpm@osdl.org: cleanups, simplifications]
38
39 Signed-off-by: Joe Korty <joe.korty@ccur.com>
40 Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
41 ---
42
43 --- a/block/genhd.c
44 +++ b/block/genhd.c
45 @@ -16,8 +16,6 @@
46 #include <linux/kobj_map.h>
47 #include <linux/buffer_head.h>
48
49 -#define MAX_PROBE_HASH 255 /* random */
50 -
51 static struct subsystem block_subsys;
52
53 static DECLARE_MUTEX(block_subsys_sem);
54 @@ -30,108 +28,29 @@ static struct blk_major_name {
55 struct blk_major_name *next;
56 int major;
57 char name[16];
58 -} *major_names[MAX_PROBE_HASH];
59 +} *major_names[BLKDEV_MAJOR_HASH_SIZE];
60
61 /* index in the above - for now: assume no multimajor ranges */
62 static inline int major_to_index(int major)
63 {
64 - return major % MAX_PROBE_HASH;
65 -}
66 -
67 -struct blkdev_info {
68 - int index;
69 - struct blk_major_name *bd;
70 -};
71 -
72 -/*
73 - * iterate over a list of blkdev_info structures. allows
74 - * the major_names array to be iterated over from outside this file
75 - * must be called with the block_subsys_sem held
76 - */
77 -void *get_next_blkdev(void *dev)
78 -{
79 - struct blkdev_info *info;
80 -
81 - if (dev == NULL) {
82 - info = kmalloc(sizeof(*info), GFP_KERNEL);
83 - if (!info)
84 - goto out;
85 - info->index=0;
86 - info->bd = major_names[info->index];
87 - if (info->bd)
88 - goto out;
89 - } else {
90 - info = dev;
91 - }
92 -
93 - while (info->index < ARRAY_SIZE(major_names)) {
94 - if (info->bd)
95 - info->bd = info->bd->next;
96 - if (info->bd)
97 - goto out;
98 - /*
99 - * No devices on this chain, move to the next
100 - */
101 - info->index++;
102 - info->bd = (info->index < ARRAY_SIZE(major_names)) ?
103 - major_names[info->index] : NULL;
104 - if (info->bd)
105 - goto out;
106 - }
107 -
108 -out:
109 - return info;
110 -}
111 -
112 -void *acquire_blkdev_list(void)
113 -{
114 - down(&block_subsys_sem);
115 - return get_next_blkdev(NULL);
116 -}
117 -
118 -void release_blkdev_list(void *dev)
119 -{
120 - up(&block_subsys_sem);
121 - kfree(dev);
122 + return major % BLKDEV_MAJOR_HASH_SIZE;
123 }
124
125 +#ifdef CONFIG_PROC_FS
126
127 -/*
128 - * Count the number of records in the blkdev_list.
129 - * must be called with the block_subsys_sem held
130 - */
131 -int count_blkdev_list(void)
132 +void blkdev_show(struct seq_file *f, off_t offset)
133 {
134 - struct blk_major_name *n;
135 - int i, count;
136 -
137 - count = 0;
138 + struct blk_major_name *dp;
139
140 - for (i = 0; i < ARRAY_SIZE(major_names); i++) {
141 - for (n = major_names[i]; n; n = n->next)
142 - count++;
143 + if (offset < BLKDEV_MAJOR_HASH_SIZE) {
144 + down(&block_subsys_sem);
145 + for (dp = major_names[offset]; dp; dp = dp->next)
146 + seq_printf(f, "%3d %s\n", dp->major, dp->name);
147 + up(&block_subsys_sem);
148 }
149 -
150 - return count;
151 -}
152 -
153 -/*
154 - * extract the major and name values from a blkdev_info struct
155 - * passed in as a void to *dev. Must be called with
156 - * block_subsys_sem held
157 - */
158 -int get_blkdev_info(void *dev, int *major, char **name)
159 -{
160 - struct blkdev_info *info = dev;
161 -
162 - if (info->bd == NULL)
163 - return 1;
164 -
165 - *major = info->bd->major;
166 - *name = info->bd->name;
167 - return 0;
168 }
169
170 +#endif /* CONFIG_PROC_FS */
171
172 int register_blkdev(unsigned int major, const char *name)
173 {
174 --- a/fs/char_dev.c
175 +++ b/fs/char_dev.c
176 @@ -15,6 +15,7 @@
177 #include <linux/module.h>
178 #include <linux/smp_lock.h>
179 #include <linux/devfs_fs_kernel.h>
180 +#include <linux/seq_file.h>
181
182 #include <linux/kobject.h>
183 #include <linux/kobj_map.h>
184 @@ -26,8 +27,6 @@
185
186 static struct kobj_map *cdev_map;
187
188 -#define MAX_PROBE_HASH 255 /* random */
189 -
190 static DECLARE_MUTEX(chrdevs_lock);
191
192 static struct char_device_struct {
193 @@ -38,93 +37,29 @@ static struct char_device_struct {
194 char name[64];
195 struct file_operations *fops;
196 struct cdev *cdev; /* will die */
197 -} *chrdevs[MAX_PROBE_HASH];
198 +} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
199
200 /* index in the above */
201 static inline int major_to_index(int major)
202 {
203 - return major % MAX_PROBE_HASH;
204 -}
205 -
206 -struct chrdev_info {
207 - int index;
208 - struct char_device_struct *cd;
209 -};
210 -
211 -void *get_next_chrdev(void *dev)
212 -{
213 - struct chrdev_info *info;
214 -
215 - if (dev == NULL) {
216 - info = kmalloc(sizeof(*info), GFP_KERNEL);
217 - if (!info)
218 - goto out;
219 - info->index=0;
220 - info->cd = chrdevs[info->index];
221 - if (info->cd)
222 - goto out;
223 - } else {
224 - info = dev;
225 - }
226 -
227 - while (info->index < ARRAY_SIZE(chrdevs)) {
228 - if (info->cd)
229 - info->cd = info->cd->next;
230 - if (info->cd)
231 - goto out;
232 - /*
233 - * No devices on this chain, move to the next
234 - */
235 - info->index++;
236 - info->cd = (info->index < ARRAY_SIZE(chrdevs)) ?
237 - chrdevs[info->index] : NULL;
238 - if (info->cd)
239 - goto out;
240 - }
241 -
242 -out:
243 - return info;
244 -}
245 -
246 -void *acquire_chrdev_list(void)
247 -{
248 - down(&chrdevs_lock);
249 - return get_next_chrdev(NULL);
250 -}
251 -
252 -void release_chrdev_list(void *dev)
253 -{
254 - up(&chrdevs_lock);
255 - kfree(dev);
256 + return major % CHRDEV_MAJOR_HASH_SIZE;
257 }
258
259 +#ifdef CONFIG_PROC_FS
260
261 -int count_chrdev_list(void)
262 +void chrdev_show(struct seq_file *f, off_t offset)
263 {
264 struct char_device_struct *cd;
265 - int i, count;
266 -
267 - count = 0;
268
269 - for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) {
270 - for (cd = chrdevs[i]; cd; cd = cd->next)
271 - count++;
272 + if (offset < CHRDEV_MAJOR_HASH_SIZE) {
273 + down(&chrdevs_lock);
274 + for (cd = chrdevs[offset]; cd; cd = cd->next)
275 + seq_printf(f, "%3d %s\n", cd->major, cd->name);
276 + up(&chrdevs_lock);
277 }
278 -
279 - return count;
280 }
281
282 -int get_chrdev_info(void *dev, int *major, char **name)
283 -{
284 - struct chrdev_info *info = dev;
285 -
286 - if (info->cd == NULL)
287 - return 1;
288 -
289 - *major = info->cd->major;
290 - *name = info->cd->name;
291 - return 0;
292 -}
293 +#endif /* CONFIG_PROC_FS */
294
295 /*
296 * Register a single major with a specified minor range.
297 --- a/fs/proc/proc_misc.c
298 +++ b/fs/proc/proc_misc.c
299 @@ -249,144 +249,60 @@ static int cpuinfo_open(struct inode *in
300 return seq_open(file, &cpuinfo_op);
301 }
302
303 -enum devinfo_states {
304 - CHR_HDR,
305 - CHR_LIST,
306 - BLK_HDR,
307 - BLK_LIST,
308 - DEVINFO_DONE
309 -};
310 -
311 -struct devinfo_state {
312 - void *chrdev;
313 - void *blkdev;
314 - unsigned int num_records;
315 - unsigned int cur_record;
316 - enum devinfo_states state;
317 +static struct file_operations proc_cpuinfo_operations = {
318 + .open = cpuinfo_open,
319 + .read = seq_read,
320 + .llseek = seq_lseek,
321 + .release = seq_release,
322 };
323
324 -static void *devinfo_start(struct seq_file *f, loff_t *pos)
325 +static int devinfo_show(struct seq_file *f, void *v)
326 {
327 - struct devinfo_state *info = f->private;
328 + int i = *(loff_t *) v;
329
330 - if (*pos) {
331 - if ((info) && (*pos <= info->num_records))
332 - return info;
333 - return NULL;
334 + if (i < CHRDEV_MAJOR_HASH_SIZE) {
335 + if (i == 0)
336 + seq_printf(f, "Character devices:\n");
337 + chrdev_show(f, i);
338 + } else {
339 + i -= CHRDEV_MAJOR_HASH_SIZE;
340 + if (i == 0)
341 + seq_printf(f, "\nBlock devices:\n");
342 + blkdev_show(f, i);
343 }
344 - info = kmalloc(sizeof(*info), GFP_KERNEL);
345 - f->private = info;
346 - info->chrdev = acquire_chrdev_list();
347 - info->blkdev = acquire_blkdev_list();
348 - info->state = CHR_HDR;
349 - info->num_records = count_chrdev_list();
350 - info->num_records += count_blkdev_list();
351 - info->num_records += 2; /* Character and Block headers */
352 - *pos = 1;
353 - info->cur_record = *pos;
354 - return info;
355 + return 0;
356 }
357
358 -static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos)
359 +static void *devinfo_start(struct seq_file *f, loff_t *pos)
360 {
361 - int idummy;
362 - char *ndummy;
363 - struct devinfo_state *info = f->private;
364 -
365 - switch (info->state) {
366 - case CHR_HDR:
367 - info->state = CHR_LIST;
368 - (*pos)++;
369 - /*fallthrough*/
370 - case CHR_LIST:
371 - if (get_chrdev_info(info->chrdev,&idummy,&ndummy)) {
372 - /*
373 - * The character dev list is complete
374 - */
375 - info->state = BLK_HDR;
376 - } else {
377 - info->chrdev = get_next_chrdev(info->chrdev);
378 - }
379 - (*pos)++;
380 - break;
381 - case BLK_HDR:
382 - info->state = BLK_LIST;
383 - (*pos)++;
384 - /*fallthrough*/
385 - case BLK_LIST:
386 - if (get_blkdev_info(info->blkdev,&idummy,&ndummy)) {
387 - /*
388 - * The block dev list is complete
389 - */
390 - info->state = DEVINFO_DONE;
391 - } else {
392 - info->blkdev = get_next_blkdev(info->blkdev);
393 - }
394 - (*pos)++;
395 - break;
396 - case DEVINFO_DONE:
397 - (*pos)++;
398 - info->cur_record = *pos;
399 - info = NULL;
400 - break;
401 - default:
402 - break;
403 - }
404 - if (info)
405 - info->cur_record = *pos;
406 - return info;
407 + if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
408 + return pos;
409 + return NULL;
410 }
411
412 -static void devinfo_stop(struct seq_file *f, void *v)
413 +static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos)
414 {
415 - struct devinfo_state *info = f->private;
416 -
417 - if (info) {
418 - release_chrdev_list(info->chrdev);
419 - release_blkdev_list(info->blkdev);
420 - f->private = NULL;
421 - kfree(info);
422 - }
423 + (*pos)++;
424 + if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
425 + return NULL;
426 + return pos;
427 }
428
429 -static int devinfo_show(struct seq_file *f, void *arg)
430 +static void devinfo_stop(struct seq_file *f, void *v)
431 {
432 - int major;
433 - char *name;
434 - struct devinfo_state *info = f->private;
435 -
436 - switch(info->state) {
437 - case CHR_HDR:
438 - seq_printf(f,"Character devices:\n");
439 - /* fallthrough */
440 - case CHR_LIST:
441 - if (!get_chrdev_info(info->chrdev,&major,&name))
442 - seq_printf(f,"%3d %s\n",major,name);
443 - break;
444 - case BLK_HDR:
445 - seq_printf(f,"\nBlock devices:\n");
446 - /* fallthrough */
447 - case BLK_LIST:
448 - if (!get_blkdev_info(info->blkdev,&major,&name))
449 - seq_printf(f,"%3d %s\n",major,name);
450 - break;
451 - default:
452 - break;
453 - }
454 -
455 - return 0;
456 + /* Nothing to do */
457 }
458
459 -static struct seq_operations devinfo_op = {
460 - .start = devinfo_start,
461 - .next = devinfo_next,
462 - .stop = devinfo_stop,
463 - .show = devinfo_show,
464 +static struct seq_operations devinfo_ops = {
465 + .start = devinfo_start,
466 + .next = devinfo_next,
467 + .stop = devinfo_stop,
468 + .show = devinfo_show
469 };
470
471 -static int devinfo_open(struct inode *inode, struct file *file)
472 +static int devinfo_open(struct inode *inode, struct file *filp)
473 {
474 - return seq_open(file, &devinfo_op);
475 + return seq_open(filp, &devinfo_ops);
476 }
477
478 static struct file_operations proc_devinfo_operations = {
479 @@ -396,13 +312,6 @@ static struct file_operations proc_devin
480 .release = seq_release,
481 };
482
483 -static struct file_operations proc_cpuinfo_operations = {
484 - .open = cpuinfo_open,
485 - .read = seq_read,
486 - .llseek = seq_lseek,
487 - .release = seq_release,
488 -};
489 -
490 extern struct seq_operations vmstat_op;
491 static int vmstat_open(struct inode *inode, struct file *file)
492 {
493 --- a/include/linux/fs.h
494 +++ b/include/linux/fs.h
495 @@ -1383,6 +1383,7 @@ extern int bd_claim(struct block_device
496 extern void bd_release(struct block_device *);
497
498 /* fs/char_dev.c */
499 +#define CHRDEV_MAJOR_HASH_SIZE 255
500 extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
501 extern int register_chrdev_region(dev_t, unsigned, const char *);
502 extern int register_chrdev(unsigned int, const char *,
503 @@ -1390,25 +1391,17 @@ extern int register_chrdev(unsigned int,
504 extern int unregister_chrdev(unsigned int, const char *);
505 extern void unregister_chrdev_region(dev_t, unsigned);
506 extern int chrdev_open(struct inode *, struct file *);
507 -extern int get_chrdev_list(char *);
508 -extern void *acquire_chrdev_list(void);
509 -extern int count_chrdev_list(void);
510 -extern void *get_next_chrdev(void *);
511 -extern int get_chrdev_info(void *, int *, char **);
512 -extern void release_chrdev_list(void *);
513 +extern void chrdev_show(struct seq_file *,off_t);
514
515 /* fs/block_dev.c */
516 +#define BLKDEV_MAJOR_HASH_SIZE 255
517 #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
518 extern const char *__bdevname(dev_t, char *buffer);
519 extern const char *bdevname(struct block_device *bdev, char *buffer);
520 extern struct block_device *lookup_bdev(const char *);
521 extern struct block_device *open_bdev_excl(const char *, int, void *);
522 extern void close_bdev_excl(struct block_device *);
523 -extern void *acquire_blkdev_list(void);
524 -extern int count_blkdev_list(void);
525 -extern void *get_next_blkdev(void *);
526 -extern int get_blkdev_info(void *, int *, char **);
527 -extern void release_blkdev_list(void *);
528 +extern void blkdev_show(struct seq_file *,off_t);
529
530 extern void init_special_inode(struct inode *, umode_t, dev_t);
531