raw1394: fix whitespace after x86_64 compat patch
[safe/jmp/linux-2.6] / drivers / md / md.c
index 07f180f..f19b874 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/buffer_head.h> /* for invalidate_bdev */
 #include <linux/suspend.h>
 #include <linux/poll.h>
+#include <linux/mutex.h>
 
 #include <linux/init.h>
 
@@ -81,10 +82,22 @@ static DEFINE_SPINLOCK(pers_lock);
  * idle IO detection.
  *
  * you can change it via /proc/sys/dev/raid/speed_limit_min and _max.
+ * or /sys/block/mdX/md/sync_speed_{min,max}
  */
 
 static int sysctl_speed_limit_min = 1000;
 static int sysctl_speed_limit_max = 200000;
+static inline int speed_min(mddev_t *mddev)
+{
+       return mddev->sync_speed_min ?
+               mddev->sync_speed_min : sysctl_speed_limit_min;
+}
+
+static inline int speed_max(mddev_t *mddev)
+{
+       return mddev->sync_speed_max ?
+               mddev->sync_speed_max : sysctl_speed_limit_max;
+}
 
 static struct ctl_table_header *raid_table_header;
 
@@ -144,12 +157,23 @@ static int start_readonly;
  *  start array, stop array, error, add device, remove device,
  *  start build, activate spare
  */
-DECLARE_WAIT_QUEUE_HEAD(md_event_waiters);
+static DECLARE_WAIT_QUEUE_HEAD(md_event_waiters);
 static atomic_t md_event_count;
 void md_new_event(mddev_t *mddev)
 {
        atomic_inc(&md_event_count);
        wake_up(&md_event_waiters);
+       sysfs_notify(&mddev->kobj, NULL, "sync_action");
+}
+EXPORT_SYMBOL_GPL(md_new_event);
+
+/* Alternate version that can be called from interrupts
+ * when calling sysfs_notify isn't needed.
+ */
+void md_new_event_inintr(mddev_t *mddev)
+{
+       atomic_inc(&md_event_count);
+       wake_up(&md_event_waiters);
 }
 
 /*
@@ -201,10 +225,11 @@ static void mddev_put(mddev_t *mddev)
                return;
        if (!mddev->raid_disks && list_empty(&mddev->disks)) {
                list_del(&mddev->all_mddevs);
-               blk_put_queue(mddev->queue);
+               spin_unlock(&all_mddevs_lock);
+               blk_cleanup_queue(mddev->queue);
                kobject_unregister(&mddev->kobj);
-       }
-       spin_unlock(&all_mddevs_lock);
+       } else
+               spin_unlock(&all_mddevs_lock);
 }
 
 static mddev_t * mddev_find(dev_t unit)
@@ -238,7 +263,7 @@ static mddev_t * mddev_find(dev_t unit)
        else
                new->md_minor = MINOR(unit) >> MdpMinorShift;
 
-       init_MUTEX(&new->reconfig_sem);
+       mutex_init(&new->reconfig_mutex);
        INIT_LIST_HEAD(&new->disks);
        INIT_LIST_HEAD(&new->all_mddevs);
        init_timer(&new->safemode_timer);
@@ -251,6 +276,7 @@ static mddev_t * mddev_find(dev_t unit)
                kfree(new);
                return NULL;
        }
+       set_bit(QUEUE_FLAG_CLUSTER, &new->queue->queue_flags);
 
        blk_queue_make_request(new->queue, md_fail_request);
 
@@ -259,27 +285,22 @@ static mddev_t * mddev_find(dev_t unit)
 
 static inline int mddev_lock(mddev_t * mddev)
 {
-       return down_interruptible(&mddev->reconfig_sem);
-}
-
-static inline void mddev_lock_uninterruptible(mddev_t * mddev)
-{
-       down(&mddev->reconfig_sem);
+       return mutex_lock_interruptible(&mddev->reconfig_mutex);
 }
 
 static inline int mddev_trylock(mddev_t * mddev)
 {
-       return down_trylock(&mddev->reconfig_sem);
+       return mutex_trylock(&mddev->reconfig_mutex);
 }
 
 static inline void mddev_unlock(mddev_t * mddev)
 {
-       up(&mddev->reconfig_sem);
+       mutex_unlock(&mddev->reconfig_mutex);
 
        md_wakeup_thread(mddev->thread);
 }
 
-mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr)
+static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr)
 {
        mdk_rdev_t * rdev;
        struct list_head *tmp;
@@ -303,12 +324,15 @@ static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev)
        return NULL;
 }
 
-static struct mdk_personality *find_pers(int level)
+static struct mdk_personality *find_pers(int level, char *clevel)
 {
        struct mdk_personality *pers;
-       list_for_each_entry(pers, &pers_list, list)
-               if (pers->level == level)
+       list_for_each_entry(pers, &pers_list, list) {
+               if (level != LEVEL_NONE && pers->level == level)
+                       return pers;
+               if (strcmp(pers->name, clevel)==0)
                        return pers;
+       }
        return NULL;
 }
 
@@ -642,7 +666,8 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
        }
 
        if (sb->major_version != 0 ||
-           sb->minor_version != 90) {
+           sb->minor_version < 90 ||
+           sb->minor_version > 91) {
                printk(KERN_WARNING "Bad version number %d.%d on %s\n",
                        sb->major_version, sb->minor_version,
                        b);
@@ -692,6 +717,10 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
        }
        rdev->size = calc_dev_size(rdev, sb->chunk_size);
 
+       if (rdev->size < sb->size && sb->level > 1)
+               /* "this cannot possibly happen" ... */
+               ret = -EINVAL;
+
  abort:
        return ret;
 }
@@ -715,6 +744,7 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                mddev->ctime = sb->ctime;
                mddev->utime = sb->utime;
                mddev->level = sb->level;
+               mddev->clevel[0] = 0;
                mddev->layout = sb->layout;
                mddev->raid_disks = sb->raid_disks;
                mddev->size = sb->size;
@@ -722,6 +752,20 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                mddev->bitmap_offset = 0;
                mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
 
