[PATCH] md: fix various bugs with aligned reads in RAID5
[linux-2.6.git] / block / genhd.c
index f1ed83f..457fdac 100644 (file)
@@ -2,7 +2,6 @@
  *  gendisk handling
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/genhd.h>
 #include <linux/kmod.h>
 #include <linux/kobj_map.h>
 #include <linux/buffer_head.h>
+#include <linux/mutex.h>
 
-#define MAX_PROBE_HASH 255     /* random */
-
-static struct subsystem block_subsys;
-
-static DECLARE_MUTEX(block_subsys_sem);
+struct subsystem block_subsys;
+static DEFINE_MUTEX(block_subsys_lock);
 
 /*
  * Can be deleted altogether. Later.
@@ -30,49 +27,36 @@ 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;
+       return major % BLKDEV_MAJOR_HASH_SIZE;
 }
 
 #ifdef CONFIG_PROC_FS
-/* get block device names in somewhat random order */
-int get_blkdev_list(char *p, int used)
-{
-       struct blk_major_name *n;
-       int i, len;
-
-       len = snprintf(p, (PAGE_SIZE-used), "\nBlock devices:\n");
-
-       down(&block_subsys_sem);
-       for (i = 0; i < ARRAY_SIZE(major_names); i++) {
-               for (n = major_names[i]; n; n = n->next) {
-                       /*
-                        * If the curent string plus the 5 extra characters
-                        * in the line would run us off the page, then we're done
-                        */
-                       if ((len + used + strlen(n->name) + 5) >= PAGE_SIZE)
-                               goto page_full;
-                       len += sprintf(p+len, "%3d %s\n",
-                                      n->major, n->name);
-               }
-       }
-page_full:
-       up(&block_subsys_sem);
 
-       return len;
+void blkdev_show(struct seq_file *f, off_t offset)
+{
+       struct blk_major_name *dp;
+
+       if (offset < BLKDEV_MAJOR_HASH_SIZE) {
+               mutex_lock(&block_subsys_lock);
+               for (dp = major_names[offset]; dp; dp = dp->next)
+                       seq_printf(f, "%3d %s\n", dp->major, dp->name);
+               mutex_unlock(&block_subsys_lock);
+       }
 }
-#endif
+
+#endif /* CONFIG_PROC_FS */
 
 int register_blkdev(unsigned int major, const char *name)
 {
        struct blk_major_name **n, *p;
        int index, ret = 0;
 
-       down(&block_subsys_sem);
+       mutex_lock(&block_subsys_lock);
 
        /* temporary */
        if (major == 0) {
@@ -117,7 +101,7 @@ int register_blkdev(unsigned int major, const char *name)
                kfree(p);
        }
 out:
-       up(&block_subsys_sem);
+       mutex_unlock(&block_subsys_lock);
        return ret;
 }
 
@@ -131,7 +115,7 @@ int unregister_blkdev(unsigned int major, const char *name)
        int index = major_to_index(major);
        int ret = 0;
 
-       down(&block_subsys_sem);
+       mutex_lock(&block_subsys_lock);
        for (n = &major_names[index]; *n; n = &(*n)->next)
                if ((*n)->major == major)
                        break;
@@ -141,7 +125,7 @@ int unregister_blkdev(unsigned int major, const char *name)
                p = *n;
                *n = p->next;
        }
-       up(&block_subsys_sem);
+       mutex_unlock(&block_subsys_lock);
        kfree(p);
 
        return ret;
@@ -235,7 +219,7 @@ static void *part_start(struct seq_file *part, loff_t *pos)
        struct list_head *p;
        loff_t l = *pos;
 
-       down(&block_subsys_sem);
+       mutex_lock(&block_subsys_lock);
        list_for_each(p, &block_subsys.kset.list)
                if (!l--)
                        return list_entry(p, struct gendisk, kobj.entry);
@@ -252,7 +236,7 @@ static void *part_next(struct seq_file *part, void *v, loff_t *pos)
 
 static void part_stop(struct seq_file *part, void *v)
 {
-       up(&block_subsys_sem);
+       mutex_unlock(&block_subsys_lock);
 }
 
 static int show_partition(struct seq_file *part, void *v)
