summaryrefslogtreecommitdiff
path: root/block
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2006-04-21 01:51:36 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2006-05-01 12:03:43 -0700
commit692c0509fd0719406f8f781d9a9f2e19aa6b7c0a (patch)
tree947a3bfc5610fd194785f9e081ec6439c3972deb /block
parentebea8457d4b94864a818ae3e6a95655602244935 (diff)
[PATCH] Simplify proc/devices and fix early termination regression
Repair /proc/devices early-termination regression. 2.6.16 broke /proc/devices. An application often gets an EOF before the end of data is reached, if that application uses a series of short read(2)s to access the data. I have used read buffers of varying sizes with varying degrees of unsuccess (larger sizes get further into the data than smaller sizes, following a simple pattern). It appears that the only safe way to get the data is to use a single read buffer larger than all the data in /proc/devices. The following example demonstates the problem: # dd if=/proc/devices bs=1 Character devices: 1 mem 27+0 records in 27+0 records out This patch is a backport of the fix recently accepted to Linus's tree: commit 68eef3b4791572ecb70249c7fb145bb3742dd899 [PATCH] Simplify proc/devices and fix early termination regression It replaces the complex, state-machine algorithm introduced in 2.6.16 with a simple algorithm, modeled on the implementation of /proc/interrupts. [akpm@osdl.org: cleanups, simplifications] Signed-off-by: Joe Korty <joe.korty@ccur.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'block')
-rw-r--r--block/genhd.c103
1 files changed, 11 insertions, 92 deletions
diff --git a/block/genhd.c b/block/genhd.c
index db57546a709d..7f406f97c0f3 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -16,8 +16,6 @@
#include <linux/kobj_map.h>
#include <linux/buffer_head.h>
-#define MAX_PROBE_HASH 255 /* random */
-
static struct subsystem block_subsys;
static DECLARE_MUTEX(block_subsys_sem);
@@ -30,108 +28,29 @@ static struct blk_major_name {
struct blk_major_name *next;
int major;
char name[16];
-} *major_names[MAX_PROBE_HASH];
+} *major_names[BLKDEV_MAJOR_HASH_SIZE];
/* index in the above - for now: assume no multimajor ranges */
static inline int major_to_index(int major)
{
- return major % MAX_PROBE_HASH;
-}
-
-struct blkdev_info {
- int index;
- struct blk_major_name *bd;
-};
-
-/*
- * iterate over a list of blkdev_info structures. allows
- * the major_names array to be iterated over from outside this file
- * must be called with the block_subsys_sem held
- */
-void *get_next_blkdev(void *dev)
-{
- struct blkdev_info *info;
-
- if (dev == NULL) {
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- goto out;
- info->index=0;
- info->bd = major_names[info->index];
- if (info->bd)
- goto out;
- } else {
- info = dev;
- }
-
- while (info->index < ARRAY_SIZE(major_names)) {
- if (info->bd)
- info->bd = info->bd->next;
- if (info->bd)
- goto out;
- /*
- * No devices on this chain, move to the next
- */
- info->index++;
- info->bd = (info->index < ARRAY_SIZE(major_names)) ?
- major_names[info->index] : NULL;
- if (info->bd)
- goto out;
- }
-
-out:
- return info;
-}
-
-void *acquire_blkdev_list(void)
-{
- down(&block_subsys_sem);
- return get_next_blkdev(NULL);
-}
-
-void release_blkdev_list(void *dev)
-{
- up(&block_subsys_sem);
- kfree(dev);
+ return major % BLKDEV_MAJOR_HASH_SIZE;
}
+#ifdef CONFIG_PROC_FS
-/*
- * Count the number of records in the blkdev_list.
- * must be called with the block_subsys_sem held
- */
-int count_blkdev_list(void)
+void blkdev_show(struct seq_file *f, off_t offset)
{
- struct blk_major_name *n;
- int i, count;
+ struct blk_major_name *dp;
- count = 0;
-
- for (i = 0; i < ARRAY_SIZE(major_names); i++) {
- for (n = major_names[i]; n; n = n->next)
- count++;
+ if (offset < BLKDEV_MAJOR_HASH_SIZE) {
+ down(&block_subsys_sem);
+ for (dp = major_names[offset]; dp; dp = dp->next)
+ seq_printf(f, "%3d %s\n", dp->major, dp->name);
+ up(&block_subsys_sem);
}
-
- return count;
-}
-
-/*
- * extract the major and name values from a blkdev_info struct
- * passed in as a void to *dev. Must be called with
- * block_subsys_sem held
- */
-int get_blkdev_info(void *dev, int *major, char **name)
-{
- struct blkdev_info *info = dev;
-
- if (info->bd == NULL)
- return 1;
-
- *major = info->bd->major;
- *name = info->bd->name;
- return 0;
}
+#endif /* CONFIG_PROC_FS */
int register_blkdev(unsigned int major, const char *name)
{