+               if (mddev->minor_version >= 91) {
+                       mddev->reshape_position = sb->reshape_position;
+                       mddev->delta_disks = sb->delta_disks;
+                       mddev->new_level = sb->new_level;
+                       mddev->new_layout = sb->new_layout;
+                       mddev->new_chunk = sb->new_chunk;
+               } else {
+                       mddev->reshape_position = MaxSector;
+                       mddev->delta_disks = 0;
+                       mddev->new_level = mddev->level;
+                       mddev->new_layout = mddev->layout;
+                       mddev->new_chunk = mddev->chunk_size;
+               }
+
                if (sb->state & (1<<MD_SB_CLEAN))
                        mddev->recovery_cp = MaxSector;
                else {
@@ -741,7 +785,8 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
 
                if (sb->state & (1<<MD_SB_BITMAP_PRESENT) &&
                    mddev->bitmap_file == NULL) {
-                       if (mddev->level != 1 && mddev->level != 5 && mddev->level != 6
+                       if (mddev->level != 1 && mddev->level != 4
+                           && mddev->level != 5 && mddev->level != 6
                            && mddev->level != 10) {
                                /* FIXME use a better test */
                                printk(KERN_WARNING "md: bitmaps not supported for this level.\n");
@@ -815,7 +860,6 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
 
        sb->md_magic = MD_SB_MAGIC;
        sb->major_version = mddev->major_version;
-       sb->minor_version = mddev->minor_version;
        sb->patch_version = mddev->patch_version;
        sb->gvalid_words  = 0; /* ignored */
        memcpy(&sb->set_uuid0, mddev->uuid+0, 4);
@@ -834,6 +878,17 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
        sb->events_hi = (mddev->events>>32);
        sb->events_lo = (u32)mddev->events;
 
+       if (mddev->reshape_position == MaxSector)
+               sb->minor_version = 90;
+       else {
+               sb->minor_version = 91;
+               sb->reshape_position = mddev->reshape_position;
+               sb->new_level = mddev->new_level;
+               sb->delta_disks = mddev->delta_disks;
+               sb->new_layout = mddev->new_layout;
+               sb->new_chunk = mddev->new_chunk;
+       }
+       mddev->minor_version = sb->minor_version;
        if (mddev->in_sync)
        {
                sb->recovery_cp = mddev->recovery_cp;
@@ -870,10 +925,9 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
                        d->raid_disk = rdev2->raid_disk;
                else
                        d->raid_disk = rdev2->desc_nr; /* compatibility */
-               if (test_bit(Faulty, &rdev2->flags)) {
+               if (test_bit(Faulty, &rdev2->flags))
                        d->state = (1<<MD_DISK_FAULTY);
-                       failed++;
-               } else if (test_bit(In_sync, &rdev2->flags)) {
+               else if (test_bit(In_sync, &rdev2->flags)) {
                        d->state = (1<<MD_DISK_ACTIVE);
                        d->state |= (1<<MD_DISK_SYNC);
                        active++;
@@ -996,6 +1050,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
        }
        rdev->preferred_minor = 0xffff;
        rdev->data_offset = le64_to_cpu(sb->data_offset);
+       atomic_set(&rdev->corrected_errors, le32_to_cpu(sb->cnt_corrected_read));
 
        rdev->sb_size = le32_to_cpu(sb->max_dev) * 2 + 256;
        bmask = queue_hardsect_size(rdev->bdev->bd_disk->queue)-1;
@@ -1003,7 +1058,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
                rdev-> sb_size = (rdev->sb_size | bmask)+1;
 
        if (refdev == 0)
-               return 1;
+               ret = 1;
        else {
                __u64 ev1, ev2;
                struct mdp_superblock_1 *refsb = 
@@ -1023,7 +1078,9 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
                ev2 = le64_to_cpu(refsb->events);
 
                if (ev1 > ev2)
-                       return 1;
+                       ret = 1;
+               else
+                       ret = 0;
        }
        if (minor_version) 
                rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2;
@@ -1034,7 +1091,10 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
        rdev->size = le64_to_cpu(sb->data_size)/2;
        if (le32_to_cpu(sb->chunksize))
                rdev->size &= ~((sector_t)le32_to_cpu(sb->chunksize)/2 - 1);
-       return 0;
+
+       if (le32_to_cpu(sb->size) > rdev->size*2)
+               return -EINVAL;
+       return ret;
 }
 
 static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
@@ -1051,12 +1111,13 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1);
                mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1);
                mddev->level = le32_to_cpu(sb->level);
+               mddev->clevel[0] = 0;
                mddev->layout = le32_to_cpu(sb->layout);
                mddev->raid_disks = le32_to_cpu(sb->raid_disks);
                mddev->size = le64_to_cpu(sb->size)/2;
                mddev->events = le64_to_cpu(sb->events);
                mddev->bitmap_offset = 0;
-               mddev->default_bitmap_offset = 1024;
+               mddev->default_bitmap_offset = 1024 >> 9;
                
                mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
                memcpy(mddev->uuid, sb->set_uuid, 16);
@@ -1072,6 +1133,20 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                        }
                        mddev->bitmap_offset = (__s32)le32_to_cpu(sb->bitmap_offset);
                }
+               if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) {
+                       mddev->reshape_position = le64_to_cpu(sb->reshape_position);
+                       mddev->delta_disks = le32_to_cpu(sb->delta_disks);
+                       mddev->new_level = le32_to_cpu(sb->new_level);
+                       mddev->new_layout = le32_to_cpu(sb->new_layout);
+                       mddev->new_chunk = le32_to_cpu(sb->new_chunk)<<9;
+               } else {
+                       mddev->reshape_position = MaxSector;
+                       mddev->delta_disks = 0;
+                       mddev->new_level = mddev->level;
+                       mddev->new_layout = mddev->layout;
+                       mddev->new_chunk = mddev->chunk_size;
+               }
+
        } else if (mddev->pers == NULL) {
                /* Insist of good event counter while assembling */
                __u64 ev1 = le64_to_cpu(sb->events);
@@ -1134,10 +1209,23 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
        else
                sb->resync_offset = cpu_to_le64(0);
 
+       sb->cnt_corrected_read = atomic_read(&rdev->corrected_errors);
+
+       sb->raid_disks = cpu_to_le32(mddev->raid_disks);
+       sb->size = cpu_to_le64(mddev->size<<1);
+
        if (mddev->bitmap && mddev->bitmap_file == NULL) {
                sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset);
                sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET);
        }
+       if (mddev->reshape_position != MaxSector) {
+               sb->feature_map |= cpu_to_le32(MD_FEATURE_RESHAPE_ACTIVE);
+               sb->reshape_position = cpu_to_le64(mddev->reshape_position);
+               sb->new_layout = cpu_to_le32(mddev->new_layout);
+               sb->delta_disks = cpu_to_le32(mddev->delta_disks);
+               sb->new_level = cpu_to_le32(mddev->new_level);
+               sb->new_chunk = cpu_to_le32(mddev->new_chunk>>9);
+       }
 
        max_dev = 0;
        ITERATE_RDEV(mddev,rdev2,tmp)
@@ -1211,11 +1299,20 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
        mdk_rdev_t *same_pdev;
        char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
        struct kobject *ko;
+       char *s;
 
        if (rdev->mddev) {
                MD_BUG();
                return -EINVAL;
        }
+       /* make sure rdev->size exceeds mddev->size */
+       if (rdev->size && (mddev->size == 0 || rdev->size < mddev->size)) {
+               if (mddev->pers)
+                       /* Cannot change size, so fail */
+                       return -ENOSPC;
+               else
+                       mddev->size = rdev->size;
+       }
        same_pdev = match_dev_unit(mddev, rdev);
        if (same_pdev)
                printk(KERN_WARNING
@@ -1242,6 +1339,8 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
        bdevname(rdev->bdev,b);
        if (kobject_set_name(&rdev->kobj, "dev-%s", b) < 0)
                return -ENOMEM;
+       while ( (s=strchr(rdev->kobj.k_name, '/')) != NULL)
+               *s = '!';
                        
        list_add(&rdev->same_set, &mddev->disks);
        rdev->mddev = mddev;
@@ -1255,6 +1354,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
        else
                ko = &rdev->bdev->bd_disk->kobj;
        sysfs_create_link(&rdev->kobj, ko, "block");
+       bd_claim_by_disk(rdev->bdev, rdev, mddev->gendisk);
        return 0;
 }
 