@@ -311,10 +295,15 @@ static struct kobject *base_probe(dev_t dev, int *part, void *data)
 
 static int __init genhd_device_init(void)
 {
-       bdev_map = kobj_map_init(base_probe, &block_subsys_sem);
+       int err;
+
+       bdev_map = kobj_map_init(base_probe, &block_subsys_lock);
        blk_dev_init();
-       subsystem_register(&block_subsys);
-       return 0;
+       err = subsystem_register(&block_subsys);
+       if (err < 0)
+               printk(KERN_WARNING "%s: subsystem_register error: %d\n",
+                       __FUNCTION__, err);
+       return err;
 }
 
 subsys_initcall(genhd_device_init);
@@ -387,8 +376,8 @@ static ssize_t disk_stats_read(struct gendisk * disk, char *page)
        disk_round_stats(disk);
        preempt_enable();
        return sprintf(page,
-               "%8u %8u %8llu %8u "
-               "%8u %8u %8llu %8u "
+               "%8lu %8lu %8llu %8u "
+               "%8lu %8lu %8llu %8u "
                "%8u %8u %8u"
                "\n",
                disk_stat_read(disk, ios[READ]),
@@ -428,6 +417,34 @@ static struct disk_attribute disk_attr_stat = {
        .show   = disk_stats_read
 };
 
+#ifdef CONFIG_FAIL_MAKE_REQUEST
+
+static ssize_t disk_fail_store(struct gendisk * disk,
+                              const char *buf, size_t count)
+{
+       int i;
+
+       if (count > 0 && sscanf(buf, "%d", &i) > 0) {
+               if (i == 0)
+                       disk->flags &= ~GENHD_FL_FAIL;
+               else
+                       disk->flags |= GENHD_FL_FAIL;
+       }
+
+       return count;
+}
+static ssize_t disk_fail_read(struct gendisk * disk, char *page)
+{
+       return sprintf(page, "%d\n", disk->flags & GENHD_FL_FAIL ? 1 : 0);
+}
+static struct disk_attribute disk_attr_fail = {
+       .attr = {.name = "make-it-fail", .mode = S_IRUGO | S_IWUSR },
+       .store  = disk_fail_store,
+       .show   = disk_fail_read
+};
+
+#endif
+
 static struct attribute * default_attrs[] = {
        &disk_attr_uevent.attr,
        &disk_attr_dev.attr,
@@ -435,6 +452,9 @@ static struct attribute * default_attrs[] = {
        &disk_attr_removable.attr,
        &disk_attr_size.attr,
        &disk_attr_stat.attr,
+#ifdef CONFIG_FAIL_MAKE_REQUEST
+       &disk_attr_fail.attr,
+#endif
        NULL,
 };
 
@@ -525,9 +545,7 @@ static struct kset_uevent_ops block_uevent_ops = {
        .uevent         = block_uevent,
 };
 
-/* declare block_subsys. */
-static decl_subsys(block, &ktype_block, &block_uevent_ops);
-
+decl_subsys(block, &ktype_block, &block_uevent_ops);
 
 /*
  * aggregate disk stat collector.  Uses the same stats that the sysfs
@@ -545,7 +563,7 @@ static void *diskstats_start(struct seq_file *part, loff_t *pos)
        loff_t k = *pos;
        struct list_head *p;
 
-       down(&block_subsys_sem);
+       mutex_lock(&block_subsys_lock);
        list_for_each(p, &block_subsys.kset.list)
                if (!k--)
                        return list_entry(p, struct gendisk, kobj.entry);
@@ -562,7 +580,7 @@ static void *diskstats_next(struct seq_file *part, void *v, loff_t *pos)
 
 static void diskstats_stop(struct seq_file *part, void *v)
 {
-       up(&block_subsys_sem);
+       mutex_unlock(&block_subsys_lock);
 }
 
 static int diskstats_show(struct seq_file *s, void *v)
@@ -582,7 +600,7 @@ static int diskstats_show(struct seq_file *s, void *v)
        preempt_disable();
        disk_round_stats(gp);
        preempt_enable();
-       seq_printf(s, "%4d %4d %s %u %u %llu %u %u %u %llu %u %u %u %u\n",
+       seq_printf(s, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n",
                gp->major, n + gp->first_minor, disk_name(gp, n, buf),
                disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]),
                (unsigned long long)disk_stat_read(gp, sectors[0]),