Annotation of /trunk/kernel26-magellan/patches-2.6.16-r12/0114-2.6.16.12-proc.patch
Parent Directory | Revision Log
Revision 72 -
(hide annotations)
(download)
Mon Jun 5 09:25:38 2006 UTC (18 years, 3 months ago) by niro
File size: 13636 byte(s)
Mon Jun 5 09:25:38 2006 UTC (18 years, 3 months ago) by niro
File size: 13636 byte(s)
ver bump to 2.6.16-r12: - updated to linux-2.6.16.19 - updated to ck11
1 | niro | 72 | 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 |