@@ -1265,6 +1365,7 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev)
                MD_BUG();
                return;
        }
+       bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk);
        list_del_init(&rdev->same_set);
        printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b));
        rdev->mddev = NULL;
@@ -1447,7 +1548,7 @@ static void sync_sbs(mddev_t * mddev)
        }
 }
 
-static void md_update_sb(mddev_t * mddev)
+void md_update_sb(mddev_t * mddev)
 {
        int err;
        struct list_head *tmp;
@@ -1524,6 +1625,27 @@ repeat:
        wake_up(&mddev->sb_wait);
 
 }
+EXPORT_SYMBOL_GPL(md_update_sb);
+
+/* words written to sysfs files may, or my not, be \n terminated.
+ * We want to accept with case. For this we use cmd_match.
+ */
+static int cmd_match(const char *cmd, const char *str)
+{
+       /* See if cmd, written into a sysfs file, matches
+        * str.  They must either be the same, or cmd can
+        * have a trailing newline
+        */
+       while (*cmd && *str && *cmd == *str) {
+               cmd++;
+               str++;
+       }
+       if (*cmd == '\n')
+               cmd++;
+       if (*str || *cmd)
+               return 0;
+       return 1;
+}
 
 struct rdev_sysfs_entry {
        struct attribute attr;
@@ -1567,9 +1689,113 @@ super_show(mdk_rdev_t *rdev, char *page)
 }
 static struct rdev_sysfs_entry rdev_super = __ATTR_RO(super);
 
+static ssize_t
+errors_show(mdk_rdev_t *rdev, char *page)
+{
+       return sprintf(page, "%d\n", atomic_read(&rdev->corrected_errors));
+}
+
+static ssize_t
+errors_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+{
+       char *e;
+       unsigned long n = simple_strtoul(buf, &e, 10);
+       if (*buf && (*e == 0 || *e == '\n')) {
+               atomic_set(&rdev->corrected_errors, n);
+               return len;
+       }
+       return -EINVAL;
+}
+static struct rdev_sysfs_entry rdev_errors =
+__ATTR(errors, 0644, errors_show, errors_store);
+
+static ssize_t
+slot_show(mdk_rdev_t *rdev, char *page)
+{
+       if (rdev->raid_disk < 0)
+               return sprintf(page, "none\n");
+       else
+               return sprintf(page, "%d\n", rdev->raid_disk);
+}
+
+static ssize_t
+slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+{
+       char *e;
+       int slot = simple_strtoul(buf, &e, 10);
+       if (strncmp(buf, "none", 4)==0)
+               slot = -1;
+       else if (e==buf || (*e && *e!= '\n'))
+               return -EINVAL;
+       if (rdev->mddev->pers)
+               /* Cannot set slot in active array (yet) */
+               return -EBUSY;
+       if (slot >= rdev->mddev->raid_disks)
+               return -ENOSPC;
+       rdev->raid_disk = slot;
+       /* assume it is working */
+       rdev->flags = 0;
+       set_bit(In_sync, &rdev->flags);
+       return len;
+}
+
+
+static struct rdev_sysfs_entry rdev_slot =
+__ATTR(slot, 0644, slot_show, slot_store);
+
+static ssize_t
+offset_show(mdk_rdev_t *rdev, char *page)
+{
+       return sprintf(page, "%llu\n", (unsigned long long)rdev->data_offset);
+}
+
+static ssize_t
+offset_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+{
+       char *e;
+       unsigned long long offset = simple_strtoull(buf, &e, 10);
+       if (e==buf || (*e && *e != '\n'))
+               return -EINVAL;
+       if (rdev->mddev->pers)
+               return -EBUSY;
+       rdev->data_offset = offset;
+       return len;
+}
+
+static struct rdev_sysfs_entry rdev_offset =
+__ATTR(offset, 0644, offset_show, offset_store);
+
+static ssize_t
+rdev_size_show(mdk_rdev_t *rdev, char *page)
+{
+       return sprintf(page, "%llu\n", (unsigned long long)rdev->size);
+}
+
+static ssize_t
+rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+{
+       char *e;
+       unsigned long long size = simple_strtoull(buf, &e, 10);
+       if (e==buf || (*e && *e != '\n'))
+               return -EINVAL;
+       if (rdev->mddev->pers)
+               return -EBUSY;
+       rdev->size = size;
+       if (size < rdev->mddev->size || rdev->mddev->size == 0)
+               rdev->mddev->size = size;
+       return len;
+}
+
+static struct rdev_sysfs_entry rdev_size =
+__ATTR(size, 0644, rdev_size_show, rdev_size_store);
+
 static struct attribute *rdev_default_attrs[] = {
        &rdev_state.attr,
        &rdev_super.attr,
+       &rdev_errors.attr,
+       &rdev_slot.attr,
+       &rdev_offset.attr,
+       &rdev_size.attr,
        NULL,
 };
 static ssize_t
@@ -1649,6 +1875,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
        rdev->data_offset = 0;
        atomic_set(&rdev->nr_pending, 0);
        atomic_set(&rdev->read_errors, 0);
+       atomic_set(&rdev->corrected_errors, 0);
 
        size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
        if (!size) {
@@ -1754,15 +1981,36 @@ static ssize_t
 level_show(mddev_t *mddev, char *page)
 {
        struct mdk_personality *p = mddev->pers;
-       if (p == NULL && mddev->raid_disks == 0)
-               return 0;
-       if (mddev->level >= 0)
-               return sprintf(page, "raid%d\n", mddev->level);
-       else
+       if (p)
                return sprintf(page, "%s\n", p->name);
+       else if (mddev->clevel[0])
+               return sprintf(page, "%s\n", mddev->clevel);
+       else if (mddev->level != LEVEL_NONE)
+               return sprintf(page, "%d\n", mddev->level);
+       else
+               return 0;
+}
+
+static ssize_t
+level_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       int rv = len;
+       if (mddev->pers)
+               return -EBUSY;
+       if (len == 0)
+               return 0;
+       if (len >= sizeof(mddev->clevel))
+               return -ENOSPC;
+       strncpy(mddev->clevel, buf, len);
+       if (mddev->clevel[len-1] == '\n')
+               len--;
+       mddev->clevel[len] = 0;
+       mddev->level = LEVEL_NONE;
+       return rv;
 }
 
-static struct md_sysfs_entry md_level = __ATTR_RO(level);
+static struct md_sysfs_entry md_level =
+__ATTR(level, 0644, level_show, level_store);
 
 static ssize_t
 raid_disks_show(mddev_t *mddev, char *page)
@@ -1772,7 +2020,197 @@ raid_disks_show(mddev_t *mddev, char *page)
        return sprintf(page, "%d\n", mddev->raid_disks);
 }
 
-static struct md_sysfs_entry md_raid_disks = __ATTR_RO(raid_disks);
+static int update_raid_disks(mddev_t *mddev, int raid_disks);
+
+static ssize_t
+raid_disks_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       /* can only set raid_disks if array is not yet active */
+       char *e;
+       int rv = 0;
+       unsigned long n = simple_strtoul(buf, &e, 10);
+
+       if (!*buf || (*e && *e != '\n'))
+               return -EINVAL;
+
+       if (mddev->pers)
+               rv = update_raid_disks(mddev, n);
+       else
+               mddev->raid_disks = n;
+       return rv ? rv : len;
+}
+static struct md_sysfs_entry md_raid_disks =
+__ATTR(raid_disks, 0644, raid_disks_show, raid_disks_store);
+
+static ssize_t
+chunk_size_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%d\n", mddev->chunk_size);
+}
+
+static ssize_t
+chunk_size_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       /* can only set chunk_size if array is not yet active */
+       char *e;
+       unsigned long n = simple_strtoul(buf, &e, 10);
+
+       if (mddev->pers)
+               return -EBUSY;
+       if (!*buf || (*e && *e != '\n'))
+               return -EINVAL;
+
+       mddev->chunk_size = n;
+       return len;
+}
+static struct md_sysfs_entry md_chunk_size =
+__ATTR(chunk_size, 0644, chunk_size_show, chunk_size_store);
+
+static ssize_t
+null_show(mddev_t *mddev, char *page)
+{
+       return -EINVAL;
+}
+
+static ssize_t
+new_dev_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       /* buf must be %d:%d\n? giving major and minor numbers */
+       /* The new device is added to the array.
+        * If the array has a persistent superblock, we read the
+        * superblock to initialise info and check validity.
+        * Otherwise, only checking done is that in bind_rdev_to_array,
+        * which mainly checks size.
+        */
+       char *e;
+       int major = simple_strtoul(buf, &e, 10);
+       int minor;
+       dev_t dev;
+       mdk_rdev_t *rdev;
+       int err;
+
+       if (!*buf || *e != ':' || !e[1] || e[1] == '\n')
+               return -EINVAL;
+       minor = simple_strtoul(e+1, &e, 10);
+       if (*e && *e != '\n')
+               return -EINVAL;
+       dev = MKDEV(major, minor);
+       if (major != MAJOR(dev) ||
+           minor != MINOR(dev))
+               return -EOVERFLOW;
+
+
+       if (mddev->persistent) {
+               rdev = md_import_device(dev, mddev->major_version,
+                                       mddev->minor_version);
+               if (!IS_ERR(rdev) && !list_empty(&mddev->disks)) {
+                       mdk_rdev_t *rdev0 = list_entry(mddev->disks.next,
+                                                      mdk_rdev_t, same_set);
+                       err = super_types[mddev->major_version]
+                               .load_super(rdev, rdev0, mddev->minor_version);
+                       if (err < 0)
+                               goto out;
+               }
+       } else
+               rdev = md_import_device(dev, -1, -1);
+
+       if (IS_ERR(rdev))
+               return PTR_ERR(rdev);
+       err = bind_rdev_to_array(rdev, mddev);
+ out:
+       if (err)
+               export_rdev(rdev);
+       return err ? err : len;
+}
+
+static struct md_sysfs_entry md_new_device =
+__ATTR(new_dev, 0200, null_show, new_dev_store);
+
+static ssize_t
+size_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%llu\n", (unsigned long long)mddev->size);
+}
+
+static int update_size(mddev_t *mddev, unsigned long size);
+
+static ssize_t
+size_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       /* If array is inactive, we can reduce the component size, but
+        * not increase it (except from 0).
+        * If array is active, we can try an on-line resize
+        */
+       char *e;
+       int err = 0;
+       unsigned long long size = simple_strtoull(buf, &e, 10);
+       if (!*buf || *buf == '\n' ||
+           (*e && *e != '\n'))
+               return -EINVAL;
+
+       if (mddev->pers) {
+               err = update_size(mddev, size);
+               md_update_sb(mddev);
+       } else {
+               if (mddev->size == 0 ||
+                   mddev->size > size)
+                       mddev->size = size;
+               else
+                       err = -ENOSPC;
+       }
+       return err ? err : len;
+}
+
+static struct md_sysfs_entry md_size =
+__ATTR(component_size, 0644, size_show, size_store);
+
+
+/* Metdata version.
+ * This is either 'none' for arrays with externally managed metadata,
+ * or N.M for internally known formats
+ */
+static ssize_t
+metadata_show(mddev_t *mddev, char *page)
+{
+       if (mddev->persistent)
+               return sprintf(page, "%d.%d\n",
+                              mddev->major_version, mddev->minor_version);
+       else
+               return sprintf(page, "none\n");
+}
+
+static ssize_t
+metadata_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       int major, minor;
+       char *e;
+       if (!list_empty(&mddev->disks))
+               return -EBUSY;
+
+       if (cmd_match(buf, "none")) {
+               mddev->persistent = 0;
+               mddev->major_version = 0;
+               mddev->minor_version = 90;
+               return len;
+       }
+       major = simple_strtoul(buf, &e, 10);
+       if (e==buf || *e != '.')
+               return -EINVAL;
+       buf = e+1;
+       minor = simple_strtoul(buf, &e, 10);
+       if (e==buf || *e != '\n')
+               return -EINVAL;
+       if (major >= sizeof(super_types)/sizeof(super_types[0]) ||
+           super_types[major].name == NULL)
+               return -ENOENT;
+       mddev->major_version = major;
+       mddev->minor_version = minor;
+       mddev->persistent = 1;
+       return len;
+}
+
+static struct md_sysfs_entry md_metadata =
+__ATTR(metadata_version, 0644, metadata_show, metadata_store);
 
 static ssize_t
 action_show(mddev_t *mddev, char *page)
@@ -1780,7 +2218,9 @@ action_show(mddev_t *mddev, char *page)
        char *type = "idle";
        if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
            test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) {
-               if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
+               if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
+                       type = "reshape";
+               else if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
                        if (!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
                                type = "resync";
                        else if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
@@ -1799,31 +2239,34 @@ action_store(mddev_t *mddev, const char *page, size_t len)
        if (!mddev->pers || !mddev->pers->sync_request)
                return -EINVAL;
 
-       if (strcmp(page, "idle")==0 || strcmp(page, "idle\n")==0) {
+       if (cmd_match(page, "idle")) {
                if (mddev->sync_thread) {
                        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
                        md_unregister_thread(mddev->sync_thread);
                        mddev->sync_thread = NULL;
                        mddev->recovery = 0;
                }
-               return len;
-       }
-
-       if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
-           test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
+       } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
+                  test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
                return -EBUSY;
-       if (strcmp(page, "resync")==0 || strcmp(page, "resync\n")==0 ||
-           strcmp(page, "recover")==0 || strcmp(page, "recover\n")==0)
+       else if (cmd_match(page, "resync") || cmd_match(page, "recover"))
                set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
-       else {
-               if (strcmp(page, "check")==0 || strcmp(page, "check\n")==0)
+       else if (cmd_match(page, "reshape")) {
+               int err;
+               if (mddev->pers->start_reshape == NULL)
+                       return -EINVAL;
+               err = mddev->pers->start_reshape(mddev);
+               if (err)
+                       return err;
+       } else {
+               if (cmd_match(page, "check"))
                        set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
-               else if (strcmp(page, "repair")!=0 && strcmp(page, "repair\n")!=0)
+               else if (!cmd_match(page, "repair"))
                        return -EINVAL;
                set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
                set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
-               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
        }
+       set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
        return len;
 }
@@ -1842,15 +2285,166 @@ md_scan_mode = __ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store);
 static struct md_sysfs_entry
 md_mismatches = __ATTR_RO(mismatch_cnt);
 
+static ssize_t
+sync_min_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%d (%s)\n", speed_min(mddev),
+                      mddev->sync_speed_min ? "local": "system");
+}
+
+static ssize_t
+sync_min_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       int min;
+       char *e;
+       if (strncmp(buf, "system", 6)==0) {
+               mddev->sync_speed_min = 0;
+               return len;
+       }
+       min = simple_strtoul(buf, &e, 10);
+       if (buf == e || (*e && *e != '\n') || min <= 0)
+               return -EINVAL;
+       mddev->sync_speed_min = min;
+       return len;
+}
+
+static struct md_sysfs_entry md_sync_min =
+__ATTR(sync_speed_min, S_IRUGO|S_IWUSR, sync_min_show, sync_min_store);
+
+static ssize_t
+sync_max_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%d (%s)\n", speed_max(mddev),
+                      mddev->sync_speed_max ? "local": "system");
+}
+
+static ssize_t
+sync_max_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       int max;
+       char *e;
+       if (strncmp(buf, "system", 6)==0) {
+               mddev->sync_speed_max = 0;
+               return len;
+       }
+       max = simple_strtoul(buf, &e, 10);
+       if (buf == e || (*e && *e != '\n') || max <= 0)
+               return -EINVAL;
+       mddev->sync_speed_max = max;
+       return len;
+}
+
+static struct md_sysfs_entry md_sync_max =
+__ATTR(sync_speed_max, S_IRUGO|S_IWUSR, sync_max_show, sync_max_store);
+
+
+static ssize_t
+sync_speed_show(mddev_t *mddev, char *page)
+{
+       unsigned long resync, dt, db;
+       resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active));
+       dt = ((jiffies - mddev->resync_mark) / HZ);
+       if (!dt) dt++;
+       db = resync - (mddev->resync_mark_cnt);
+       return sprintf(page, "%ld\n", db/dt/2); /* K/sec */
+}
+
+static struct md_sysfs_entry
+md_sync_speed = __ATTR_RO(sync_speed);
+
+static ssize_t
+sync_completed_show(mddev_t *mddev, char *page)
+{
+       unsigned long max_blocks, resync;
+
+       if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
+               max_blocks = mddev->resync_max_sectors;
+       else
+               max_blocks = mddev->size << 1;
+
+       resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active));
+       return sprintf(page, "%lu / %lu\n", resync, max_blocks);
+}
+
+static struct md_sysfs_entry
+md_sync_completed = __ATTR_RO(sync_completed);
+
+static ssize_t
+suspend_lo_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_lo);
+}
+
+static ssize_t
+suspend_lo_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       char *e;
+       unsigned long long new = simple_strtoull(buf, &e, 10);
+
+       if (mddev->pers->quiesce == NULL)
+               return -EINVAL;
+       if (buf == e || (*e && *e != '\n'))
+               return -EINVAL;
+       if (new >= mddev->suspend_hi ||
+           (new > mddev->suspend_lo && new < mddev->suspend_hi)) {
+               mddev->suspend_lo = new;
+               mddev->pers->quiesce(mddev, 2);
+               return len;
+       } else
+               return -EINVAL;
+}
+static struct md_sysfs_entry md_suspend_lo =
+__ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);
+
+
+static ssize_t
+suspend_hi_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_hi);
+}
+
+static ssize_t
+suspend_hi_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       char *e;
+       unsigned long long new = simple_strtoull(buf, &e, 10);
+
+       if (mddev->pers->quiesce == NULL)
+               return -EINVAL;
+       if (buf == e || (*e && *e != '\n'))
+               return -EINVAL;
+       if ((new <= mddev->suspend_lo && mddev->suspend_lo >= mddev->suspend_hi) ||
+           (new > mddev->suspend_lo && new > mddev->suspend_hi)) {
+               mddev->suspend_hi = new;
+               mddev->pers->quiesce(mddev, 1);
+               mddev->pers->quiesce(mddev, 0);
+               return len;
+       } else
+               return -EINVAL;
+}
+static struct md_sysfs_entry md_suspend_hi =
+__ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
+
+
 static struct attribute *md_default_attrs[] = {
        &md_level.attr,
        &md_raid_disks.attr,
+       &md_chunk_size.attr,
+       &md_size.attr,
+       &md_metadata.attr,
+       &md_new_device.attr,
        NULL,
 };
 
 static struct attribute *md_redundancy_attrs[] = {
        &md_scan_mode.attr,
        &md_mismatches.attr,
+       &md_sync_min.attr,
+       &md_sync_max.attr,
+       &md_sync_speed.attr,
+       &md_sync_completed.attr,
+       &md_suspend_lo.attr,
+       &md_suspend_hi.attr,
        NULL,
 };
 static struct attribute_group md_redundancy_group = {
@@ -1868,9 +2462,11 @@ md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
 
        if (!entry->show)
                return -EIO;
-       mddev_lock(mddev);
-       rv = entry->show(mddev, page);
-       mddev_unlock(mddev);
+       rv = mddev_lock(mddev);
+       if (!rv) {
+               rv = entry->show(mddev, page);
+               mddev_unlock(mddev);
+       }
        return rv;
 }
 
@@ -1884,9 +2480,11 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
 
        if (!entry->store)
                return -EIO;
-       mddev_lock(mddev);
-       rv = entry->store(mddev, page, length);
-       mddev_unlock(mddev);
+       rv = mddev_lock(mddev);
+       if (!rv) {
+               rv = entry->store(mddev, page, length);
+               mddev_unlock(mddev);
+       }
        return rv;
 }
 
@@ -1910,7 +2508,7 @@ int mdp_major = 0;
 
 static struct kobject *md_probe(dev_t dev, int *part, void *data)
 {
-       static DECLARE_MUTEX(disks_sem);
+       static DEFINE_MUTEX(disks_mutex);
        mddev_t *mddev = mddev_find(dev);
        struct gendisk *disk;
        int partitioned = (MAJOR(dev) != MD_MAJOR);
@@ -1920,15 +2518,15 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data)
        if (!mddev)
                return NULL;
 
-       down(&disks_sem);
+       mutex_lock(&disks_mutex);
        if (mddev->gendisk) {
-               up(&disks_sem);
+               mutex_unlock(&disks_mutex);
                mddev_put(mddev);
                return NULL;
        }
        disk = alloc_disk(1 << shift);
        if (!disk) {
-               up(&disks_sem);
+               mutex_unlock(&disks_mutex);
                mddev_put(mddev);
                return NULL;
        }
@@ -1946,7 +2544,7 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data)
        disk->queue = mddev->queue;
        add_disk(disk);
        mddev->gendisk = disk;
-       up(&disks_sem);
+       mutex_unlock(&disks_mutex);
        mddev->kobj.parent = &disk->kobj;
        mddev->kobj.k_name = NULL;
        snprintf(mddev->kobj.name, KOBJ_NAME_LEN, "%s", "md");
@@ -2028,7 +2626,10 @@ static int do_md_run(mddev_t * mddev)
        }
 
 #ifdef CONFIG_KMOD
-       request_module("md-level-%d", mddev->level);
+       if (mddev->level != LEVEL_NONE)
+               request_module("md-level-%d", mddev->level);
+       else if (mddev->clevel[0])
+               request_module("md-%s", mddev->clevel);
 #endif
 
        /*
@@ -2050,15 +2651,29 @@ static int do_md_run(mddev_t * mddev)
                return -ENOMEM;
 
        spin_lock(&pers_lock);
-       pers = find_pers(mddev->level);
+       pers = find_pers(mddev->level, mddev->clevel);
        if (!pers || !try_module_get(pers->owner)) {
                spin_unlock(&pers_lock);
-               printk(KERN_WARNING "md: personality for level %d is not loaded!\n",
-                      mddev->level);
+               if (mddev->level != LEVEL_NONE)
+                       printk(KERN_WARNING "md: personality for level %d is not loaded!\n",
+                              mddev->level);
+               else
+                       printk(KERN_WARNING "md: personality for level %s is not loaded!\n",
+                              mddev->clevel);
                return -EINVAL;
        }
        mddev->pers = pers;
        spin_unlock(&pers_lock);
+       mddev->level = pers->level;
+       strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
+
+       if (mddev->reshape_position != MaxSector &&
+           pers->start_reshape == NULL) {
+               /* This personality cannot handle reshaping... */
+               mddev->pers = NULL;
+               module_put(pers->owner);
+               return -EINVAL;
+       }
 
        mddev->recovery = 0;
        mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */
@@ -2215,14 +2830,6 @@ static int do_md_stop(mddev_t * mddev, int ro)
                        set_disk_ro(disk, 1);
        }
 
-       bitmap_destroy(mddev);
-       if (mddev->bitmap_file) {
-               atomic_set(&mddev->bitmap_file->f_dentry->d_inode->i_writecount, 1);
-               fput(mddev->bitmap_file);
-               mddev->bitmap_file = NULL;
-       }
-       mddev->bitmap_offset = 0;
-
        /*
         * Free resources if final stop
         */
@@ -2232,6 +2839,14 @@ static int do_md_stop(mddev_t * mddev, int ro)
                struct gendisk *disk;
                printk(KERN_INFO "md: %s stopped.\n", mdname(mddev));
 
+               bitmap_destroy(mddev);
+               if (mddev->bitmap_file) {
+                       atomic_set(&mddev->bitmap_file->f_dentry->d_inode->i_writecount, 1);
+                       fput(mddev->bitmap_file);
+                       mddev->bitmap_file = NULL;
+               }
+               mddev->bitmap_offset = 0;
+
                ITERATE_RDEV(mddev,rdev,tmp)
                        if (rdev->raid_disk >= 0) {
                                char nm[20];
@@ -2293,7 +2908,6 @@ static void autorun_array(mddev_t *mddev)
  */
 static void autorun_devices(int part)
 {
-       struct list_head candidates;
        struct list_head *tmp;
        mdk_rdev_t *rdev0, *rdev;
        mddev_t *mddev;
@@ -2302,6 +2916,7 @@ static void autorun_devices(int part)
        printk(KERN_INFO "md: autorun ...\n");
        while (!list_empty(&pending_raid_disks)) {
                dev_t dev;
+               LIST_HEAD(candidates);
                rdev0 = list_entry(pending_raid_disks.next,
                                         mdk_rdev_t, same_set);
 
@@ -2468,6 +3083,8 @@ static int get_array_info(mddev_t * mddev, void __user * arg)
        info.ctime         = mddev->ctime;
        info.level         = mddev->level;
        info.size          = mddev->size;
+       if (info.size != mddev->size) /* overflow */
+               info.size = -1;
        info.nr_disks      = nr;
        info.raid_disks    = mddev->raid_disks;
        info.md_minor      = mddev->md_minor;
@@ -2688,12 +3305,6 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
                if (info->state & (1<<MD_DISK_WRITEMOSTLY))
                        set_bit(WriteMostly, &rdev->flags);
 
-               err = bind_rdev_to_array(rdev, mddev);
-               if (err) {
-                       export_rdev(rdev);
-                       return err;
-               }
-
                if (!mddev->persistent) {
                        printk(KERN_INFO "md: nonpersistent superblock ...\n");
                        rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
@@ -2701,8 +3312,11 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
                        rdev->sb_offset = calc_dev_sboffset(rdev->bdev);
                rdev->size = calc_dev_size(rdev, mddev->chunk_size);
 
-               if (!mddev->size || (mddev->size > rdev->size))
-                       mddev->size = rdev->size;
+               err = bind_rdev_to_array(rdev, mddev);
+               if (err) {
+                       export_rdev(rdev);
+                       return err;
+               }
        }
 
        return 0;
@@ -2774,15 +3388,6 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
        size = calc_dev_size(rdev, mddev->chunk_size);
        rdev->size = size;
 
-       if (size < mddev->size) {
-               printk(KERN_WARNING 
-                       "%s: disk size %llu blocks < array size %llu\n",
-                       mdname(mddev), (unsigned long long)size,
-                       (unsigned long long)mddev->size);
-               err = -ENOSPC;
-               goto abort_export;
-       }
-
        if (test_bit(Faulty, &rdev->flags)) {
                printk(KERN_WARNING 
                        "md: can not hot-add faulty %s disk to %s!\n",
@@ -2792,7 +3397,9 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
        }
        clear_bit(In_sync, &rdev->flags);
        rdev->desc_nr = -1;
-       bind_rdev_to_array(rdev, mddev);
+       err = bind_rdev_to_array(rdev, mddev);
+       if (err)
+               goto abort_export;
 
        /*
         * The rest should better be atomic, we can have disk failures
@@ -2934,6 +3541,7 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
        mddev->ctime         = get_seconds();
 
        mddev->level         = info->level;
+       mddev->clevel[0]     = 0;
        mddev->size          = info->size;
        mddev->raid_disks    = info->raid_disks;
        /* don't set md_minor, it is determined by which /dev/md* was
@@ -2955,14 +3563,87 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
        mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
        mddev->bitmap_offset = 0;
 
+       mddev->reshape_position = MaxSector;
+
        /*
         * Generate a 128 bit UUID
         */
        get_random_bytes(mddev->uuid, 16);
 
+       mddev->new_level = mddev->level;
+       mddev->new_chunk = mddev->chunk_size;
+       mddev->new_layout = mddev->layout;
+       mddev->delta_disks = 0;
+
        return 0;
 }
 
+static int update_size(mddev_t *mddev, unsigned long size)
+{
+       mdk_rdev_t * rdev;
+       int rv;
+       struct list_head *tmp;
+       int fit = (size == 0);
+
+       if (mddev->pers->resize == NULL)
+               return -EINVAL;
+       /* The "size" is the amount of each device that is used.
+        * This can only make sense for arrays with redundancy.
+        * linear and raid0 always use whatever space is available
+        * We can only consider changing the size if no resync
+        * or reconstruction is happening, and if the new size
+        * is acceptable. It must fit before the sb_offset or,
+        * if that is <data_offset, it must fit before the
+        * size of each device.
+        * If size is zero, we find the largest size that fits.
+        */
+       if (mddev->sync_thread)
+               return -EBUSY;
+       ITERATE_RDEV(mddev,rdev,tmp) {
+               sector_t avail;
+               if (rdev->sb_offset > rdev->data_offset)
+                       avail = (rdev->sb_offset*2) - rdev->data_offset;
+               else
+                       avail = get_capacity(rdev->bdev->bd_disk)
+                               - rdev->data_offset;
+               if (fit && (size == 0 || size > avail/2))
+                       size = avail/2;
+               if (avail < ((sector_t)size << 1))
+                       return -ENOSPC;
+       }
+       rv = mddev->pers->resize(mddev, (sector_t)size *2);
+       if (!rv) {
+               struct block_device *bdev;
+
+               bdev = bdget_disk(mddev->gendisk, 0);
+               if (bdev) {
+                       mutex_lock(&bdev->bd_inode->i_mutex);
+                       i_size_write(bdev->bd_inode, (loff_t)mddev->array_size << 10);
+                       mutex_unlock(&bdev->bd_inode->i_mutex);
+                       bdput(bdev);
+               }
+       }
+       return rv;
+}
+
+static int update_raid_disks(mddev_t *mddev, int raid_disks)
+{
+       int rv;
+       /* change the number of raid disks */
+       if (mddev->pers->check_reshape == NULL)
+               return -EINVAL;
+       if (raid_disks <= 0 ||
+           raid_disks >= mddev->max_disks)
+               return -EINVAL;
+       if (mddev->sync_thread || mddev->reshape_position != MaxSector)
+               return -EBUSY;
+       mddev->delta_disks = raid_disks - mddev->raid_disks;
+
+       rv = mddev->pers->check_reshape(mddev);
+       return rv;
+}
+
+
 /*
  * update_array_info is used to change the configuration of an
  * on-line array.
@@ -2994,7 +3675,7 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
                )
                return -EINVAL;
        /* Check there is only one change */
-       if (mddev->size != info->size) cnt++;
+       if (info->size >= 0 && mddev->size != info->size) cnt++;
        if (mddev->raid_disks != info->raid_disks) cnt++;
        if (mddev->layout != info->layout) cnt++;
        if ((state ^ info->state) & (1<<MD_SB_BITMAP_PRESENT)) cnt++;
@@ -3011,71 +3692,12 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
                else
                        return mddev->pers->reconfig(mddev, info->layout, -1);
        }
-       if (mddev->size != info->size) {
-               mdk_rdev_t * rdev;
-               struct list_head *tmp;
-               if (mddev->pers->resize == NULL)
-                       return -EINVAL;
-               /* The "size" is the amount of each device that is used.
-                * This can only make sense for arrays with redundancy.
-                * linear and raid0 always use whatever space is available
-                * We can only consider changing the size if no resync
-                * or reconstruction is happening, and if the new size
-                * is acceptable. It must fit before the sb_offset or,
-                * if that is <data_offset, it must fit before the
-                * size of each device.
-                * If size is zero, we find the largest size that fits.
-                */
-               if (mddev->sync_thread)
-                       return -EBUSY;
-               ITERATE_RDEV(mddev,rdev,tmp) {
-                       sector_t avail;
-                       int fit = (info->size == 0);
-                       if (rdev->sb_offset > rdev->data_offset)
-                               avail = (rdev->sb_offset*2) - rdev->data_offset;
-                       else
-                               avail = get_capacity(rdev->bdev->bd_disk)
-                                       - rdev->data_offset;
-                       if (fit && (info->size == 0 || info->size > avail/2))
-                               info->size = avail/2;
-                       if (avail < ((sector_t)info->size << 1))
-                               return -ENOSPC;
-               }
-               rv = mddev->pers->resize(mddev, (sector_t)info->size *2);
-               if (!rv) {
-                       struct block_device *bdev;
-
-                       bdev = bdget_disk(mddev->gendisk, 0);
-                       if (bdev) {
-                               down(&bdev->bd_inode->i_sem);
-                               i_size_write(bdev->bd_inode, mddev->array_size << 10);
-                               up(&bdev->bd_inode->i_sem);
-                               bdput(bdev);
-                       }
-               }
-       }
-       if (mddev->raid_disks    != info->raid_disks) {
-               /* change the number of raid disks */
-               if (mddev->pers->reshape == NULL)
-                       return -EINVAL;
-               if (info->raid_disks <= 0 ||
-                   info->raid_disks >= mddev->max_disks)
-                       return -EINVAL;
-               if (mddev->sync_thread)
-                       return -EBUSY;
-               rv = mddev->pers->reshape(mddev, info->raid_disks);
-               if (!rv) {
-                       struct block_device *bdev;
-
-                       bdev = bdget_disk(mddev->gendisk, 0);
-                       if (bdev) {
-                               down(&bdev->bd_inode->i_sem);
-                               i_size_write(bdev->bd_inode, mddev->array_size << 10);
-                               up(&bdev->bd_inode->i_sem);
-                               bdput(bdev);
-                       }
-               }
-       }
+       if (info->size >= 0 && mddev->size != info->size)
+               rv = update_size(mddev, info->size);
+
+       if (mddev->raid_disks    != info->raid_disks)
+               rv = update_raid_disks(mddev, info->raid_disks);
+
        if ((state ^ info->state) & (1<<MD_SB_BITMAP_PRESENT)) {
                if (mddev->pers->quiesce == NULL)
                        return -EINVAL;
@@ -3124,12 +3746,21 @@ static int set_disk_faulty(mddev_t *mddev, dev_t dev)
        return 0;
 }
 
+static int md_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+       mddev_t *mddev = bdev->bd_disk->private_data;
+
+       geo->heads = 2;
+       geo->sectors = 4;
+       geo->cylinders = get_capacity(mddev->gendisk) / 8;
+       return 0;
+}
+
 static int md_ioctl(struct inode *inode, struct file *file,
                        unsigned int cmd, unsigned long arg)
 {
        int err = 0;
        void __user *argp = (void __user *)arg;
-       struct hd_geometry __user *loc = argp;
        mddev_t *mddev = NULL;
 
        if (!capable(CAP_SYS_ADMIN))
@@ -3291,24 +3922,6 @@ static int md_ioctl(struct inode *inode, struct file *file,
         * 4 sectors (with a BIG number of cylinders...). This drives
         * dosfs just mad... ;-)
         */
-               case HDIO_GETGEO:
-                       if (!loc) {
-                               err = -EINVAL;
-                               goto abort_unlock;
-                       }
-                       err = put_user (2, (char __user *) &loc->heads);
-                       if (err)
-                               goto abort_unlock;
-                       err = put_user (4, (char __user *) &loc->sectors);
-                       if (err)
-                               goto abort_unlock;
-                       err = put_user(get_capacity(mddev->gendisk)/8,
-                                       (short __user *) &loc->cylinders);
-                       if (err)
-                               goto abort_unlock;
-                       err = put_user (get_start_sect(inode->i_bdev),
-                                               (long __user *) &loc->start);
-                       goto done_unlock;
        }
 
        /*
@@ -3437,6 +4050,7 @@ static struct block_device_operations md_fops =
        .open           = md_open,
        .release        = md_release,
        .ioctl          = md_ioctl,
+       .getgeo         = md_getgeo,
        .media_changed  = md_media_changed,
        .revalidate_disk= md_revalidate,
 };
@@ -3544,7 +4158,7 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
        set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
-       md_new_event(mddev);
+       md_new_event_inintr(mddev);
 }
 
 /* seq_file implementation /proc/mdstat */
@@ -3572,7 +4186,10 @@ static void status_unused(struct seq_file *seq)
 
 static void status_resync(struct seq_file *seq, mddev_t * mddev)
 {
-       unsigned long max_blocks, resync, res, dt, db, rt;
+       sector_t max_blocks, resync, res;
+       unsigned long dt, db, rt;
+       int scale;
+       unsigned int per_milli;
 
        resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2;
 
@@ -3588,9 +4205,22 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev)
                MD_BUG();
                return;
        }
-       res = (resync/1024)*1000/(max_blocks/1024 + 1);
+       /* Pick 'scale' such that (resync>>scale)*1000 will fit
+        * in a sector_t, and (max_blocks>>scale) will fit in a
+        * u32, as those are the requirements for sector_div.
+        * Thus 'scale' must be at least 10
+        */
+       scale = 10;
+       if (sizeof(sector_t) > sizeof(unsigned long)) {
+               while ( max_blocks/2 > (1ULL<<(scale+32)))
+                       scale++;
+       }
+       res = (resync>>scale)*1000;
+       sector_div(res, (u32)((max_blocks>>scale)+1));
+
+       per_milli = res;
        {
-               int i, x = res/50, y = 20-x;
+               int i, x = per_milli/50, y = 20-x;
                seq_printf(seq, "[");
                for (i = 0; i < x; i++)
                        seq_printf(seq, "=");
@@ -3599,10 +4229,14 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev)
                        seq_printf(seq, ".");
                seq_printf(seq, "] ");
        }
-       seq_printf(seq, " %s =%3lu.%lu%% (%lu/%lu)",
+       seq_printf(seq, " %s =%3u.%u%% (%llu/%llu)",
+                  (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)?
+                   "reshape" :
                      (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ?
-                      "resync" : "recovery"),
-                     res/10, res % 10, resync, max_blocks);
+                      "resync" : "recovery")),
+                     per_milli/10, per_milli % 10,
+                  (unsigned long long) resync,
+                  (unsigned long long) max_blocks);
 
        /*
         * We do not want to overflow, so the order of operands and
@@ -3616,7 +4250,7 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev)
        dt = ((jiffies - mddev->resync_mark) / HZ);
        if (!dt) dt++;
        db = resync - (mddev->resync_mark_cnt/2);
-       rt = (dt * ((max_blocks-resync) / (db/100+1)))/100;
+       rt = (dt * ((unsigned long)(max_blocks-resync) / (db/100+1)))/100;
 
        seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6);
 
@@ -3715,8 +4349,9 @@ static int md_seq_show(struct seq_file *seq, void *v)
                return 0;
        }
 
-       if (mddev_lock(mddev)!=0) 
+       if (mddev_lock(mddev) < 0)
                return -EINTR;
+
        if (mddev->pers || mddev->raid_disks || !list_empty(&mddev->disks)) {
                seq_printf(seq, "%s : %sactive", mdname(mddev),
                                                mddev->pers ? "" : "in");
@@ -3973,7 +4608,7 @@ static DECLARE_WAIT_QUEUE_HEAD(resync_wait);
 
 #define SYNC_MARKS     10
 #define        SYNC_MARK_STEP  (3*HZ)
-static void md_do_sync(mddev_t *mddev)
+void md_do_sync(mddev_t *mddev)
 {
        mddev_t *mddev2;
        unsigned int currspeed = 0,
@@ -4053,16 +4688,18 @@ static void md_do_sync(mddev_t *mddev)
                 */
                max_sectors = mddev->resync_max_sectors;
                mddev->resync_mismatches = 0;
-       } else
+       } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
+               max_sectors = mddev->size << 1;
+       else
                /* recovery follows the physical size of devices */
                max_sectors = mddev->size << 1;
 
        printk(KERN_INFO "md: syncing RAID array %s\n", mdname(mddev));
        printk(KERN_INFO "md: minimum _guaranteed_ reconstruction speed:"
-               " %d KB/sec/disc.\n", sysctl_speed_limit_min);
+               " %d KB/sec/disc.\n", speed_min(mddev));
        printk(KERN_INFO "md: using maximum available idle IO bandwidth "
               "(but not more than %d KB/sec) for reconstruction.\n",
-              sysctl_speed_limit_max);
+              speed_max(mddev));
 
        is_mddev_idle(mddev); /* this also initializes IO event counters */
        /* we don't use the checkpoint if there's a bitmap */
@@ -4103,7 +4740,7 @@ static void md_do_sync(mddev_t *mddev)
 
                skipped = 0;
                sectors = mddev->pers->sync_request(mddev, j, &skipped,
-                                           currspeed < sysctl_speed_limit_min);
+                                           currspeed < speed_min(mddev));
                if (sectors == 0) {
                        set_bit(MD_RECOVERY_ERR, &mddev->recovery);
                        goto out;
@@ -4168,8 +4805,8 @@ static void md_do_sync(mddev_t *mddev)
                currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2
                        /((jiffies-mddev->resync_mark)/HZ +1) +1;
 
-               if (currspeed > sysctl_speed_limit_min) {
-                       if ((currspeed > sysctl_speed_limit_max) ||
+               if (currspeed > speed_min(mddev)) {
+                       if ((currspeed > speed_max(mddev)) ||
                                        !is_mddev_idle(mddev)) {
                                msleep(500);
                                goto repeat;
@@ -4189,6 +4826,8 @@ static void md_do_sync(mddev_t *mddev)
        mddev->pers->sync_request(mddev, max_sectors, &skipped, 1);
 
        if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) &&
+           test_bit(MD_RECOVERY_SYNC, &mddev->recovery) &&
+           !test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&
            mddev->curr_resync > 2 &&
            mddev->curr_resync >= mddev->recovery_cp) {
                if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
@@ -4206,6 +4845,7 @@ static void md_do_sync(mddev_t *mddev)
        set_bit(MD_RECOVERY_DONE, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
 }
+EXPORT_SYMBOL_GPL(md_do_sync);
 
 
 /*
@@ -4261,7 +4901,7 @@ void md_check_recovery(mddev_t *mddev)
                ))
                return;
 
-       if (mddev_trylock(mddev)==0) {
+       if (mddev_trylock(mddev)) {
                int spares =0;
 
                spin_lock_irq(&mddev->write_lock);
@@ -4397,8 +5037,10 @@ static int md_notify_reboot(struct notifier_block *this,
                printk(KERN_INFO "md: stopping all md devices.\n");
 
                ITERATE_MDDEV(mddev,tmp)
-                       if (mddev_trylock(mddev)==0)
+                       if (mddev_trylock(mddev)) {
                                do_md_stop (mddev, 1);
+                               mddev_unlock(mddev);
+                       }
                /*
                 * certain more exotic SCSI devices are known to be
                 * volatile wrt too early system reboots. While the
@@ -4556,7 +5198,7 @@ static int set_ro(const char *val, struct kernel_param *kp)
        int num = simple_strtoul(val, &e, 10);
        if (*val && (*e == '\0' || *e == '\n')) {
                start_readonly = num;
-               return 0;;
+               return 0;
        }
        return -EINVAL;
 }