md: allow v0.91 metadata to record devices as being active but not in-sync.
[safe/jmp/linux-2.6] / drivers / md / md.c
index f173ace..b182f86 100644 (file)
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include <linux/module.h>
-#include <linux/kernel.h>
 #include <linux/kthread.h>
-#include <linux/linkage.h>
-#include <linux/raid/md.h>
-#include <linux/raid/bitmap.h>
+#include <linux/blkdev.h>
 #include <linux/sysctl.h>
+#include <linux/seq_file.h>
 #include <linux/buffer_head.h> /* for invalidate_bdev */
 #include <linux/poll.h>
-#include <linux/mutex.h>
 #include <linux/ctype.h>
-#include <linux/freezer.h>
-
-#include <linux/init.h>
-
+#include <linux/hdreg.h>
+#include <linux/proc_fs.h>
+#include <linux/random.h>
+#include <linux/reboot.h>
 #include <linux/file.h>
-
-#ifdef CONFIG_KMOD
-#include <linux/kmod.h>
-#endif
-
-#include <asm/unaligned.h>
-
-#define MAJOR_NR MD_MAJOR
-#define MD_DRIVER
-
-/* 63 partitions with the alternate major number (mdp) */
-#define MdpMinorShift 6
+#include <linux/delay.h>
+#include <linux/raid/md_p.h>
+#include <linux/raid/md_u.h>
+#include "md.h"
+#include "bitmap.h"
 
 #define DEBUG 0
 #define dprintk(x...) ((void)(DEBUG && printk(x)))
 
 
 #ifndef MODULE
-static void autostart_arrays (int part);
+static void autostart_arrays(int part);
 #endif
 
 static LIST_HEAD(pers_list);
@@ -74,6 +63,8 @@ static DEFINE_SPINLOCK(pers_lock);
 
 static void md_print_devices(void);
 
+static DECLARE_WAIT_QUEUE_HEAD(resync_wait);
+
 #define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); }
 
 /*
@@ -147,7 +138,7 @@ static ctl_table raid_root_table[] = {
        { .ctl_name = 0 }
 };
 
-static struct block_device_operations md_fops;
+static const struct block_device_operations md_fops;
 
 static int start_readonly;
 
@@ -167,7 +158,6 @@ 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);
 
@@ -195,7 +185,7 @@ static DEFINE_SPINLOCK(all_mddevs_lock);
  * Any code which breaks out of this loop while own
  * a reference to the current mddev and must mddev_put it.
  */
-#define ITERATE_MDDEV(mddev,tmp)                                       \
+#define for_each_mddev(mddev,tmp)                                      \
                                                                        \
        for (({ spin_lock(&all_mddevs_lock);                            \
                tmp = all_mddevs.next;                                  \
@@ -211,11 +201,73 @@ static DEFINE_SPINLOCK(all_mddevs_lock);
                )
 
 
-static int md_fail_request (struct request_queue *q, struct bio *bio)
+/* Rather than calling directly into the personality make_request function,
+ * IO requests come here first so that we can check if the device is
+ * being suspended pending a reconfiguration.
+ * We hold a refcount over the call to ->make_request.  By the time that
+ * call has finished, the bio has been linked into some internal structure
+ * and so is visible to ->quiesce(), so we don't need the refcount any more.
+ */
+static int md_make_request(struct request_queue *q, struct bio *bio)
 {
-       bio_io_error(bio);
-       return 0;
+       mddev_t *mddev = q->queuedata;
+       int rv;
+       if (mddev == NULL || mddev->pers == NULL) {
+               bio_io_error(bio);
+               return 0;
+       }
+       rcu_read_lock();
+       if (mddev->suspended) {
+               DEFINE_WAIT(__wait);
+               for (;;) {
+                       prepare_to_wait(&mddev->sb_wait, &__wait,
+                                       TASK_UNINTERRUPTIBLE);
+                       if (!mddev->suspended)
+                               break;
+                       rcu_read_unlock();
+                       schedule();
+                       rcu_read_lock();
+               }
+               finish_wait(&mddev->sb_wait, &__wait);
+       }
+       atomic_inc(&mddev->active_io);
+       rcu_read_unlock();
+       rv = mddev->pers->make_request(q, bio);
+       if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended)
+               wake_up(&mddev->sb_wait);
+
+       return rv;
+}
+
+static void mddev_suspend(mddev_t *mddev)
+{
+       BUG_ON(mddev->suspended);
+       mddev->suspended = 1;
+       synchronize_rcu();
+       wait_event(mddev->sb_wait, atomic_read(&mddev->active_io) == 0);
+       mddev->pers->quiesce(mddev, 1);
+       md_unregister_thread(mddev->thread);
+       mddev->thread = NULL;
+       /* we now know that no code is executing in the personality module,
+        * except possibly the tail end of a ->bi_end_io function, but that
+        * is certain to complete before the module has a chance to get
+        * unloaded
+        */
+}
+
+static void mddev_resume(mddev_t *mddev)
+{
+       mddev->suspended = 0;
+       wake_up(&mddev->sb_wait);
+       mddev->pers->quiesce(mddev, 0);
+}
+
+int mddev_congested(mddev_t *mddev, int bits)
+{
+       return mddev->suspended;
 }
+EXPORT_SYMBOL(mddev_congested);
+
 
 static inline mddev_t *mddev_get(mddev_t *mddev)
 {
@@ -223,17 +275,28 @@ static inline mddev_t *mddev_get(mddev_t *mddev)
        return mddev;
 }
 
+static void mddev_delayed_delete(struct work_struct *ws);
+
 static void mddev_put(mddev_t *mddev)
 {
        if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock))
                return;
-       if (!mddev->raid_disks && list_empty(&mddev->disks)) {
+       if (!mddev->raid_disks && list_empty(&mddev->disks) &&
+           !mddev->hold_active) {
                list_del(&mddev->all_mddevs);
-               spin_unlock(&all_mddevs_lock);
-               blk_cleanup_queue(mddev->queue);
-               kobject_unregister(&mddev->kobj);
-       } else
-               spin_unlock(&all_mddevs_lock);
+               if (mddev->gendisk) {
+                       /* we did a probe so need to clean up.
+                        * Call schedule_work inside the spinlock
+                        * so that flush_scheduled_work() after
+                        * mddev_find will succeed in waiting for the
+                        * work to be done.
+                        */
+                       INIT_WORK(&mddev->del_work, mddev_delayed_delete);
+                       schedule_work(&mddev->del_work);
+               } else
+                       kfree(mddev);
+       }
+       spin_unlock(&all_mddevs_lock);
 }
 
 static mddev_t * mddev_find(dev_t unit)
@@ -242,15 +305,50 @@ static mddev_t * mddev_find(dev_t unit)
 
  retry:
        spin_lock(&all_mddevs_lock);
-       list_for_each_entry(mddev, &all_mddevs, all_mddevs)
-               if (mddev->unit == unit) {
-                       mddev_get(mddev);
+
+       if (unit) {
+               list_for_each_entry(mddev, &all_mddevs, all_mddevs)
+                       if (mddev->unit == unit) {
+                               mddev_get(mddev);
+                               spin_unlock(&all_mddevs_lock);
+                               kfree(new);
+                               return mddev;
+                       }
+
+               if (new) {
+                       list_add(&new->all_mddevs, &all_mddevs);
                        spin_unlock(&all_mddevs_lock);
-                       kfree(new);
-                       return mddev;
+                       new->hold_active = UNTIL_IOCTL;
+                       return new;
                }
-
-       if (new) {
+       } else if (new) {
+               /* find an unused unit number */
+               static int next_minor = 512;
+               int start = next_minor;
+               int is_free = 0;
+               int dev = 0;
+               while (!is_free) {
+                       dev = MKDEV(MD_MAJOR, next_minor);
+                       next_minor++;
+                       if (next_minor > MINORMASK)
+                               next_minor = 0;
+                       if (next_minor == start) {
+                               /* Oh dear, all in use. */
+                               spin_unlock(&all_mddevs_lock);
+                               kfree(new);
+                               return NULL;
+                       }
+                               
+                       is_free = 1;
+                       list_for_each_entry(mddev, &all_mddevs, all_mddevs)
+                               if (mddev->unit == dev) {
+                                       is_free = 0;
+                                       break;
+                               }
+               }
+               new->unit = dev;
+               new->md_minor = MINOR(dev);
+               new->hold_active = UNTIL_STOP;
                list_add(&new->all_mddevs, &all_mddevs);
                spin_unlock(&all_mddevs_lock);
                return new;
@@ -267,23 +365,21 @@ static mddev_t * mddev_find(dev_t unit)
        else
                new->md_minor = MINOR(unit) >> MdpMinorShift;
 
+       mutex_init(&new->open_mutex);
        mutex_init(&new->reconfig_mutex);
        INIT_LIST_HEAD(&new->disks);
        INIT_LIST_HEAD(&new->all_mddevs);
        init_timer(&new->safemode_timer);
        atomic_set(&new->active, 1);
+       atomic_set(&new->openers, 0);
+       atomic_set(&new->active_io, 0);
        spin_lock_init(&new->write_lock);
        init_waitqueue_head(&new->sb_wait);
+       init_waitqueue_head(&new->recovery_wait);
        new->reshape_position = MaxSector;
-
-       new->queue = blk_alloc_queue(GFP_KERNEL);
-       if (!new->queue) {
-               kfree(new);
-               return NULL;
-       }
-       set_bit(QUEUE_FLAG_CLUSTER, &new->queue->queue_flags);
-
-       blk_queue_make_request(new->queue, md_fail_request);
+       new->resync_min = 0;
+       new->resync_max = MaxSector;
+       new->level = LEVEL_NONE;
 
        goto retry;
 }
@@ -293,6 +389,11 @@ static inline int mddev_lock(mddev_t * mddev)
        return mutex_lock_interruptible(&mddev->reconfig_mutex);
 }
 
+static inline int mddev_is_locked(mddev_t *mddev)
+{
+       return mutex_is_locked(&mddev->reconfig_mutex);
+}
+
 static inline int mddev_trylock(mddev_t * mddev)
 {
        return mutex_trylock(&mddev->reconfig_mutex);
@@ -307,25 +408,23 @@ static inline void mddev_unlock(mddev_t * mddev)
 
 static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr)
 {
-       mdk_rdev_t * rdev;
-       struct list_head *tmp;
+       mdk_rdev_t *rdev;
 
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       list_for_each_entry(rdev, &mddev->disks, same_set)
                if (rdev->desc_nr == nr)
                        return rdev;
-       }
+
        return NULL;
 }
 
 static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev)
 {
-       struct list_head *tmp;
        mdk_rdev_t *rdev;
 
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       list_for_each_entry(rdev, &mddev->disks, same_set)
                if (rdev->bdev->bd_dev == dev)
                        return rdev;
-       }
+
        return NULL;
 }
 
@@ -341,21 +440,11 @@ static struct mdk_personality *find_pers(int level, char *clevel)
        return NULL;
 }
 
+/* return the offset of the super block in 512byte sectors */
 static inline sector_t calc_dev_sboffset(struct block_device *bdev)
 {
-       sector_t size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
-       return MD_NEW_SIZE_BLOCKS(size);
-}
-
-static sector_t calc_dev_size(mdk_rdev_t *rdev, unsigned chunk_size)
-{
-       sector_t size;
-
-       size = rdev->sb_offset;
-
-       if (chunk_size)
-               size &= ~((sector_t)chunk_size/1024 - 1);
-       return size;
+       sector_t num_sectors = bdev->bd_inode->i_size / 512;
+       return MD_NEW_SIZE_SECTORS(num_sectors);
 }
 
 static int alloc_disk_sb(mdk_rdev_t * rdev)
@@ -366,7 +455,7 @@ static int alloc_disk_sb(mdk_rdev_t * rdev)
        rdev->sb_page = alloc_page(GFP_KERNEL);
        if (!rdev->sb_page) {
                printk(KERN_ALERT "md: out of memory.\n");
-               return -EINVAL;
+               return -ENOMEM;
        }
 
        return 0;
@@ -378,8 +467,8 @@ static void free_disk_sb(mdk_rdev_t * rdev)
                put_page(rdev->sb_page);
                rdev->sb_loaded = 0;
                rdev->sb_page = NULL;
-               rdev->sb_offset = 0;
-               rdev->size = 0;
+               rdev->sb_start = 0;
+               rdev->sectors = 0;
        }
 }
 
@@ -439,7 +528,7 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
         * causes ENOTSUPP, we allocate a spare bio...
         */
        struct bio *bio = bio_alloc(GFP_NOIO, 1);
-       int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNC);
+       int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG);
 
        bio->bi_bdev = rdev->bdev;
        bio->bi_sector = sector;
@@ -496,7 +585,7 @@ int sync_page_io(struct block_device *bdev, sector_t sector, int size,
        struct completion event;
        int ret;
 
-       rw |= (1 << BIO_RW_SYNC);
+       rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
 
        bio->bi_bdev = bdev;
        bio->bi_sector = sector;
@@ -524,7 +613,7 @@ static int read_disk_sb(mdk_rdev_t * rdev, int size)
                return 0;
 
 
-       if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, size, rdev->sb_page, READ))
+       if (!sync_page_io(rdev->bdev, rdev->sb_start, size, rdev->sb_page, READ))
                goto fail;
        rdev->sb_loaded = 1;
        return 0;
@@ -537,17 +626,12 @@ fail:
 
 static int uuid_equal(mdp_super_t *sb1, mdp_super_t *sb2)
 {
-       if (    (sb1->set_uuid0 == sb2->set_uuid0) &&
-               (sb1->set_uuid1 == sb2->set_uuid1) &&
-               (sb1->set_uuid2 == sb2->set_uuid2) &&
-               (sb1->set_uuid3 == sb2->set_uuid3))
-
-               return 1;
-
-       return 0;
+       return  sb1->set_uuid0 == sb2->set_uuid0 &&
+               sb1->set_uuid1 == sb2->set_uuid1 &&
+               sb1->set_uuid2 == sb2->set_uuid2 &&
+               sb1->set_uuid3 == sb2->set_uuid3;
 }
 
-
 static int sb_equal(mdp_super_t *sb1, mdp_super_t *sb2)
 {
        int ret;
@@ -558,7 +642,7 @@ static int sb_equal(mdp_super_t *sb1, mdp_super_t *sb2)
 
        if (!tmp1 || !tmp2) {
                ret = 0;
-               printk(KERN_INFO "md.c: sb1 is not equal to sb2!\n");
+               printk(KERN_INFO "md.c sb_equal(): failed to allocate memory!\n");
                goto abort;
        }
 
@@ -571,11 +655,7 @@ static int sb_equal(mdp_super_t *sb1, mdp_super_t *sb2)
        tmp1->nr_disks = 0;
        tmp2->nr_disks = 0;
 
-       if (memcmp(tmp1, tmp2, MD_SB_GENERIC_CONSTANT_WORDS * 4))
-               ret = 0;
-       else
-               ret = 1;
-
+       ret = (memcmp(tmp1, tmp2, MD_SB_GENERIC_CONSTANT_WORDS * 4) == 0);
 abort:
        kfree(tmp1);
        kfree(tmp2);
@@ -652,14 +732,35 @@ static unsigned int calc_sb_csum(mdp_super_t * sb)
  */
 
 struct super_type  {
-       char            *name;
-       struct module   *owner;
-       int             (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version);
-       int             (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev);
-       void            (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev);
+       char                *name;
+       struct module       *owner;
+       int                 (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev,
+                                         int minor_version);
+       int                 (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev);
+       void                (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev);
+       unsigned long long  (*rdev_size_change)(mdk_rdev_t *rdev,
+                                               sector_t num_sectors);
 };
 
 /*
+ * Check that the given mddev has no bitmap.
+ *
+ * This function is called from the run method of all personalities that do not
+ * support bitmaps. It prints an error message and returns non-zero if mddev
+ * has a bitmap. Otherwise, it returns 0.
+ *
+ */
+int md_check_no_bitmap(mddev_t *mddev)
+{
+       if (!mddev->bitmap_file && !mddev->bitmap_offset)
+               return 0;
+       printk(KERN_ERR "%s: bitmaps are not supported for %s\n",
+               mdname(mddev), mddev->pers->name);
+       return 1;
+}
+EXPORT_SYMBOL(md_check_no_bitmap);
+
+/*
  * load_super for 0.90.0 
  */
 static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
@@ -667,16 +768,14 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
        char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
        mdp_super_t *sb;
        int ret;
-       sector_t sb_offset;
 
        /*
-        * Calculate the position of the superblock,
+        * Calculate the position of the superblock (512byte sectors),
         * it's at the end of the disk.
         *
         * It also happens to be a multiple of 4Kb.
         */
-       sb_offset = calc_dev_sboffset(rdev->bdev);
-       rdev->sb_offset = sb_offset;
+       rdev->sb_start = calc_dev_sboffset(rdev->bdev);
 
        ret = read_disk_sb(rdev, MD_SB_BYTES);
        if (ret) return ret;
@@ -714,25 +813,14 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
        rdev->data_offset = 0;
        rdev->sb_size = MD_SB_BYTES;
 
-       if (sb->state & (1<<MD_SB_BITMAP_PRESENT)) {
-               if (sb->level != 1 && sb->level != 4
-                   && sb->level != 5 && sb->level != 6
-                   && sb->level != 10) {
-                       /* FIXME use a better test */
-                       printk(KERN_WARNING
-                              "md: bitmaps not supported for this level.\n");
-                       goto abort;
-               }
-       }
-
        if (sb->level == LEVEL_MULTIPATH)
                rdev->desc_nr = -1;
        else
                rdev->desc_nr = sb->this_disk.number;
 
-       if (refdev == 0)
+       if (!refdev) {
                ret = 1;
-       else {
+       else {
                __u64 ev1, ev2;
                mdp_super_t *refsb = (mdp_super_t*)page_address(refdev->sb_page);
                if (!uuid_equal(refsb, sb)) {
@@ -753,9 +841,9 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
                else 
                        ret = 0;
        }
-       rdev->size = calc_dev_size(rdev, sb->chunk_size);
+       rdev->sectors = rdev->sb_start;
 
-       if (rdev->size < sb->size && sb->level > 1)
+       if (rdev->sectors < sb->size * 2 && sb->level > 1)
                /* "this cannot possibly happen" ... */
                ret = -EINVAL;
 
@@ -773,20 +861,24 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
        __u64 ev1 = md_event(sb);
 
        rdev->raid_disk = -1;
-       rdev->flags = 0;
+       clear_bit(Faulty, &rdev->flags);
+       clear_bit(In_sync, &rdev->flags);
+       clear_bit(WriteMostly, &rdev->flags);
+       clear_bit(BarriersNotsupp, &rdev->flags);
+
        if (mddev->raid_disks == 0) {
                mddev->major_version = 0;
                mddev->minor_version = sb->minor_version;
                mddev->patch_version = sb->patch_version;
-               mddev->persistent = ! sb->not_persistent;
-               mddev->chunk_size = sb->chunk_size;
+               mddev->external = 0;
+               mddev->chunk_sectors = sb->chunk_size >> 9;
                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;
+               mddev->dev_sectors = sb->size * 2;
                mddev->events = ev1;
                mddev->bitmap_offset = 0;
                mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
@@ -796,13 +888,13 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                        mddev->delta_disks = sb->delta_disks;
                        mddev->new_level = sb->new_level;
                        mddev->new_layout = sb->new_layout;
-                       mddev->new_chunk = sb->new_chunk;
+                       mddev->new_chunk_sectors = 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;
+                       mddev->new_chunk_sectors = mddev->chunk_sectors;
                }
 
                if (sb->state & (1<<MD_SB_CLEAN))
@@ -852,6 +944,14 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                            desc->raid_disk < mddev->raid_disks */) {
                        set_bit(In_sync, &rdev->flags);
                        rdev->raid_disk = desc->raid_disk;
+               } else if (desc->state & (1<<MD_DISK_ACTIVE)) {
+                       /* active but not in sync implies recovery up to
+                        * reshape position.  We don't know exactly where
+                        * that is, so set to zero for now */
+                       if (mddev->minor_version >= 91) {
+                               rdev->recovery_offset = 0;
+                               rdev->raid_disk = desc->raid_disk;
+                       }
                }
                if (desc->state & (1<<MD_DISK_WRITEMOSTLY))
                        set_bit(WriteMostly, &rdev->flags);
@@ -866,7 +966,6 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
 static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
 {
        mdp_super_t *sb;
-       struct list_head *tmp;
        mdk_rdev_t *rdev2;
        int next_spare = mddev->raid_disks;
 
@@ -901,10 +1000,10 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
 
        sb->ctime = mddev->ctime;
        sb->level = mddev->level;
-       sb->size  = mddev->size;
+       sb->size = mddev->dev_sectors / 2;
        sb->raid_disks = mddev->raid_disks;
        sb->md_minor = mddev->md_minor;
-       sb->not_persistent = !mddev->persistent;
+       sb->not_persistent = 0;
        sb->utime = mddev->utime;
        sb->state = 0;
        sb->events_hi = (mddev->events>>32);
@@ -918,7 +1017,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
                sb->new_level = mddev->new_level;
                sb->delta_disks = mddev->delta_disks;
                sb->new_layout = mddev->new_layout;
-               sb->new_chunk = mddev->new_chunk;
+               sb->new_chunk = mddev->new_chunk_sectors << 9;
        }
        mddev->minor_version = sb->minor_version;
        if (mddev->in_sync)
@@ -932,17 +1031,28 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
                sb->recovery_cp = 0;
 
        sb->layout = mddev->layout;
-       sb->chunk_size = mddev->chunk_size;
+       sb->chunk_size = mddev->chunk_sectors << 9;
 
        if (mddev->bitmap && mddev->bitmap_file == NULL)
                sb->state |= (1<<MD_SB_BITMAP_PRESENT);
 
        sb->disks[0].state = (1<<MD_DISK_REMOVED);
-       ITERATE_RDEV(mddev,rdev2,tmp) {
+       list_for_each_entry(rdev2, &mddev->disks, same_set) {
                mdp_disk_t *d;
                int desc_nr;
-               if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags)
-                   && !test_bit(Faulty, &rdev2->flags))
+               int is_active = test_bit(In_sync, &rdev2->flags);
+
+               if (rdev2->raid_disk >= 0 &&
+                   sb->minor_version >= 91)
+                       /* we have nowhere to store the recovery_offset,
+                        * but if it is not below the reshape_position,
+                        * we can piggy-back on that.
+                        */
+                       is_active = 1;
+               if (rdev2->raid_disk < 0 ||
+                   test_bit(Faulty, &rdev2->flags))
+                       is_active = 0;
+               if (is_active)
                        desc_nr = rdev2->raid_disk;
                else
                        desc_nr = next_spare++;
@@ -952,16 +1062,16 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
                d->number = rdev2->desc_nr;
                d->major = MAJOR(rdev2->bdev->bd_dev);
                d->minor = MINOR(rdev2->bdev->bd_dev);
-               if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags)
-                   && !test_bit(Faulty, &rdev2->flags))
+               if (is_active)
                        d->raid_disk = rdev2->raid_disk;
                else
                        d->raid_disk = rdev2->desc_nr; /* compatibility */
                if (test_bit(Faulty, &rdev2->flags))
                        d->state = (1<<MD_DISK_FAULTY);
-               else if (test_bit(In_sync, &rdev2->flags)) {
+               else if (is_active) {
                        d->state = (1<<MD_DISK_ACTIVE);
-                       d->state |= (1<<MD_DISK_SYNC);
+                       if (test_bit(In_sync, &rdev2->flags))
+                               d->state |= (1<<MD_DISK_SYNC);
                        active++;
                        working++;
                } else {
@@ -994,6 +1104,26 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev)
 }
 
 /*
+ * rdev_size_change for 0.90.0
+ */
+static unsigned long long
+super_90_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
+{
+       if (num_sectors && num_sectors < rdev->mddev->dev_sectors)
+               return 0; /* component must fit device */
+       if (rdev->mddev->bitmap_offset)
+               return 0; /* can't move bitmap */
+       rdev->sb_start = calc_dev_sboffset(rdev->bdev);
+       if (!num_sectors || num_sectors > rdev->sb_start)
+               num_sectors = rdev->sb_start;
+       md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
+                      rdev->sb_page);
+       md_super_wait(rdev->mddev);
+       return num_sectors / 2; /* kB for sysfs */
+}
+
+
+/*
  * version 1 superblock
  */
 
@@ -1024,12 +1154,12 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
 {
        struct mdp_superblock_1 *sb;
        int ret;
-       sector_t sb_offset;
+       sector_t sb_start;
        char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
        int bmask;
 
        /*
-        * Calculate the position of the superblock.
+        * Calculate the position of the superblock in 512byte sectors.
         * It is always aligned to a 4K boundary and
         * depeding on minor_version, it can be:
         * 0: At least 8K, but less than 12K, from end of device
@@ -1038,22 +1168,20 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
         */
        switch(minor_version) {
        case 0:
-               sb_offset = rdev->bdev->bd_inode->i_size >> 9;
-               sb_offset -= 8*2;
-               sb_offset &= ~(sector_t)(4*2-1);
-               /* convert from sectors to K */
-               sb_offset /= 2;
+               sb_start = rdev->bdev->bd_inode->i_size >> 9;
+               sb_start -= 8*2;
+               sb_start &= ~(sector_t)(4*2-1);
                break;
        case 1:
-               sb_offset = 0;
+               sb_start = 0;
                break;
        case 2:
-               sb_offset = 4;
+               sb_start = 8;
                break;
        default:
                return -EINVAL;
        }
-       rdev->sb_offset = sb_offset;
+       rdev->sb_start = sb_start;
 
        /* superblock is rarely larger than 1K, but it can be larger,
         * and it is safe to read 4k, so we do that
@@ -1067,7 +1195,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
        if (sb->magic != cpu_to_le32(MD_SB_MAGIC) ||
            sb->major_version != cpu_to_le32(1) ||
            le32_to_cpu(sb->max_dev) > (4096-256)/2 ||
-           le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) ||
+           le64_to_cpu(sb->super_offset) != rdev->sb_start ||
            (le32_to_cpu(sb->feature_map) & ~MD_FEATURE_ALL) != 0)
                return -EINVAL;
 
@@ -1081,35 +1209,28 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
                       bdevname(rdev->bdev,b));
                return -EINVAL;
        }
-       if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET)) {
-               if (sb->level != cpu_to_le32(1) &&
-                   sb->level != cpu_to_le32(4) &&
-                   sb->level != cpu_to_le32(5) &&
-                   sb->level != cpu_to_le32(6) &&
-                   sb->level != cpu_to_le32(10)) {
-                       printk(KERN_WARNING
-                              "md: bitmaps not supported for this level.\n");
-                       return -EINVAL;
-               }
-       }
 
        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;
+       bmask = queue_logical_block_size(rdev->bdev->bd_disk->queue)-1;
        if (rdev->sb_size & bmask)
-               rdev-> sb_size = (rdev->sb_size | bmask)+1;
+               rdev->sb_size = (rdev->sb_size | bmask) + 1;
+
+       if (minor_version
+           && rdev->data_offset < sb_start + (rdev->sb_size/512))
+               return -EINVAL;
 
        if (sb->level == cpu_to_le32(LEVEL_MULTIPATH))
                rdev->desc_nr = -1;
        else
                rdev->desc_nr = le32_to_cpu(sb->dev_number);
 
-       if (refdev == 0)
+       if (!refdev) {
                ret = 1;
-       else {
+       else {
                __u64 ev1, ev2;
                struct mdp_superblock_1 *refsb = 
                        (struct mdp_superblock_1*)page_address(refdev->sb_page);
@@ -1132,17 +1253,15 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
                else
                        ret = 0;
        }
-       if (minor_version) 
-               rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2;
+       if (minor_version)
+               rdev->sectors = (rdev->bdev->bd_inode->i_size >> 9) -
+                       le64_to_cpu(sb->data_offset);
        else
-               rdev->size = rdev->sb_offset;
-       if (rdev->size < le64_to_cpu(sb->data_size)/2)
+               rdev->sectors = rdev->sb_start;
+       if (rdev->sectors < le64_to_cpu(sb->data_size))
                return -EINVAL;
-       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);
-
-       if (le64_to_cpu(sb->size) > rdev->size*2)
+       rdev->sectors = le64_to_cpu(sb->data_size);
+       if (le64_to_cpu(sb->size) > rdev->sectors)
                return -EINVAL;
        return ret;
 }
@@ -1153,19 +1272,23 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
        __u64 ev1 = le64_to_cpu(sb->events);
 
        rdev->raid_disk = -1;
-       rdev->flags = 0;
+       clear_bit(Faulty, &rdev->flags);
+       clear_bit(In_sync, &rdev->flags);
+       clear_bit(WriteMostly, &rdev->flags);
+       clear_bit(BarriersNotsupp, &rdev->flags);
+
        if (mddev->raid_disks == 0) {
                mddev->major_version = 1;
                mddev->patch_version = 0;
-               mddev->persistent = 1;
-               mddev->chunk_size = le32_to_cpu(sb->chunksize) << 9;
+               mddev->external = 0;
+               mddev->chunk_sectors = le32_to_cpu(sb->chunksize);
                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->dev_sectors = le64_to_cpu(sb->size);
                mddev->events = ev1;
                mddev->bitmap_offset = 0;
                mddev->default_bitmap_offset = 1024 >> 9;
@@ -1184,13 +1307,13 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                        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;
+                       mddev->new_chunk_sectors = le32_to_cpu(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;
+                       mddev->new_chunk_sectors = mddev->chunk_sectors;
                }
 
        } else if (mddev->pers == NULL) {
@@ -1211,7 +1334,12 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
        }
        if (mddev->level != LEVEL_MULTIPATH) {
                int role;
-               role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]);
+               if (rdev->desc_nr < 0 ||
+                   rdev->desc_nr >= le32_to_cpu(sb->max_dev)) {
+                       role = 0xffff;
+                       rdev->desc_nr = -1;
+               } else
+                       role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]);
                switch(role) {
                case 0xffff: /* spare */
                        break;
@@ -1238,7 +1366,6 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
 static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
 {
        struct mdp_superblock_1 *sb;
-       struct list_head *tmp;
        mdk_rdev_t *rdev2;
        int max_dev, i;
        /* make rdev->sb match mddev and rdev data. */
@@ -1262,7 +1389,10 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
        sb->cnt_corrected_read = cpu_to_le32(atomic_read(&rdev->corrected_errors));
 
        sb->raid_disks = cpu_to_le32(mddev->raid_disks);
-       sb->size = cpu_to_le64(mddev->size<<1);
+       sb->size = cpu_to_le64(mddev->dev_sectors);
+       sb->chunksize = cpu_to_le32(mddev->chunk_sectors);
+       sb->level = cpu_to_le32(mddev->level);
+       sb->layout = cpu_to_le32(mddev->layout);
 
        if (mddev->bitmap && mddev->bitmap_file == NULL) {
                sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset);
@@ -1270,10 +1400,13 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
        }
 
        if (rdev->raid_disk >= 0 &&
-           !test_bit(In_sync, &rdev->flags) &&
-           rdev->recovery_offset > 0) {
-               sb->feature_map |= cpu_to_le32(MD_FEATURE_RECOVERY_OFFSET);
-               sb->recovery_offset = cpu_to_le64(rdev->recovery_offset);
+           !test_bit(In_sync, &rdev->flags)) {
+               if (rdev->recovery_offset > 0) {
+                       sb->feature_map |=
+                               cpu_to_le32(MD_FEATURE_RECOVERY_OFFSET);
+                       sb->recovery_offset =
+                               cpu_to_le64(rdev->recovery_offset);
+               }
        }
 
        if (mddev->reshape_position != MaxSector) {
@@ -1282,20 +1415,26 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
                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);
+               sb->new_chunk = cpu_to_le32(mddev->new_chunk_sectors);
        }
 
        max_dev = 0;
-       ITERATE_RDEV(mddev,rdev2,tmp)
+       list_for_each_entry(rdev2, &mddev->disks, same_set)
                if (rdev2->desc_nr+1 > max_dev)
                        max_dev = rdev2->desc_nr+1;
 
-       if (max_dev > le32_to_cpu(sb->max_dev))
+       if (max_dev > le32_to_cpu(sb->max_dev)) {
+               int bmask;
                sb->max_dev = cpu_to_le32(max_dev);
+               rdev->sb_size = max_dev * 2 + 256;
+               bmask = queue_logical_block_size(rdev->bdev->bd_disk->queue)-1;
+               if (rdev->sb_size & bmask)
+                       rdev->sb_size = (rdev->sb_size | bmask) + 1;
+       }
        for (i=0; i<max_dev;i++)
                sb->dev_roles[i] = cpu_to_le16(0xfffe);
        
-       ITERATE_RDEV(mddev,rdev2,tmp) {
+       list_for_each_entry(rdev2, &mddev->disks, same_set) {
                i = rdev2->desc_nr;
                if (test_bit(Faulty, &rdev2->flags))
                        sb->dev_roles[i] = cpu_to_le16(0xfffe);
@@ -1310,40 +1449,150 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
        sb->sb_csum = calc_sb_1_csum(sb);
 }
 
+static unsigned long long
+super_1_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
+{
+       struct mdp_superblock_1 *sb;
+       sector_t max_sectors;
+       if (num_sectors && num_sectors < rdev->mddev->dev_sectors)
+               return 0; /* component must fit device */
+       if (rdev->sb_start < rdev->data_offset) {
+               /* minor versions 1 and 2; superblock before data */
+               max_sectors = rdev->bdev->bd_inode->i_size >> 9;
+               max_sectors -= rdev->data_offset;
+               if (!num_sectors || num_sectors > max_sectors)
+                       num_sectors = max_sectors;
+       } else if (rdev->mddev->bitmap_offset) {
+               /* minor version 0 with bitmap we can't move */
+               return 0;
+       } else {
+               /* minor version 0; superblock after data */
+               sector_t sb_start;
+               sb_start = (rdev->bdev->bd_inode->i_size >> 9) - 8*2;
+               sb_start &= ~(sector_t)(4*2 - 1);
+               max_sectors = rdev->sectors + sb_start - rdev->sb_start;
+               if (!num_sectors || num_sectors > max_sectors)
+                       num_sectors = max_sectors;
+               rdev->sb_start = sb_start;
+       }
+       sb = (struct mdp_superblock_1 *) page_address(rdev->sb_page);
+       sb->data_size = cpu_to_le64(num_sectors);
+       sb->super_offset = rdev->sb_start;
+       sb->sb_csum = calc_sb_1_csum(sb);
+       md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
+                      rdev->sb_page);
+       md_super_wait(rdev->mddev);
+       return num_sectors / 2; /* kB for sysfs */
+}
 
 static struct super_type super_types[] = {
        [0] = {
                .name   = "0.90.0",
                .owner  = THIS_MODULE,
-               .load_super     = super_90_load,
-               .validate_super = super_90_validate,
-               .sync_super     = super_90_sync,
+               .load_super         = super_90_load,
+               .validate_super     = super_90_validate,
+               .sync_super         = super_90_sync,
+               .rdev_size_change   = super_90_rdev_size_change,
        },
        [1] = {
                .name   = "md-1",
                .owner  = THIS_MODULE,
-               .load_super     = super_1_load,
-               .validate_super = super_1_validate,
-               .sync_super     = super_1_sync,
+               .load_super         = super_1_load,
+               .validate_super     = super_1_validate,
+               .sync_super         = super_1_sync,
+               .rdev_size_change   = super_1_rdev_size_change,
        },
 };
 
 static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2)
 {
-       struct list_head *tmp, *tmp2;
        mdk_rdev_t *rdev, *rdev2;
 
-       ITERATE_RDEV(mddev1,rdev,tmp)
-               ITERATE_RDEV(mddev2, rdev2, tmp2)
+       rcu_read_lock();
+       rdev_for_each_rcu(rdev, mddev1)
+               rdev_for_each_rcu(rdev2, mddev2)
                        if (rdev->bdev->bd_contains ==
-                           rdev2->bdev->bd_contains)
+                           rdev2->bdev->bd_contains) {
+                               rcu_read_unlock();
                                return 1;
-
+                       }
+       rcu_read_unlock();
        return 0;
 }
 
 static LIST_HEAD(pending_raid_disks);
 
+/*
+ * Try to register data integrity profile for an mddev
+ *
+ * This is called when an array is started and after a disk has been kicked
+ * from the array. It only succeeds if all working and active component devices
+ * are integrity capable with matching profiles.
+ */
+int md_integrity_register(mddev_t *mddev)
+{
+       mdk_rdev_t *rdev, *reference = NULL;
+
+       if (list_empty(&mddev->disks))
+               return 0; /* nothing to do */
+       if (blk_get_integrity(mddev->gendisk))
+               return 0; /* already registered */
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
+               /* skip spares and non-functional disks */
+               if (test_bit(Faulty, &rdev->flags))
+                       continue;
+               if (rdev->raid_disk < 0)
+                       continue;
+               /*
+                * If at least one rdev is not integrity capable, we can not
+                * enable data integrity for the md device.
+                */
+               if (!bdev_get_integrity(rdev->bdev))
+                       return -EINVAL;
+               if (!reference) {
+                       /* Use the first rdev as the reference */
+                       reference = rdev;
+                       continue;
+               }
+               /* does this rdev's profile match the reference profile? */
+               if (blk_integrity_compare(reference->bdev->bd_disk,
+                               rdev->bdev->bd_disk) < 0)
+                       return -EINVAL;
+       }
+       /*
+        * All component devices are integrity capable and have matching
+        * profiles, register the common profile for the md device.
+        */
+       if (blk_integrity_register(mddev->gendisk,
+                       bdev_get_integrity(reference->bdev)) != 0) {
+               printk(KERN_ERR "md: failed to register integrity for %s\n",
+                       mdname(mddev));
+               return -EINVAL;
+       }
+       printk(KERN_NOTICE "md: data integrity on %s enabled\n",
+               mdname(mddev));
+       return 0;
+}
+EXPORT_SYMBOL(md_integrity_register);
+
+/* Disable data integrity if non-capable/non-matching disk is being added */
+void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
+{
+       struct blk_integrity *bi_rdev = bdev_get_integrity(rdev->bdev);
+       struct blk_integrity *bi_mddev = blk_get_integrity(mddev->gendisk);
+
+       if (!bi_mddev) /* nothing to do */
+               return;
+       if (rdev->raid_disk < 0) /* skip spares */
+               return;
+       if (bi_rdev && blk_integrity_compare(mddev->gendisk,
+                                            rdev->bdev->bd_disk) >= 0)
+               return;
+       printk(KERN_NOTICE "disabling data integrity on %s\n", mdname(mddev));
+       blk_integrity_unregister(mddev->gendisk);
+}
+EXPORT_SYMBOL(md_integrity_add_rdev);
+
 static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
 {
        char b[BDEVNAME_SIZE];
@@ -1355,8 +1604,14 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
                MD_BUG();
                return -EINVAL;
        }
-       /* make sure rdev->size exceeds mddev->size */
-       if (rdev->size && (mddev->size == 0 || rdev->size < mddev->size)) {
+
+       /* prevent duplicates */
+       if (find_rdev(mddev, rdev->bdev->bd_dev))
+               return -EEXIST;
+
+       /* make sure rdev->sectors exceeds mddev->dev_sectors */
+       if (rdev->sectors && (mddev->dev_sectors == 0 ||
+                       rdev->sectors < mddev->dev_sectors)) {
                if (mddev->pers) {
                        /* Cannot change size, so fail
                         * If mddev->level <= 0, then we don't care
@@ -1365,7 +1620,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
                        if (mddev->level > 0)
                                return -ENOSPC;
                } else
-                       mddev->size = rdev->size;
+                       mddev->dev_sectors = rdev->sectors;
        }
 
        /* Verify rdev->desc_nr is unique.
@@ -1382,29 +1637,34 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
                if (find_rdev_nr(mddev, rdev->desc_nr))
                        return -EBUSY;
        }
+       if (mddev->max_disks && rdev->desc_nr >= mddev->max_disks) {
+               printk(KERN_WARNING "md: %s: array is limited to %d devices\n",
+                      mdname(mddev), mddev->max_disks);
+               return -EBUSY;
+       }
        bdevname(rdev->bdev,b);
-       if (kobject_set_name(&rdev->kobj, "dev-%s", b) < 0)
-               return -ENOMEM;
-       while ( (s=strchr(rdev->kobj.k_name, '/')) != NULL)
+       while ( (s=strchr(b, '/')) != NULL)
                *s = '!';
-                       
+
        rdev->mddev = mddev;
        printk(KERN_INFO "md: bind<%s>\n", b);
 
-       rdev->kobj.parent = &mddev->kobj;
-       if ((err = kobject_add(&rdev->kobj)))
+       if ((err = kobject_add(&rdev->kobj, &mddev->kobj, "dev-%s", b)))
                goto fail;
 
-       if (rdev->bdev->bd_part)
-               ko = &rdev->bdev->bd_part->kobj;
-       else
-               ko = &rdev->bdev->bd_disk->kobj;
+       ko = &part_to_dev(rdev->bdev->bd_part)->kobj;
        if ((err = sysfs_create_link(&rdev->kobj, ko, "block"))) {
                kobject_del(&rdev->kobj);
                goto fail;
        }
-       list_add(&rdev->same_set, &mddev->disks);
-       bd_claim_by_disk(rdev->bdev, rdev, mddev->gendisk);
+       rdev->sysfs_state = sysfs_get_dirent(rdev->kobj.sd, "state");
+
+       list_add_rcu(&rdev->same_set, &mddev->disks);
+       bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk);
+
+       /* May as well allow recovery to be retried once */
+       mddev->recovery_disabled = 0;
+
        return 0;
 
  fail:
@@ -1413,10 +1673,11 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
        return err;
 }
 
-static void delayed_delete(struct work_struct *ws)
+static void md_delayed_delete(struct work_struct *ws)
 {
        mdk_rdev_t *rdev = container_of(ws, mdk_rdev_t, del_work);
        kobject_del(&rdev->kobj);
+       kobject_put(&rdev->kobj);
 }
 
 static void unbind_rdev_from_array(mdk_rdev_t * rdev)
@@ -1427,15 +1688,19 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev)
                return;
        }
        bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk);
-       list_del_init(&rdev->same_set);
+       list_del_rcu(&rdev->same_set);
        printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b));
        rdev->mddev = NULL;
        sysfs_remove_link(&rdev->kobj, "block");
-
+       sysfs_put(rdev->sysfs_state);
+       rdev->sysfs_state = NULL;
        /* We need to delay this, otherwise we can deadlock when
-        * writing to 'remove' to "dev/state"
+        * writing to 'remove' to "dev/state".  We also need
+        * to delay it due to rcu usage.
         */
-       INIT_WORK(&rdev->del_work, delayed_delete);
+       synchronize_rcu();
+       INIT_WORK(&rdev->del_work, md_delayed_delete);
+       kobject_get(&rdev->kobj);
        schedule_work(&rdev->del_work);
 }
 
@@ -1444,7 +1709,7 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev)
  * otherwise reused by a RAID array (or any other kernel
  * subsystem), by bd_claiming the device.
  */
-static int lock_rdev(mdk_rdev_t *rdev, dev_t dev)
+static int lock_rdev(mdk_rdev_t *rdev, dev_t dev, int shared)
 {
        int err = 0;
        struct block_device *bdev;
@@ -1456,13 +1721,15 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev)
                        __bdevname(dev, b));
                return PTR_ERR(bdev);
        }
-       err = bd_claim(bdev, rdev);
+       err = bd_claim(bdev, shared ? (mdk_rdev_t *)lock_rdev : rdev);
        if (err) {
                printk(KERN_ERR "md: could not bd_claim %s.\n",
                        bdevname(bdev, b));
-               blkdev_put(bdev);
+               blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
                return err;
        }
+       if (!shared)
+               set_bit(AllReserved, &rdev->flags);
        rdev->bdev = bdev;
        return err;
 }
@@ -1474,7 +1741,7 @@ static void unlock_rdev(mdk_rdev_t *rdev)
        if (!bdev)
                MD_BUG();
        bd_release(bdev);
-       blkdev_put(bdev);
+       blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
 }
 
 void md_autodetect_dev(dev_t dev);
@@ -1487,9 +1754,9 @@ static void export_rdev(mdk_rdev_t * rdev)
        if (rdev->mddev)
                MD_BUG();
        free_disk_sb(rdev);
-       list_del_init(&rdev->same_set);
 #ifndef MODULE
-       md_autodetect_dev(rdev->bdev->bd_dev);
+       if (test_bit(AutoDetected, &rdev->flags))
+               md_autodetect_dev(rdev->bdev->bd_dev);
 #endif
        unlock_rdev(rdev);
        kobject_put(&rdev->kobj);
@@ -1503,10 +1770,9 @@ static void kick_rdev_from_array(mdk_rdev_t * rdev)
 
 static void export_array(mddev_t *mddev)
 {
-       struct list_head *tmp;
-       mdk_rdev_t *rdev;
+       mdk_rdev_t *rdev, *tmp;
 
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       rdev_for_each(rdev, tmp, mddev) {
                if (!rdev->mddev) {
                        MD_BUG();
                        continue;
@@ -1525,7 +1791,7 @@ static void print_desc(mdp_disk_t *desc)
                desc->major,desc->minor,desc->raid_disk,desc->state);
 }
 
-static void print_sb(mdp_super_t *sb)
+static void print_sb_90(mdp_super_t *sb)
 {
        int i;
 
@@ -1556,26 +1822,82 @@ static void print_sb(mdp_super_t *sb)
        }
        printk(KERN_INFO "md:     THIS: ");
        print_desc(&sb->this_disk);
-
 }
 
-static void print_rdev(mdk_rdev_t *rdev)
+static void print_sb_1(struct mdp_superblock_1 *sb)
+{
+       __u8 *uuid;
+
+       uuid = sb->set_uuid;
+       printk(KERN_INFO
+              "md:  SB: (V:%u) (F:0x%08x) Array-ID:<%02x%02x%02x%02x"
+              ":%02x%02x:%02x%02x:%02x%02x:%02x%02x%02x%02x%02x%02x>\n"
+              "md:    Name: \"%s\" CT:%llu\n",
+               le32_to_cpu(sb->major_version),
+               le32_to_cpu(sb->feature_map),
+               uuid[0], uuid[1], uuid[2], uuid[3],
+               uuid[4], uuid[5], uuid[6], uuid[7],
+               uuid[8], uuid[9], uuid[10], uuid[11],
+               uuid[12], uuid[13], uuid[14], uuid[15],
+               sb->set_name,
+               (unsigned long long)le64_to_cpu(sb->ctime)
+                      & MD_SUPERBLOCK_1_TIME_SEC_MASK);
+
+       uuid = sb->device_uuid;
+       printk(KERN_INFO
+              "md:       L%u SZ%llu RD:%u LO:%u CS:%u DO:%llu DS:%llu SO:%llu"
+                       " RO:%llu\n"
+              "md:     Dev:%08x UUID: %02x%02x%02x%02x:%02x%02x:%02x%02x:%02x%02x"
+                       ":%02x%02x%02x%02x%02x%02x\n"
+              "md:       (F:0x%08x) UT:%llu Events:%llu ResyncOffset:%llu CSUM:0x%08x\n"
+              "md:         (MaxDev:%u) \n",
+               le32_to_cpu(sb->level),
+               (unsigned long long)le64_to_cpu(sb->size),
+               le32_to_cpu(sb->raid_disks),
+               le32_to_cpu(sb->layout),
+               le32_to_cpu(sb->chunksize),
+               (unsigned long long)le64_to_cpu(sb->data_offset),
+               (unsigned long long)le64_to_cpu(sb->data_size),
+               (unsigned long long)le64_to_cpu(sb->super_offset),
+               (unsigned long long)le64_to_cpu(sb->recovery_offset),
+               le32_to_cpu(sb->dev_number),
+               uuid[0], uuid[1], uuid[2], uuid[3],
+               uuid[4], uuid[5], uuid[6], uuid[7],
+               uuid[8], uuid[9], uuid[10], uuid[11],
+               uuid[12], uuid[13], uuid[14], uuid[15],
+               sb->devflags,
+               (unsigned long long)le64_to_cpu(sb->utime) & MD_SUPERBLOCK_1_TIME_SEC_MASK,
+               (unsigned long long)le64_to_cpu(sb->events),
+               (unsigned long long)le64_to_cpu(sb->resync_offset),
+               le32_to_cpu(sb->sb_csum),
+               le32_to_cpu(sb->max_dev)
+               );
+}
+
+static void print_rdev(mdk_rdev_t *rdev, int major_version)
 {
        char b[BDEVNAME_SIZE];
-       printk(KERN_INFO "md: rdev %s, SZ:%08llu F:%d S:%d DN:%u\n",
-               bdevname(rdev->bdev,b), (unsigned long long)rdev->size,
+       printk(KERN_INFO "md: rdev %s, Sect:%08llu F:%d S:%d DN:%u\n",
+               bdevname(rdev->bdev, b), (unsigned long long)rdev->sectors,
                test_bit(Faulty, &rdev->flags), test_bit(In_sync, &rdev->flags),
                rdev->desc_nr);
        if (rdev->sb_loaded) {
-               printk(KERN_INFO "md: rdev superblock:\n");
-               print_sb((mdp_super_t*)page_address(rdev->sb_page));
+               printk(KERN_INFO "md: rdev superblock (MJ:%d):\n", major_version);
+               switch (major_version) {
+               case 0:
+                       print_sb_90((mdp_super_t*)page_address(rdev->sb_page));
+                       break;
+               case 1:
+                       print_sb_1((struct mdp_superblock_1 *)page_address(rdev->sb_page));
+                       break;
+               }
        } else
                printk(KERN_INFO "md: no rdev superblock!\n");
 }
 
 static void md_print_devices(void)
 {
-       struct list_head *tmp, *tmp2;
+       struct list_head *tmp;
        mdk_rdev_t *rdev;
        mddev_t *mddev;
        char b[BDEVNAME_SIZE];
@@ -1584,18 +1906,18 @@ static void md_print_devices(void)
        printk("md:     **********************************\n");
        printk("md:     * <COMPLETE RAID STATE PRINTOUT> *\n");
        printk("md:     **********************************\n");
-       ITERATE_MDDEV(mddev,tmp) {
+       for_each_mddev(mddev, tmp) {
 
                if (mddev->bitmap)
                        bitmap_print_sb(mddev->bitmap);
                else
                        printk("%s: ", mdname(mddev));
-               ITERATE_RDEV(mddev,rdev,tmp2)
+               list_for_each_entry(rdev, &mddev->disks, same_set)
                        printk("<%s>", bdevname(rdev->bdev,b));
                printk("\n");
 
-               ITERATE_RDEV(mddev,rdev,tmp2)
-                       print_rdev(rdev);
+               list_for_each_entry(rdev, &mddev->disks, same_set)
+                       print_rdev(rdev, mddev->major_version);
        }
        printk("md:     **********************************\n");
        printk("\n");
@@ -1611,9 +1933,16 @@ static void sync_sbs(mddev_t * mddev, int nospares)
         * with the rest of the array)
         */
        mdk_rdev_t *rdev;
-       struct list_head *tmp;
 
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       /* First make sure individual recovery_offsets are correct */
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
+               if (rdev->raid_disk >= 0 &&
+                   !test_bit(In_sync, &rdev->flags) &&
+                   mddev->curr_resync_completed > rdev->recovery_offset)
+                               rdev->recovery_offset = mddev->curr_resync_completed;
+
+       }       
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
                if (rdev->sb_events == mddev->events ||
                    (nospares &&
                     rdev->raid_disk < 0 &&
@@ -1631,11 +1960,13 @@ static void sync_sbs(mddev_t * mddev, int nospares)
 
 static void md_update_sb(mddev_t * mddev, int force_change)
 {
-       struct list_head *tmp;
        mdk_rdev_t *rdev;
        int sync_req;
        int nospares = 0;
 
+       mddev->utime = get_seconds();
+       if (mddev->external)
+               return;
 repeat:
        spin_lock_irq(&mddev->write_lock);
 
@@ -1663,7 +1994,6 @@ repeat:
                nospares = 0;
 
        sync_req = mddev->in_sync;
-       mddev->utime = get_seconds();
 
        /* If this is just a dirty<->clean transition, and the array is clean
         * and 'events' is odd, we can roll back to the previous clean state */
@@ -1676,17 +2006,14 @@ repeat:
                /* otherwise we have to go forward and ... */
                mddev->events ++;
                if (!mddev->in_sync || mddev->recovery_cp != MaxSector) { /* not clean */
-                       /* .. if the array isn't clean, insist on an odd 'events' */
-                       if ((mddev->events&1)==0) {
-                               mddev->events++;
+                       /* .. if the array isn't clean, an 'even' event must also go
+                        * to spares. */
+                       if ((mddev->events&1)==0)
                                nospares = 0;
-                       }
                } else {
-                       /* otherwise insist on an even 'events' (for clean states) */
-                       if ((mddev->events&1)) {
-                               mddev->events++;
+                       /* otherwise an 'odd' event must go to spares */
+                       if ((mddev->events&1))
                                nospares = 0;
-                       }
                }
        }
 
@@ -1699,18 +2026,20 @@ repeat:
                MD_BUG();
                mddev->events --;
        }
-       sync_sbs(mddev, nospares);
 
        /*
         * do not write anything to disk if using
         * nonpersistent superblocks
         */
        if (!mddev->persistent) {
-               clear_bit(MD_CHANGE_PENDING, &mddev->flags);
+               if (!mddev->external)
+                       clear_bit(MD_CHANGE_PENDING, &mddev->flags);
+
                spin_unlock_irq(&mddev->write_lock);
                wake_up(&mddev->sb_wait);
                return;
        }
+       sync_sbs(mddev, nospares);
        spin_unlock_irq(&mddev->write_lock);
 
        dprintk(KERN_INFO 
@@ -1718,7 +2047,7 @@ repeat:
                mdname(mddev),mddev->in_sync);
 
        bitmap_update_sb(mddev->bitmap);
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
                char b[BDEVNAME_SIZE];
                dprintk(KERN_INFO "md: ");
                if (rdev->sb_loaded != 1)
@@ -1729,11 +2058,11 @@ repeat:
                dprintk("%s ", bdevname(rdev->bdev,b));
                if (!test_bit(Faulty, &rdev->flags)) {
                        md_super_write(mddev,rdev,
-                                      rdev->sb_offset<<1, rdev->sb_size,
+                                      rdev->sb_start, rdev->sb_size,
                                       rdev->sb_page);
                        dprintk(KERN_INFO "(write) %s's sb offset: %llu\n",
                                bdevname(rdev->bdev,b),
-                               (unsigned long long)rdev->sb_offset);
+                               (unsigned long long)rdev->sb_start);
                        rdev->sb_events = mddev->events;
 
                } else
@@ -1755,10 +2084,12 @@ repeat:
        clear_bit(MD_CHANGE_PENDING, &mddev->flags);
        spin_unlock_irq(&mddev->write_lock);
        wake_up(&mddev->sb_wait);
+       if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+               sysfs_notify(&mddev->kobj, NULL, "sync_completed");
 
 }
 
-/* words written to sysfs files may, or my not, be \n terminated.
+/* words written to sysfs files may, or may 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)
@@ -1788,7 +2119,7 @@ static ssize_t
 state_show(mdk_rdev_t *rdev, char *page)
 {
        char *sep = "";
-       int len=0;
+       size_t len = 0;
 
        if (test_bit(Faulty, &rdev->flags)) {
                len+= sprintf(page+len, "%sfaulty",sep);
@@ -1802,6 +2133,10 @@ state_show(mdk_rdev_t *rdev, char *page)
                len += sprintf(page+len, "%swrite_mostly",sep);
                sep = ",";
        }
+       if (test_bit(Blocked, &rdev->flags)) {
+               len += sprintf(page+len, "%sblocked", sep);
+               sep = ",";
+       }
        if (!test_bit(Faulty, &rdev->flags) &&
            !test_bit(In_sync, &rdev->flags)) {
                len += sprintf(page+len, "%sspare", sep);
@@ -1818,6 +2153,9 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
         *  remove  - disconnects the device
         *  writemostly - sets write_mostly
         *  -writemostly - clears write_mostly
+        *  blocked - sets the Blocked flag
+        *  -blocked - clears the Blocked flag
+        *  insync - sets Insync providing device isn't active
         */
        int err = -EINVAL;
        if (cmd_match(buf, "faulty") && rdev->mddev->pers) {
@@ -1840,24 +2178,28 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
        } else if (cmd_match(buf, "-writemostly")) {
                clear_bit(WriteMostly, &rdev->flags);
                err = 0;
+       } else if (cmd_match(buf, "blocked")) {
+               set_bit(Blocked, &rdev->flags);
+               err = 0;
+       } else if (cmd_match(buf, "-blocked")) {
+               clear_bit(Blocked, &rdev->flags);
+               wake_up(&rdev->blocked_wait);
+               set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
+               md_wakeup_thread(rdev->mddev->thread);
+
+               err = 0;
+       } else if (cmd_match(buf, "insync") && rdev->raid_disk == -1) {
+               set_bit(In_sync, &rdev->flags);
+               err = 0;
        }
+       if (!err && rdev->sysfs_state)
+               sysfs_notify_dirent(rdev->sysfs_state);
        return err ? err : len;
 }
 static struct rdev_sysfs_entry rdev_state =
 __ATTR(state, S_IRUGO|S_IWUSR, state_show, state_store);
 
 static ssize_t
-super_show(mdk_rdev_t *rdev, char *page)
-{
-       if (rdev->sb_loaded && rdev->sb_size) {
-               memcpy(page, page_address(rdev->sb_page), rdev->sb_size);
-               return rdev->sb_size;
-       } else
-               return 0;
-}
-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));
@@ -1890,20 +2232,80 @@ static ssize_t
 slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
 {
        char *e;
+       int err;
+       char nm[20];
        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);
+       if (rdev->mddev->pers && slot == -1) {
+               /* Setting 'slot' on an active array requires also
+                * updating the 'rd%d' link, and communicating
+                * with the personality with ->hot_*_disk.
+                * For now we only support removing
+                * failed/spare devices.  This normally happens automatically,
+                * but not when the metadata is externally managed.
+                */
+               if (rdev->raid_disk == -1)
+                       return -EEXIST;
+               /* personality does all needed checks */
+               if (rdev->mddev->pers->hot_add_disk == NULL)
+                       return -EINVAL;
+               err = rdev->mddev->pers->
+                       hot_remove_disk(rdev->mddev, rdev->raid_disk);
+               if (err)
+                       return err;
+               sprintf(nm, "rd%d", rdev->raid_disk);
+               sysfs_remove_link(&rdev->mddev->kobj, nm);
+               set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
+               md_wakeup_thread(rdev->mddev->thread);
+       } else if (rdev->mddev->pers) {
+               mdk_rdev_t *rdev2;
+               /* Activating a spare .. or possibly reactivating
+                * if we ever get bitmaps working here.
+                */
+
+               if (rdev->raid_disk != -1)
+                       return -EBUSY;
+
+               if (rdev->mddev->pers->hot_add_disk == NULL)
+                       return -EINVAL;
+
+               list_for_each_entry(rdev2, &rdev->mddev->disks, same_set)
+                       if (rdev2->raid_disk == slot)
+                               return -EEXIST;
+
+               rdev->raid_disk = slot;
+               if (test_bit(In_sync, &rdev->flags))
+                       rdev->saved_raid_disk = slot;
+               else
+                       rdev->saved_raid_disk = -1;
+               err = rdev->mddev->pers->
+                       hot_add_disk(rdev->mddev, rdev);
+               if (err) {
+                       rdev->raid_disk = -1;
+                       return err;
+               } else
+                       sysfs_notify_dirent(rdev->sysfs_state);
+               sprintf(nm, "rd%d", rdev->raid_disk);
+               if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm))
+                       printk(KERN_WARNING
+                              "md: cannot register "
+                              "%s for %s\n",
+                              nm, mdname(rdev->mddev));
+
+               /* don't wakeup anyone, leave that to userspace. */
+       } else {
+               if (slot >= rdev->mddev->raid_disks)
+                       return -ENOSPC;
+               rdev->raid_disk = slot;
+               /* assume it is working */
+               clear_bit(Faulty, &rdev->flags);
+               clear_bit(WriteMostly, &rdev->flags);
+               set_bit(In_sync, &rdev->flags);
+               sysfs_notify_dirent(rdev->sysfs_state);
+       }
        return len;
 }
 
@@ -1924,7 +2326,11 @@ offset_store(mdk_rdev_t *rdev, const char *buf, size_t len)
        unsigned long long offset = simple_strtoull(buf, &e, 10);
        if (e==buf || (*e && *e != '\n'))
                return -EINVAL;
-       if (rdev->mddev->pers)
+       if (rdev->mddev->pers && rdev->raid_disk >= 0)
+               return -EBUSY;
+       if (rdev->sectors && rdev->mddev->external)
+               /* Must set offset before size, so overlap checks
+                * can be sane */
                return -EBUSY;
        rdev->data_offset = offset;
        return len;
@@ -1936,30 +2342,112 @@ __ATTR(offset, S_IRUGO|S_IWUSR, 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);
+       return sprintf(page, "%llu\n", (unsigned long long)rdev->sectors / 2);
 }
 
-static ssize_t
-rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+static int overlaps(sector_t s1, sector_t l1, sector_t s2, sector_t l2)
 {
-       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;
+       /* check if two start/length pairs overlap */
+       if (s1+l1 <= s2)
+               return 0;
+       if (s2+l2 <= s1)
+               return 0;
+       return 1;
 }
 
-static struct rdev_sysfs_entry rdev_size =
-__ATTR(size, S_IRUGO|S_IWUSR, rdev_size_show, rdev_size_store);
+static int strict_blocks_to_sectors(const char *buf, sector_t *sectors)
+{
+       unsigned long long blocks;
+       sector_t new;
 
-static struct attribute *rdev_default_attrs[] = {
+       if (strict_strtoull(buf, 10, &blocks) < 0)
+               return -EINVAL;
+
+       if (blocks & 1ULL << (8 * sizeof(blocks) - 1))
+               return -EINVAL; /* sector conversion overflow */
+
+       new = blocks * 2;
+       if (new != blocks * 2)
+               return -EINVAL; /* unsigned long long to sector_t overflow */
+
+       *sectors = new;
+       return 0;
+}
+
+static ssize_t
+rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+{
+       mddev_t *my_mddev = rdev->mddev;
+       sector_t oldsectors = rdev->sectors;
+       sector_t sectors;
+
+       if (strict_blocks_to_sectors(buf, &sectors) < 0)
+               return -EINVAL;
+       if (my_mddev->pers && rdev->raid_disk >= 0) {
+               if (my_mddev->persistent) {
+                       sectors = super_types[my_mddev->major_version].
+                               rdev_size_change(rdev, sectors);
+                       if (!sectors)
+                               return -EBUSY;
+               } else if (!sectors)
+                       sectors = (rdev->bdev->bd_inode->i_size >> 9) -
+                               rdev->data_offset;
+       }
+       if (sectors < my_mddev->dev_sectors)
+               return -EINVAL; /* component must fit device */
+
+       rdev->sectors = sectors;
+       if (sectors > oldsectors && my_mddev->external) {
+               /* need to check that all other rdevs with the same ->bdev
+                * do not overlap.  We need to unlock the mddev to avoid
+                * a deadlock.  We have already changed rdev->sectors, and if
+                * we have to change it back, we will have the lock again.
+                */
+               mddev_t *mddev;
+               int overlap = 0;
+               struct list_head *tmp;
+
+               mddev_unlock(my_mddev);
+               for_each_mddev(mddev, tmp) {
+                       mdk_rdev_t *rdev2;
+
+                       mddev_lock(mddev);
+                       list_for_each_entry(rdev2, &mddev->disks, same_set)
+                               if (test_bit(AllReserved, &rdev2->flags) ||
+                                   (rdev->bdev == rdev2->bdev &&
+                                    rdev != rdev2 &&
+                                    overlaps(rdev->data_offset, rdev->sectors,
+                                             rdev2->data_offset,
+                                             rdev2->sectors))) {
+                                       overlap = 1;
+                                       break;
+                               }
+                       mddev_unlock(mddev);
+                       if (overlap) {
+                               mddev_put(mddev);
+                               break;
+                       }
+               }
+               mddev_lock(my_mddev);
+               if (overlap) {
+                       /* Someone else could have slipped in a size
+                        * change here, but doing so is just silly.
+                        * We put oldsectors back because we *know* it is
+                        * safe, and trust userspace not to race with
+                        * itself
+                        */
+                       rdev->sectors = oldsectors;
+                       return -EBUSY;
+               }
+       }
+       return len;
+}
+
+static struct rdev_sysfs_entry rdev_size =
+__ATTR(size, S_IRUGO|S_IWUSR, 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,
@@ -1971,10 +2459,21 @@ rdev_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
 {
        struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr);
        mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj);
+       mddev_t *mddev = rdev->mddev;
+       ssize_t rv;
 
        if (!entry->show)
                return -EIO;
-       return entry->show(rdev, page);
+
+       rv = mddev ? mddev_lock(mddev) : -EBUSY;
+       if (!rv) {
+               if (rdev->mddev == NULL)
+                       rv = -EBUSY;
+               else
+                       rv = entry->show(rdev, page);
+               mddev_unlock(mddev);
+       }
+       return rv;
 }
 
 static ssize_t
@@ -1983,12 +2482,22 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr,
 {
        struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr);
        mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj);
+       ssize_t rv;
+       mddev_t *mddev = rdev->mddev;
 
        if (!entry->store)
                return -EIO;
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
-       return entry->store(rdev, page, length);
+       rv = mddev ? mddev_lock(mddev): -EBUSY;
+       if (!rv) {
+               if (rdev->mddev == NULL)
+                       rv = -EBUSY;
+               else
+                       rv = entry->store(rdev, page, length);
+               mddev_unlock(mddev);
+       }
+       return rv;
 }
 
 static void rdev_free(struct kobject *ko)
@@ -2032,13 +2541,11 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
        if ((err = alloc_disk_sb(rdev)))
                goto abort_free;
 
-       err = lock_rdev(rdev, newdev);
+       err = lock_rdev(rdev, newdev, super_format == -2);
        if (err)
                goto abort_free;
 
-       rdev->kobj.parent = NULL;
-       rdev->kobj.ktype = &rdev_ktype;
-       kobject_init(&rdev->kobj);
+       kobject_init(&rdev->kobj, &rdev_ktype);
 
        rdev->desc_nr = -1;
        rdev->saved_raid_disk = -1;
@@ -2077,7 +2584,9 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
                        goto abort_free;
                }
        }
+
        INIT_LIST_HEAD(&rdev->same_set);
+       init_waitqueue_head(&rdev->blocked_wait);
 
        return rdev;
 
@@ -2099,12 +2608,11 @@ abort_free:
 static void analyze_sbs(mddev_t * mddev)
 {
        int i;
-       struct list_head *tmp;
-       mdk_rdev_t *rdev, *freshest;
+       mdk_rdev_t *rdev, *freshest, *tmp;
        char b[BDEVNAME_SIZE];
 
        freshest = NULL;
-       ITERATE_RDEV(mddev,rdev,tmp)
+       rdev_for_each(rdev, tmp, mddev)
                switch (super_types[mddev->major_version].
                        load_super(rdev, freshest, mddev->minor_version)) {
                case 1:
@@ -2125,7 +2633,16 @@ static void analyze_sbs(mddev_t * mddev)
                validate_super(mddev, freshest);
 
        i = 0;
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       rdev_for_each(rdev, tmp, mddev) {
+               if (rdev->desc_nr >= mddev->max_disks ||
+                   i > mddev->max_disks) {
+                       printk(KERN_WARNING
+                              "md: %s: %s: only %d devices permitted\n",
+                              mdname(mddev), bdevname(rdev->bdev, b),
+                              mddev->max_disks);
+                       kick_rdev_from_array(rdev);
+                       continue;
+               }
                if (rdev != freshest)
                        if (super_types[mddev->major_version].
                            validate_super(mddev, rdev)) {
@@ -2139,22 +2656,15 @@ static void analyze_sbs(mddev_t * mddev)
                        rdev->desc_nr = i++;
                        rdev->raid_disk = rdev->desc_nr;
                        set_bit(In_sync, &rdev->flags);
-               } else if (rdev->raid_disk >= mddev->raid_disks) {
+               } else if (rdev->raid_disk >= (mddev->raid_disks - min(0, mddev->delta_disks))) {
                        rdev->raid_disk = -1;
                        clear_bit(In_sync, &rdev->flags);
                }
        }
-
-
-
-       if (mddev->recovery_cp != MaxSector &&
-           mddev->level >= 1)
-               printk(KERN_ERR "md: %s: raid array is not clean"
-                      " -- starting background reconstruction\n",
-                      mdname(mddev));
-
 }
 
+static void md_safemode_timeout(unsigned long data);
+
 static ssize_t
 safe_delay_show(mddev_t *mddev, char *page)
 {
@@ -2169,12 +2679,11 @@ safe_delay_store(mddev_t *mddev, const char *cbuf, size_t len)
        int i;
        unsigned long msec;
        char buf[30];
-       char *e;
+
        /* remove a period, and count digits after it */
        if (len >= sizeof(buf))
                return -EINVAL;
-       strlcpy(buf, cbuf, len);
-       buf[len] = 0;
+       strlcpy(buf, cbuf, sizeof(buf));
        for (i=0; i<len; i++) {
                if (dot) {
                        if (isdigit(buf[i])) {
@@ -2187,16 +2696,18 @@ safe_delay_store(mddev_t *mddev, const char *cbuf, size_t len)
                        buf[i] = 0;
                }
        }
-       msec = simple_strtoul(buf, &e, 10);
-       if (e == buf || (*e && *e != '\n'))
+       if (strict_strtoul(buf, 10, &msec) < 0)
                return -EINVAL;
        msec = (msec * 1000) / scale;
        if (msec == 0)
                mddev->safemode_delay = 0;
        else {
+               unsigned long old_delay = mddev->safemode_delay;
                mddev->safemode_delay = (msec*HZ)/1000;
                if (mddev->safemode_delay == 0)
                        mddev->safemode_delay = 1;
+               if (mddev->safemode_delay < old_delay)
+                       md_safemode_timeout((unsigned long)mddev);
        }
        return len;
 }
@@ -2220,18 +2731,108 @@ level_show(mddev_t *mddev, char *page)
 static ssize_t
 level_store(mddev_t *mddev, const char *buf, size_t len)
 {
-       int rv = len;
-       if (mddev->pers)
+       char level[16];
+       ssize_t rv = len;
+       struct mdk_personality *pers;
+       void *priv;
+       mdk_rdev_t *rdev;
+
+       if (mddev->pers == NULL) {
+               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;
+       }
+
+       /* request to change the personality.  Need to ensure:
+        *  - array is not engaged in resync/recovery/reshape
+        *  - old personality can be suspended
+        *  - new personality will access other array.
+        */
+
+       if (mddev->sync_thread || mddev->reshape_position != MaxSector)
                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')
+
+       if (!mddev->pers->quiesce) {
+               printk(KERN_WARNING "md: %s: %s does not support online personality change\n",
+                      mdname(mddev), mddev->pers->name);
+               return -EINVAL;
+       }
+
+       /* Now find the new personality */
+       if (len == 0 || len >= sizeof(level))
+               return -EINVAL;
+       strncpy(level, buf, len);
+       if (level[len-1] == '\n')
                len--;
-       mddev->clevel[len] = 0;
-       mddev->level = LEVEL_NONE;
+       level[len] = 0;
+
+       request_module("md-%s", level);
+       spin_lock(&pers_lock);
+       pers = find_pers(LEVEL_NONE, level);
+       if (!pers || !try_module_get(pers->owner)) {
+               spin_unlock(&pers_lock);
+               printk(KERN_WARNING "md: personality %s not loaded\n", level);
+               return -EINVAL;
+       }
+       spin_unlock(&pers_lock);
+
+       if (pers == mddev->pers) {
+               /* Nothing to do! */
+               module_put(pers->owner);
+               return rv;
+       }
+       if (!pers->takeover) {
+               module_put(pers->owner);
+               printk(KERN_WARNING "md: %s: %s does not support personality takeover\n",
+                      mdname(mddev), level);
+               return -EINVAL;
+       }
+
+       /* ->takeover must set new_* and/or delta_disks
+        * if it succeeds, and may set them when it fails.
+        */
+       priv = pers->takeover(mddev);
+       if (IS_ERR(priv)) {
+               mddev->new_level = mddev->level;
+               mddev->new_layout = mddev->layout;
+               mddev->new_chunk_sectors = mddev->chunk_sectors;
+               mddev->raid_disks -= mddev->delta_disks;
+               mddev->delta_disks = 0;
+               module_put(pers->owner);
+               printk(KERN_WARNING "md: %s: %s would not accept array\n",
+                      mdname(mddev), level);
+               return PTR_ERR(priv);
+       }
+
+       /* Looks like we have a winner */
+       mddev_suspend(mddev);
+       mddev->pers->stop(mddev);
+       module_put(mddev->pers->owner);
+       /* Invalidate devices that are now superfluous */
+       list_for_each_entry(rdev, &mddev->disks, same_set)
+               if (rdev->raid_disk >= mddev->raid_disks) {
+                       rdev->raid_disk = -1;
+                       clear_bit(In_sync, &rdev->flags);
+               }
+       mddev->pers = pers;
+       mddev->private = priv;
+       strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
+       mddev->level = mddev->new_level;
+       mddev->layout = mddev->new_layout;
+       mddev->chunk_sectors = mddev->new_chunk_sectors;
+       mddev->delta_disks = 0;
+       pers->run(mddev);
+       mddev_resume(mddev);
+       set_bit(MD_CHANGE_DEVS, &mddev->flags);
+       set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+       md_wakeup_thread(mddev->thread);
        return rv;
 }
 
@@ -2259,12 +2860,21 @@ layout_store(mddev_t *mddev, const char *buf, size_t len)
        if (!*buf || (*e && *e != '\n'))
                return -EINVAL;
 
-       if (mddev->pers)
-               return -EBUSY;
-       if (mddev->reshape_position != MaxSector)
+       if (mddev->pers) {
+               int err;
+               if (mddev->pers->check_reshape == NULL)
+                       return -EBUSY;
                mddev->new_layout = n;
-       else
-               mddev->layout = n;
+               err = mddev->pers->check_reshape(mddev);
+               if (err) {
+                       mddev->new_layout = mddev->layout;
+                       return err;
+               }
+       } else {
+               mddev->new_layout = n;
+               if (mddev->reshape_position == MaxSector)
+                       mddev->layout = n;
+       }
        return len;
 }
 static struct md_sysfs_entry md_layout =
@@ -2312,28 +2922,37 @@ static ssize_t
 chunk_size_show(mddev_t *mddev, char *page)
 {
        if (mddev->reshape_position != MaxSector &&
-           mddev->chunk_size != mddev->new_chunk)
-               return sprintf(page, "%d (%d)\n", mddev->new_chunk,
-                              mddev->chunk_size);
-       return sprintf(page, "%d\n", mddev->chunk_size);
+           mddev->chunk_sectors != mddev->new_chunk_sectors)
+               return sprintf(page, "%d (%d)\n",
+                              mddev->new_chunk_sectors << 9,
+                              mddev->chunk_sectors << 9);
+       return sprintf(page, "%d\n", mddev->chunk_sectors << 9);
 }
 
 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 (!*buf || (*e && *e != '\n'))
                return -EINVAL;
 
-       if (mddev->pers)
-               return -EBUSY;
-       else if (mddev->reshape_position != MaxSector)
-               mddev->new_chunk = n;
-       else
-               mddev->chunk_size = n;
+       if (mddev->pers) {
+               int err;
+               if (mddev->pers->check_reshape == NULL)
+                       return -EBUSY;
+               mddev->new_chunk_sectors = n >> 9;
+               err = mddev->pers->check_reshape(mddev);
+               if (err) {
+                       mddev->new_chunk_sectors = mddev->chunk_sectors;
+                       return err;
+               }
+       } else {
+               mddev->new_chunk_sectors = n >> 9;
+               if (mddev->reshape_position == MaxSector)
+                       mddev->chunk_sectors = n >> 9;
+       }
        return len;
 }
 static struct md_sysfs_entry md_chunk_size =
@@ -2342,13 +2961,14 @@ __ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store);
 static ssize_t
 resync_start_show(mddev_t *mddev, char *page)
 {
+       if (mddev->recovery_cp == MaxSector)
+               return sprintf(page, "none\n");
        return sprintf(page, "%llu\n", (unsigned long long)mddev->recovery_cp);
 }
 
 static ssize_t
 resync_start_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 long n = simple_strtoull(buf, &e, 10);
 
@@ -2375,7 +2995,7 @@ __ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store);
  *     When written, doesn't tear down array, but just stops it
  * suspended (not supported yet)
  *     All IO requests will block. The array can be reconfigured.
- *     Writing this, if accepted, will block until array is quiessent
+ *     Writing this, if accepted, will block until array is quiescent
  * readonly
  *     no resync can happen.  no superblocks get written.
  *     write requests fail
@@ -2430,6 +3050,8 @@ array_state_show(mddev_t *mddev, char *page)
                case 0:
                        if (mddev->in_sync)
                                st = clean;
+                       else if (test_bit(MD_CHANGE_CLEAN, &mddev->flags))
+                               st = write_pending;
                        else if (mddev->safemode)
                                st = active_idle;
                        else
@@ -2438,7 +3060,7 @@ array_state_show(mddev_t *mddev, char *page)
        else {
                if (list_empty(&mddev->disks) &&
                    mddev->raid_disks == 0 &&
-                   mddev->size == 0)
+                   mddev->dev_sectors == 0)
                        st = clear;
                else
                        st = inactive;
@@ -2446,7 +3068,7 @@ array_state_show(mddev_t *mddev, char *page)
        return sprintf(page, "%s\n", array_states[st]);
 }
 
-static int do_md_stop(mddev_t * mddev, int ro);
+static int do_md_stop(mddev_t * mddev, int ro, int is_open);
 static int do_md_run(mddev_t * mddev);
 static int restart_array(mddev_t *mddev);
 
@@ -2460,36 +3082,40 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
                break;
        case clear:
                /* stopping an active array */
-               if (mddev->pers) {
-                       if (atomic_read(&mddev->active) > 1)
-                               return -EBUSY;
-                       err = do_md_stop(mddev, 0);
-               }
+               if (atomic_read(&mddev->openers) > 0)
+                       return -EBUSY;
+               err = do_md_stop(mddev, 0, 0);
                break;
        case inactive:
                /* stopping an active array */
                if (mddev->pers) {
-                       if (atomic_read(&mddev->active) > 1)
+                       if (atomic_read(&mddev->openers) > 0)
                                return -EBUSY;
-                       err = do_md_stop(mddev, 2);
-               }
+                       err = do_md_stop(mddev, 2, 0);
+               } else
+                       err = 0; /* already inactive */
                break;
        case suspended:
                break; /* not supported yet */
        case readonly:
                if (mddev->pers)
-                       err = do_md_stop(mddev, 1);
+                       err = do_md_stop(mddev, 1, 0);
                else {
                        mddev->ro = 1;
+                       set_disk_ro(mddev->gendisk, 1);
                        err = do_md_run(mddev);
                }
                break;
        case read_auto:
-               /* stopping an active array */
                if (mddev->pers) {
-                       err = do_md_stop(mddev, 1);
-                       if (err == 0)
-                               mddev->ro = 2; /* FIXME mark devices writable */
+                       if (mddev->ro == 0)
+                               err = do_md_stop(mddev, 1, 0);
+                       else if (mddev->ro == 1)
+                               err = restart_array(mddev);
+                       if (err == 0) {
+                               mddev->ro = 2;
+                               set_disk_ro(mddev->gendisk, 0);
+                       }
                } else {
                        mddev->ro = 2;
                        err = do_md_run(mddev);
@@ -2500,24 +3126,31 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
                        restart_array(mddev);
                        spin_lock_irq(&mddev->write_lock);
                        if (atomic_read(&mddev->writes_pending) == 0) {
-                               mddev->in_sync = 1;
-                               set_bit(MD_CHANGE_CLEAN, &mddev->flags);
-                       }
+                               if (mddev->in_sync == 0) {
+                                       mddev->in_sync = 1;
+                                       if (mddev->safemode == 1)
+                                               mddev->safemode = 0;
+                                       if (mddev->persistent)
+                                               set_bit(MD_CHANGE_CLEAN,
+                                                       &mddev->flags);
+                               }
+                               err = 0;
+                       } else
+                               err = -EBUSY;
                        spin_unlock_irq(&mddev->write_lock);
-               } else {
-                       mddev->ro = 0;
-                       mddev->recovery_cp = MaxSector;
-                       err = do_md_run(mddev);
-               }
+               } else
+                       err = -EINVAL;
                break;
        case active:
                if (mddev->pers) {
                        restart_array(mddev);
-                       clear_bit(MD_CHANGE_CLEAN, &mddev->flags);
+                       if (mddev->external)
+                               clear_bit(MD_CHANGE_CLEAN, &mddev->flags);
                        wake_up(&mddev->sb_wait);
                        err = 0;
                } else {
                        mddev->ro = 0;
+                       set_disk_ro(mddev->gendisk, 0);
                        err = do_md_run(mddev);
                }
                break;
@@ -2528,8 +3161,10 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
        }
        if (err)
                return err;
-       else
+       else {
+               sysfs_notify_dirent(mddev->sysfs_state);
                return len;
+       }
 }
 static struct md_sysfs_entry md_array_state =
 __ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store);
@@ -2579,7 +3214,9 @@ new_dev_store(mddev_t *mddev, const char *buf, size_t len)
                        if (err < 0)
                                goto out;
                }
-       } else
+       } else if (mddev->external)
+               rdev = md_import_device(dev, -2, -1);
+       else
                rdev = md_import_device(dev, -1, -1);
 
        if (IS_ERR(rdev))
@@ -2627,10 +3264,11 @@ __ATTR(bitmap_set_bits, S_IWUSR, null_show, bitmap_store);
 static ssize_t
 size_show(mddev_t *mddev, char *page)
 {
-       return sprintf(page, "%llu\n", (unsigned long long)mddev->size);
+       return sprintf(page, "%llu\n",
+               (unsigned long long)mddev->dev_sectors / 2);
 }
 
-static int update_size(mddev_t *mddev, unsigned long size);
+static int update_size(mddev_t *mddev, sector_t num_sectors);
 
 static ssize_t
 size_store(mddev_t *mddev, const char *buf, size_t len)
@@ -2639,20 +3277,18 @@ size_store(mddev_t *mddev, const char *buf, size_t len)
         * 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;
+       sector_t sectors;
+       int err = strict_blocks_to_sectors(buf, &sectors);
 
+       if (err < 0)
+               return err;
        if (mddev->pers) {
-               err = update_size(mddev, size);
+               err = update_size(mddev, sectors);
                md_update_sb(mddev, 1);
        } else {
-               if (mddev->size == 0 ||
-                   mddev->size > size)
-                       mddev->size = size;
+               if (mddev->dev_sectors == 0 ||
+                   mddev->dev_sectors > sectors)
+                       mddev->dev_sectors = sectors;
                else
                        err = -ENOSPC;
        }
@@ -2664,7 +3300,9 @@ __ATTR(component_size, S_IRUGO|S_IWUSR, size_show, size_store);
 
 
 /* Metdata version.
- * This is either 'none' for arrays with externally managed metadata,
+ * This is one of
+ *   'none' for arrays with no metadata (good luck...)
+ *   'external' for arrays with externally managed metadata,
  * or N.M for internally known formats
  */
 static ssize_t
@@ -2673,6 +3311,8 @@ metadata_show(mddev_t *mddev, char *page)
        if (mddev->persistent)
                return sprintf(page, "%d.%d\n",
                               mddev->major_version, mddev->minor_version);
+       else if (mddev->external)
+               return sprintf(page, "external:%s\n", mddev->metadata_type);
        else
                return sprintf(page, "none\n");
 }
@@ -2682,11 +3322,32 @@ metadata_store(mddev_t *mddev, const char *buf, size_t len)
 {
        int major, minor;
        char *e;
-       if (!list_empty(&mddev->disks))
+       /* Changing the details of 'external' metadata is
+        * always permitted.  Otherwise there must be
+        * no devices attached to the array.
+        */
+       if (mddev->external && strncmp(buf, "external:", 9) == 0)
+               ;
+       else if (!list_empty(&mddev->disks))
                return -EBUSY;
 
        if (cmd_match(buf, "none")) {
                mddev->persistent = 0;
+               mddev->external = 0;
+               mddev->major_version = 0;
+               mddev->minor_version = 90;
+               return len;
+       }
+       if (strncmp(buf, "external:", 9) == 0) {
+               size_t namelen = len-9;
+               if (namelen >= sizeof(mddev->metadata_type))
+                       namelen = sizeof(mddev->metadata_type)-1;
+               strncpy(mddev->metadata_type, buf+9, namelen);
+               mddev->metadata_type[namelen] = 0;
+               if (namelen && mddev->metadata_type[namelen-1] == '\n')
+                       mddev->metadata_type[--namelen] = 0;
+               mddev->persistent = 0;
+               mddev->external = 1;
                mddev->major_version = 0;
                mddev->minor_version = 90;
                return len;
@@ -2703,6 +3364,7 @@ metadata_store(mddev_t *mddev, const char *buf, size_t len)
        mddev->major_version = major;
        mddev->minor_version = minor;
        mddev->persistent = 1;
+       mddev->external = 0;
        return len;
 }
 
@@ -2713,8 +3375,10 @@ static ssize_t
 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_FROZEN, &mddev->recovery))
+               type = "frozen";
+       else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
+           (!mddev->ro && test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))) {
                if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
                        type = "reshape";
                else if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
@@ -2724,7 +3388,7 @@ action_show(mddev_t *mddev, char *page)
                                type = "check";
                        else
                                type = "repair";
-               } else
+               } else if (test_bit(MD_RECOVERY_RECOVER, &mddev->recovery))
                        type = "recover";
        }
        return sprintf(page, "%s\n", type);
@@ -2736,7 +3400,12 @@ action_store(mddev_t *mddev, const char *page, size_t len)
        if (!mddev->pers || !mddev->pers->sync_request)
                return -EINVAL;
 
-       if (cmd_match(page, "idle")) {
+       if (cmd_match(page, "frozen"))
+               set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+       else
+               clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+
+       if (cmd_match(page, "idle") || cmd_match(page, "frozen")) {
                if (mddev->sync_thread) {
                        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
                        md_unregister_thread(mddev->sync_thread);
@@ -2746,15 +3415,19 @@ action_store(mddev_t *mddev, const char *page, size_t len)
        } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
                   test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
                return -EBUSY;
-       else if (cmd_match(page, "resync") || cmd_match(page, "recover"))
+       else if (cmd_match(page, "resync"))
+               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+       else if (cmd_match(page, "recover")) {
+               set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
                set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
-       else if (cmd_match(page, "reshape")) {
+       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;
+               sysfs_notify(&mddev->kobj, NULL, "degraded");
        } else {
                if (cmd_match(page, "check"))
                        set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
@@ -2765,6 +3438,7 @@ action_store(mddev_t *mddev, const char *page, size_t len)
        }
        set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
+       sysfs_notify_dirent(mddev->sysfs_action);
        return len;
 }
 
@@ -2833,16 +3507,54 @@ sync_max_store(mddev_t *mddev, const char *buf, size_t 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
+degraded_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%d\n", mddev->degraded);
+}
+static struct md_sysfs_entry md_degraded = __ATTR_RO(degraded);
+
+static ssize_t
+sync_force_parallel_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%d\n", mddev->parallel_resync);
+}
+
+static ssize_t
+sync_force_parallel_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       long n;
+
+       if (strict_strtol(buf, 10, &n))
+               return -EINVAL;
+
+       if (n != 0 && n != 1)
+               return -EINVAL;
+
+       mddev->parallel_resync = n;
+
+       if (mddev->sync_thread)
+               wake_up(&resync_wait);
+
+       return len;
+}
+
+/* force parallel resync, even with shared block devices */
+static struct md_sysfs_entry md_sync_force_parallel =
+__ATTR(sync_force_parallel, S_IRUGO|S_IWUSR,
+       sync_force_parallel_show, sync_force_parallel_store);
 
 static ssize_t
 sync_speed_show(mddev_t *mddev, char *page)
 {
        unsigned long resync, dt, db;
-       resync = (mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active));
-       dt = ((jiffies - mddev->resync_mark) / HZ);
+       if (mddev->curr_resync == 0)
+               return sprintf(page, "none\n");
+       resync = mddev->curr_mark_cnt - 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 */
+       db = resync - mddev->resync_mark_cnt;
+       return sprintf(page, "%lu\n", db/dt/2); /* K/sec */
 }
 
 static struct md_sysfs_entry md_sync_speed = __ATTR_RO(sync_speed);
@@ -2850,20 +3562,94 @@ 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;
+       unsigned long max_sectors, resync;
+
+       if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+               return sprintf(page, "none\n");
 
        if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
-               max_blocks = mddev->resync_max_sectors;
+               max_sectors = mddev->resync_max_sectors;
        else
-               max_blocks = mddev->size << 1;
+               max_sectors = mddev->dev_sectors;
 
-       resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active));
-       return sprintf(page, "%lu / %lu\n", resync, max_blocks);
+       resync = mddev->curr_resync_completed;
+       return sprintf(page, "%lu / %lu\n", resync, max_sectors);
 }
 
 static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
 
 static ssize_t
+min_sync_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%llu\n",
+                      (unsigned long long)mddev->resync_min);
+}
+static ssize_t
+min_sync_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       unsigned long long min;
+       if (strict_strtoull(buf, 10, &min))
+               return -EINVAL;
+       if (min > mddev->resync_max)
+               return -EINVAL;
+       if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+               return -EBUSY;
+
+       /* Must be a multiple of chunk_size */
+       if (mddev->chunk_sectors) {
+               sector_t temp = min;
+               if (sector_div(temp, mddev->chunk_sectors))
+                       return -EINVAL;
+       }
+       mddev->resync_min = min;
+
+       return len;
+}
+
+static struct md_sysfs_entry md_min_sync =
+__ATTR(sync_min, S_IRUGO|S_IWUSR, min_sync_show, min_sync_store);
+
+static ssize_t
+max_sync_show(mddev_t *mddev, char *page)
+{
+       if (mddev->resync_max == MaxSector)
+               return sprintf(page, "max\n");
+       else
+               return sprintf(page, "%llu\n",
+                              (unsigned long long)mddev->resync_max);
+}
+static ssize_t
+max_sync_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       if (strncmp(buf, "max", 3) == 0)
+               mddev->resync_max = MaxSector;
+       else {
+               unsigned long long max;
+               if (strict_strtoull(buf, 10, &max))
+                       return -EINVAL;
+               if (max < mddev->resync_min)
+                       return -EINVAL;
+               if (max < mddev->resync_max &&
+                   mddev->ro == 0 &&
+                   test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+                       return -EBUSY;
+
+               /* Must be a multiple of chunk_size */
+               if (mddev->chunk_sectors) {
+                       sector_t temp = max;
+                       if (sector_div(temp, mddev->chunk_sectors))
+                               return -EINVAL;
+               }
+               mddev->resync_max = max;
+       }
+       wake_up(&mddev->recovery_wait);
+       return len;
+}
+
+static struct md_sysfs_entry md_max_sync =
+__ATTR(sync_max, S_IRUGO|S_IWUSR, max_sync_show, max_sync_store);
+
+static ssize_t
 suspend_lo_show(mddev_t *mddev, char *page)
 {
        return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_lo);
@@ -2875,7 +3661,8 @@ 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)
+       if (mddev->pers == NULL || 
+           mddev->pers->quiesce == NULL)
                return -EINVAL;
        if (buf == e || (*e && *e != '\n'))
                return -EINVAL;
@@ -2903,7 +3690,8 @@ 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)
+       if (mddev->pers == NULL ||
+           mddev->pers->quiesce == NULL)
                return -EINVAL;
        if (buf == e || (*e && *e != '\n'))
                return -EINVAL;
@@ -2942,7 +3730,7 @@ reshape_position_store(mddev_t *mddev, const char *buf, size_t len)
        mddev->delta_disks = 0;
        mddev->new_level = mddev->level;
        mddev->new_layout = mddev->layout;
-       mddev->new_chunk = mddev->chunk_size;
+       mddev->new_chunk_sectors = mddev->chunk_sectors;
        return len;
 }
 
@@ -2950,6 +3738,48 @@ static struct md_sysfs_entry md_reshape_position =
 __ATTR(reshape_position, S_IRUGO|S_IWUSR, reshape_position_show,
        reshape_position_store);
 
+static ssize_t
+array_size_show(mddev_t *mddev, char *page)
+{
+       if (mddev->external_size)
+               return sprintf(page, "%llu\n",
+                              (unsigned long long)mddev->array_sectors/2);
+       else
+               return sprintf(page, "default\n");
+}
+
+static ssize_t
+array_size_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       sector_t sectors;
+
+       if (strncmp(buf, "default", 7) == 0) {
+               if (mddev->pers)
+                       sectors = mddev->pers->size(mddev, 0, 0);
+               else
+                       sectors = mddev->array_sectors;
+
+               mddev->external_size = 0;
+       } else {
+               if (strict_blocks_to_sectors(buf, &sectors) < 0)
+                       return -EINVAL;
+               if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors)
+                       return -E2BIG;
+
+               mddev->external_size = 1;
+       }
+
+       mddev->array_sectors = sectors;
+       set_capacity(mddev->gendisk, mddev->array_sectors);
+       if (mddev->pers)
+               revalidate_disk(mddev->gendisk);
+
+       return len;
+}
+
+static struct md_sysfs_entry md_array_size =
+__ATTR(array_size, S_IRUGO|S_IWUSR, array_size_show,
+       array_size_store);
 
 static struct attribute *md_default_attrs[] = {
        &md_level.attr,
@@ -2963,6 +3793,7 @@ static struct attribute *md_default_attrs[] = {
        &md_safe_delay.attr,
        &md_array_state.attr,
        &md_reshape_position.attr,
+       &md_array_size.attr,
        NULL,
 };
 
@@ -2972,10 +3803,14 @@ static struct attribute *md_redundancy_attrs[] = {
        &md_sync_min.attr,
        &md_sync_max.attr,
        &md_sync_speed.attr,
+       &md_sync_force_parallel.attr,
        &md_sync_completed.attr,
+       &md_min_sync.attr,
+       &md_max_sync.attr,
        &md_suspend_lo.attr,
        &md_suspend_hi.attr,
        &md_bitmap.attr,
+       &md_degraded.attr,
        NULL,
 };
 static struct attribute_group md_redundancy_group = {
@@ -3014,6 +3849,8 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
        rv = mddev_lock(mddev);
+       if (mddev->hold_active == UNTIL_IOCTL)
+               mddev->hold_active = 0;
        if (!rv) {
                rv = entry->store(mddev, page, length);
                mddev_unlock(mddev);
@@ -3024,6 +3861,17 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
 static void md_free(struct kobject *ko)
 {
        mddev_t *mddev = container_of(ko, mddev_t, kobj);
+
+       if (mddev->sysfs_state)
+               sysfs_put(mddev->sysfs_state);
+
+       if (mddev->gendisk) {
+               del_gendisk(mddev->gendisk);
+               put_disk(mddev->gendisk);
+       }
+       if (mddev->queue)
+               blk_cleanup_queue(mddev->queue);
+
        kfree(mddev);
 }
 
@@ -3039,56 +3887,152 @@ static struct kobj_type md_ktype = {
 
 int mdp_major = 0;
 
-static struct kobject *md_probe(dev_t dev, int *part, void *data)
+static void mddev_delayed_delete(struct work_struct *ws)
+{
+       mddev_t *mddev = container_of(ws, mddev_t, del_work);
+
+       if (mddev->private == &md_redundancy_group) {
+               sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
+               if (mddev->sysfs_action)
+                       sysfs_put(mddev->sysfs_action);
+               mddev->sysfs_action = NULL;
+               mddev->private = NULL;
+       }
+       kobject_del(&mddev->kobj);
+       kobject_put(&mddev->kobj);
+}
+
+static int md_alloc(dev_t dev, char *name)
 {
        static DEFINE_MUTEX(disks_mutex);
        mddev_t *mddev = mddev_find(dev);
        struct gendisk *disk;
-       int partitioned = (MAJOR(dev) != MD_MAJOR);
-       int shift = partitioned ? MdpMinorShift : 0;
-       int unit = MINOR(dev) >> shift;
+       int partitioned;
+       int shift;
+       int unit;
+       int error;
 
        if (!mddev)
-               return NULL;
+               return -ENODEV;
+
+       partitioned = (MAJOR(mddev->unit) != MD_MAJOR);
+       shift = partitioned ? MdpMinorShift : 0;
+       unit = MINOR(mddev->unit) >> shift;
+
+       /* wait for any previous instance if this device
+        * to be completed removed (mddev_delayed_delete).
+        */
+       flush_scheduled_work();
 
        mutex_lock(&disks_mutex);
-       if (mddev->gendisk) {
-               mutex_unlock(&disks_mutex);
-               mddev_put(mddev);
-               return NULL;
+       error = -EEXIST;
+       if (mddev->gendisk)
+               goto abort;
+
+       if (name) {
+               /* Need to ensure that 'name' is not a duplicate.
+                */
+               mddev_t *mddev2;
+               spin_lock(&all_mddevs_lock);
+
+               list_for_each_entry(mddev2, &all_mddevs, all_mddevs)
+                       if (mddev2->gendisk &&
+                           strcmp(mddev2->gendisk->disk_name, name) == 0) {
+                               spin_unlock(&all_mddevs_lock);
+                               goto abort;
+                       }
+               spin_unlock(&all_mddevs_lock);
        }
+
+       error = -ENOMEM;
+       mddev->queue = blk_alloc_queue(GFP_KERNEL);
+       if (!mddev->queue)
+               goto abort;
+       mddev->queue->queuedata = mddev;
+
+       /* Can be unlocked because the queue is new: no concurrency */
+       queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, mddev->queue);
+
+       blk_queue_make_request(mddev->queue, md_make_request);
+
        disk = alloc_disk(1 << shift);
        if (!disk) {
-               mutex_unlock(&disks_mutex);
-               mddev_put(mddev);
-               return NULL;
+               blk_cleanup_queue(mddev->queue);
+               mddev->queue = NULL;
+               goto abort;
        }
-       disk->major = MAJOR(dev);
+       disk->major = MAJOR(mddev->unit);
        disk->first_minor = unit << shift;
-       if (partitioned)
+       if (name)
+               strcpy(disk->disk_name, name);
+       else if (partitioned)
                sprintf(disk->disk_name, "md_d%d", unit);
        else
                sprintf(disk->disk_name, "md%d", unit);
        disk->fops = &md_fops;
        disk->private_data = mddev;
        disk->queue = mddev->queue;
+       /* Allow extended partitions.  This makes the
+        * 'mdp' device redundant, but we can't really
+        * remove it now.
+        */
+       disk->flags |= GENHD_FL_EXT_DEVT;
        add_disk(disk);
        mddev->gendisk = disk;
+       error = kobject_init_and_add(&mddev->kobj, &md_ktype,
+                                    &disk_to_dev(disk)->kobj, "%s", "md");
+       if (error) {
+               /* This isn't possible, but as kobject_init_and_add is marked
+                * __must_check, we must do something with the result
+                */
+               printk(KERN_WARNING "md: cannot register %s/md - name in use\n",
+                      disk->disk_name);
+               error = 0;
+       }
+ abort:
        mutex_unlock(&disks_mutex);
-       mddev->kobj.parent = &disk->kobj;
-       kobject_set_name(&mddev->kobj, "%s", "md");
-       mddev->kobj.ktype = &md_ktype;
-       if (kobject_register(&mddev->kobj))
-               printk(KERN_WARNING "md: cannot register %s/md - name in use\n",
-                      disk->disk_name);
+       if (!error) {
+               kobject_uevent(&mddev->kobj, KOBJ_ADD);
+               mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state");
+       }
+       mddev_put(mddev);
+       return error;
+}
+
+static struct kobject *md_probe(dev_t dev, int *part, void *data)
+{
+       md_alloc(dev, NULL);
        return NULL;
 }
 
+static int add_named_array(const char *val, struct kernel_param *kp)
+{
+       /* val must be "md_*" where * is not all digits.
+        * We allocate an array with a large free minor number, and
+        * set the name to val.  val must not already be an active name.
+        */
+       int len = strlen(val);
+       char buf[DISK_NAME_LEN];
+
+       while (len && val[len-1] == '\n')
+               len--;
+       if (len >= DISK_NAME_LEN)
+               return -E2BIG;
+       strlcpy(buf, val, len+1);
+       if (strncmp(buf, "md_", 3) != 0)
+               return -EINVAL;
+       return md_alloc(0, buf);
+}
+
 static void md_safemode_timeout(unsigned long data)
 {
        mddev_t *mddev = (mddev_t *) data;
 
-       mddev->safemode = 1;
+       if (!atomic_read(&mddev->writes_pending)) {
+               mddev->safemode = 1;
+               if (mddev->external)
+                       sysfs_notify_dirent(mddev->sysfs_state);
+       }
        md_wakeup_thread(mddev->thread);
 }
 
@@ -3097,12 +4041,9 @@ static int start_dirty_degraded;
 static int do_md_run(mddev_t * mddev)
 {
        int err;
-       int chunk_size;
-       struct list_head *tmp;
        mdk_rdev_t *rdev;
        struct gendisk *disk;
        struct mdk_personality *pers;
-       char b[BDEVNAME_SIZE];
 
        if (list_empty(&mddev->disks))
                /* cannot run an array with no devices.. */
@@ -3114,59 +4055,23 @@ static int do_md_run(mddev_t * mddev)
        /*
         * Analyze all RAID superblock(s)
         */
-       if (!mddev->raid_disks)
-               analyze_sbs(mddev);
-
-       chunk_size = mddev->chunk_size;
-
-       if (chunk_size) {
-               if (chunk_size > MAX_CHUNK_SIZE) {
-                       printk(KERN_ERR "too big chunk_size: %d > %d\n",
-                               chunk_size, MAX_CHUNK_SIZE);
-                       return -EINVAL;
-               }
-               /*
-                * chunk-size has to be a power of 2 and multiples of PAGE_SIZE
-                */
-               if ( (1 << ffz(~chunk_size)) != chunk_size) {
-                       printk(KERN_ERR "chunk_size of %d not valid\n", chunk_size);
-                       return -EINVAL;
-               }
-               if (chunk_size < PAGE_SIZE) {
-                       printk(KERN_ERR "too small chunk_size: %d < %ld\n",
-                               chunk_size, PAGE_SIZE);
+       if (!mddev->raid_disks) {
+               if (!mddev->persistent)
                        return -EINVAL;
-               }
-
-               /* devices must have minimum size of one chunk */
-               ITERATE_RDEV(mddev,rdev,tmp) {
-                       if (test_bit(Faulty, &rdev->flags))
-                               continue;
-                       if (rdev->size < chunk_size / 1024) {
-                               printk(KERN_WARNING
-                                       "md: Dev %s smaller than chunk_size:"
-                                       " %lluk < %dk\n",
-                                       bdevname(rdev->bdev,b),
-                                       (unsigned long long)rdev->size,
-                                       chunk_size / 1024);
-                               return -EINVAL;
-                       }
-               }
+               analyze_sbs(mddev);
        }
 
-#ifdef CONFIG_KMOD
        if (mddev->level != LEVEL_NONE)
                request_module("md-level-%d", mddev->level);
        else if (mddev->clevel[0])
                request_module("md-%s", mddev->clevel);
-#endif
 
        /*
         * Drop all container device buffers, from now on
         * the only valid external interface is through the md
         * device.
         */
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
                if (test_bit(Faulty, &rdev->flags))
                        continue;
                sync_blockdev(rdev->bdev);
@@ -3174,24 +4079,25 @@ static int do_md_run(mddev_t * mddev)
 
                /* perform some consistency tests on the device.
                 * We don't want the data to overlap the metadata,
-                * Internal Bitmap issues has handled elsewhere.
+                * Internal Bitmap issues have been handled elsewhere.
                 */
-               if (rdev->data_offset < rdev->sb_offset) {
-                       if (mddev->size &&
-                           rdev->data_offset + mddev->size*2
-                           > rdev->sb_offset*2) {
+               if (rdev->data_offset < rdev->sb_start) {
+                       if (mddev->dev_sectors &&
+                           rdev->data_offset + mddev->dev_sectors
+                           > rdev->sb_start) {
                                printk("md: %s: data overlaps metadata\n",
                                       mdname(mddev));
                                return -EINVAL;
                        }
                } else {
-                       if (rdev->sb_offset*2 + rdev->sb_size/512
+                       if (rdev->sb_start + rdev->sb_size/512
                            > rdev->data_offset) {
                                printk("md: %s: metadata overlaps data\n",
                                       mdname(mddev));
                                return -EINVAL;
                        }
                }
+               sysfs_notify_dirent(rdev->sysfs_state);
        }
 
        md_probe(mddev->unit, NULL, NULL);
@@ -3213,7 +4119,10 @@ static int do_md_run(mddev_t * mddev)
        }
        mddev->pers = pers;
        spin_unlock(&pers_lock);
-       mddev->level = pers->level;
+       if (mddev->level != pers->level) {
+               mddev->level = pers->level;
+               mddev->new_level = pers->level;
+       }
        strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
 
        if (mddev->reshape_position != MaxSector &&
@@ -3230,10 +4139,10 @@ static int do_md_run(mddev_t * mddev)
                 */
                char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
                mdk_rdev_t *rdev2;
-               struct list_head *tmp2;
                int warned = 0;
-               ITERATE_RDEV(mddev, rdev, tmp) {
-                       ITERATE_RDEV(mddev, rdev2, tmp2) {
+
+               list_for_each_entry(rdev, &mddev->disks, same_set)
+                       list_for_each_entry(rdev2, &mddev->disks, same_set) {
                                if (rdev < rdev2 &&
                                    rdev->bdev->bd_contains ==
                                    rdev2->bdev->bd_contains) {
@@ -3247,7 +4156,7 @@ static int do_md_run(mddev_t * mddev)
                                        warned = 1;
                                }
                        }
-               }
+
                if (warned)
                        printk(KERN_WARNING
                               "True protection against single-disk"
@@ -3255,7 +4164,9 @@ static int do_md_run(mddev_t * mddev)
        }
 
        mddev->recovery = 0;
-       mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */
+       /* may be over-ridden by personality */
+       mddev->resync_max_sectors = mddev->dev_sectors;
+
        mddev->barriers_work = 1;
        mddev->ok_start_degraded = start_dirty_degraded;
 
@@ -3263,7 +4174,19 @@ static int do_md_run(mddev_t * mddev)
                mddev->ro = 2; /* read-only, but switch on first write */
 
        err = mddev->pers->run(mddev);
-       if (!err && mddev->pers->sync_request) {
+       if (err)
+               printk(KERN_ERR "md: pers->run() failed ...\n");
+       else if (mddev->pers->size(mddev, 0, 0) < mddev->array_sectors) {
+               WARN_ONCE(!mddev->external_size, "%s: default size too small,"
+                         " but 'external_size' not in effect?\n", __func__);
+               printk(KERN_ERR
+                      "md: invalid array_size %llu > default size %llu\n",
+                      (unsigned long long)mddev->array_sectors / 2,
+                      (unsigned long long)mddev->pers->size(mddev, 0, 0) / 2);
+               err = -EINVAL;
+               mddev->pers->stop(mddev);
+       }
+       if (err == 0 && mddev->pers->sync_request) {
                err = bitmap_create(mddev);
                if (err) {
                        printk(KERN_ERR "%s: failed to create bitmap (%d)\n",
@@ -3272,7 +4195,6 @@ static int do_md_run(mddev_t * mddev)
                }
        }
        if (err) {
-               printk(KERN_ERR "md: pers->run() failed ...\n");
                module_put(mddev->pers->owner);
                mddev->pers = NULL;
                bitmap_destroy(mddev);
@@ -3283,6 +4205,7 @@ static int do_md_run(mddev_t * mddev)
                        printk(KERN_WARNING
                               "md: cannot register extra attributes for %s\n",
                               mdname(mddev));
+               mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, "sync_action");
        } else if (mddev->ro == 2) /* auto-readonly not meaningful */
                mddev->ro = 0;
 
@@ -3293,7 +4216,7 @@ static int do_md_run(mddev_t * mddev)
        mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */
        mddev->in_sync = 1;
 
-       ITERATE_RDEV(mddev,rdev,tmp)
+       list_for_each_entry(rdev, &mddev->disks, same_set)
                if (rdev->raid_disk >= 0) {
                        char nm[20];
                        sprintf(nm, "rd%d", rdev->raid_disk);
@@ -3307,26 +4230,15 @@ static int do_md_run(mddev_t * mddev)
        if (mddev->flags)
                md_update_sb(mddev, 0);
 
-       set_capacity(disk, mddev->array_size<<1);
-
-       /* If we call blk_queue_make_request here, it will
-        * re-initialise max_sectors etc which may have been
-        * refined inside -> run.  So just set the bits we need to set.
-        * Most initialisation happended when we called
-        * blk_queue_make_request(..., md_fail_request)
-        * earlier.
-        */
-       mddev->queue->queuedata = mddev;
-       mddev->queue->make_request_fn = mddev->pers->make_request;
+       set_capacity(disk, mddev->array_sectors);
 
        /* If there is a partially-recovered drive we need to
         * start recovery here.  If we leave it to md_check_recovery,
         * it will remove the drives and not do the right thing
         */
        if (mddev->degraded && !mddev->sync_thread) {
-               struct list_head *rtmp;
                int spares = 0;
-               ITERATE_RDEV(mddev,rdev,rtmp)
+               list_for_each_entry(rdev, &mddev->disks, same_set)
                        if (rdev->raid_disk >= 0 &&
                            !test_bit(In_sync, &rdev->flags) &&
                            !test_bit(Faulty, &rdev->flags))
@@ -3337,7 +4249,7 @@ static int do_md_run(mddev_t * mddev)
                        set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
                        mddev->sync_thread = md_register_thread(md_do_sync,
                                                                mddev,
-                                                               "%s_resync");
+                                                               "resync");
                        if (!mddev->sync_thread) {
                                printk(KERN_ERR "%s: could not start resync"
                                       " thread...\n",
@@ -3350,47 +4262,39 @@ static int do_md_run(mddev_t * mddev)
        md_wakeup_thread(mddev->thread);
        md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
 
+       revalidate_disk(mddev->gendisk);
        mddev->changed = 1;
        md_new_event(mddev);
-       kobject_uevent(&mddev->gendisk->kobj, KOBJ_CHANGE);
+       sysfs_notify_dirent(mddev->sysfs_state);
+       if (mddev->sysfs_action)
+               sysfs_notify_dirent(mddev->sysfs_action);
+       sysfs_notify(&mddev->kobj, NULL, "degraded");
+       kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
        return 0;
 }
 
 static int restart_array(mddev_t *mddev)
 {
        struct gendisk *disk = mddev->gendisk;
-       int err;
 
-       /*
-        * Complain if it has no devices
-        */
-       err = -ENXIO;
+       /* Complain if it has no devices */
        if (list_empty(&mddev->disks))
-               goto out;
-
-       if (mddev->pers) {
-               err = -EBUSY;
-               if (!mddev->ro)
-                       goto out;
-
-               mddev->safemode = 0;
-               mddev->ro = 0;
-               set_disk_ro(disk, 0);
-
-               printk(KERN_INFO "md: %s switched to read-write mode.\n",
-                       mdname(mddev));
-               /*
-                * Kick recovery or resync if necessary
-                */
-               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
-               md_wakeup_thread(mddev->thread);
-               md_wakeup_thread(mddev->sync_thread);
-               err = 0;
-       } else
-               err = -EINVAL;
-
-out:
-       return err;
+               return -ENXIO;
+       if (!mddev->pers)
+               return -EINVAL;
+       if (!mddev->ro)
+               return -EBUSY;
+       mddev->safemode = 0;
+       mddev->ro = 0;
+       set_disk_ro(disk, 0);
+       printk(KERN_INFO "md: %s switched to read-write mode.\n",
+               mdname(mddev));
+       /* Kick recovery or resync if necessary */
+       set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+       md_wakeup_thread(mddev->thread);
+       md_wakeup_thread(mddev->sync_thread);
+       sysfs_notify_dirent(mddev->sysfs_state);
+       return 0;
 }
 
 /* similar to deny_write_access, but accounts for our holding a reference
@@ -3424,16 +4328,17 @@ static void restore_bitmap_write_access(struct file *file)
  *   1 - switch to readonly
  *   2 - stop but do not disassemble array
  */
-static int do_md_stop(mddev_t * mddev, int mode)
+static int do_md_stop(mddev_t * mddev, int mode, int is_open)
 {
        int err = 0;
        struct gendisk *disk = mddev->gendisk;
+       mdk_rdev_t *rdev;
 
-       if (mddev->pers) {
-               if (atomic_read(&mddev->active)>2) {
-                       printk("md: %s still in use.\n",mdname(mddev));
-                       return -EBUSY;
-               }
+       mutex_lock(&mddev->open_mutex);
+       if (atomic_read(&mddev->openers) > is_open) {
+               printk("md: %s still in use.\n",mdname(mddev));
+               err = -EBUSY;
+       } else if (mddev->pers) {
 
                if (mddev->sync_thread) {
                        set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
@@ -3444,8 +4349,6 @@ static int do_md_stop(mddev_t * mddev, int mode)
 
                del_timer_sync(&mddev->safemode_timer);
 
-               invalidate_partition(disk, 0);
-
                switch(mode) {
                case 1: /* readonly */
                        err  = -ENXIO;
@@ -3459,16 +4362,24 @@ static int do_md_stop(mddev_t * mddev, int mode)
                        md_super_wait(mddev);
                        if (mddev->ro)
                                set_disk_ro(disk, 0);
-                       blk_queue_make_request(mddev->queue, md_fail_request);
+
                        mddev->pers->stop(mddev);
                        mddev->queue->merge_bvec_fn = NULL;
                        mddev->queue->unplug_fn = NULL;
                        mddev->queue->backing_dev_info.congested_fn = NULL;
-                       if (mddev->pers->sync_request)
-                               sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
-
                        module_put(mddev->pers->owner);
+                       if (mddev->pers->sync_request)
+                               mddev->private = &md_redundancy_group;
                        mddev->pers = NULL;
+                       /* tell userspace to handle 'inactive' */
+                       sysfs_notify_dirent(mddev->sysfs_state);
+
+                       list_for_each_entry(rdev, &mddev->disks, same_set)
+                               if (rdev->raid_disk >= 0) {
+                                       char nm[20];
+                                       sprintf(nm, "rd%d", rdev->raid_disk);
+                                       sysfs_remove_link(&mddev->kobj, nm);
+                               }
 
                        set_capacity(disk, 0);
                        mddev->changed = 1;
@@ -3484,14 +4395,16 @@ static int do_md_stop(mddev_t * mddev, int mode)
                if (mode == 1)
                        set_disk_ro(disk, 1);
                clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+               err = 0;
        }
-
+out:
+       mutex_unlock(&mddev->open_mutex);
+       if (err)
+               return err;
        /*
         * Free resources if final stop
         */
        if (mode == 0) {
-               mdk_rdev_t *rdev;
-               struct list_head *tmp;
 
                printk(KERN_INFO "md: %s stopped.\n", mdname(mddev));
 
@@ -3503,30 +4416,56 @@ static int do_md_stop(mddev_t * mddev, int mode)
                }
                mddev->bitmap_offset = 0;
 
-               ITERATE_RDEV(mddev,rdev,tmp)
-                       if (rdev->raid_disk >= 0) {
-                               char nm[20];
-                               sprintf(nm, "rd%d", rdev->raid_disk);
-                               sysfs_remove_link(&mddev->kobj, nm);
-                       }
-
-               /* make sure all delayed_delete calls have finished */
+               /* make sure all md_delayed_delete calls have finished */
                flush_scheduled_work();
 
                export_array(mddev);
 
-               mddev->array_size = 0;
-               mddev->size = 0;
+               mddev->array_sectors = 0;
+               mddev->external_size = 0;
+               mddev->dev_sectors = 0;
                mddev->raid_disks = 0;
                mddev->recovery_cp = 0;
+               mddev->resync_min = 0;
+               mddev->resync_max = MaxSector;
                mddev->reshape_position = MaxSector;
+               mddev->external = 0;
+               mddev->persistent = 0;
+               mddev->level = LEVEL_NONE;
+               mddev->clevel[0] = 0;
+               mddev->flags = 0;
+               mddev->ro = 0;
+               mddev->metadata_type[0] = 0;
+               mddev->chunk_sectors = 0;
+               mddev->ctime = mddev->utime = 0;
+               mddev->layout = 0;
+               mddev->max_disks = 0;
+               mddev->events = 0;
+               mddev->delta_disks = 0;
+               mddev->new_level = LEVEL_NONE;
+               mddev->new_layout = 0;
+               mddev->new_chunk_sectors = 0;
+               mddev->curr_resync = 0;
+               mddev->resync_mismatches = 0;
+               mddev->suspend_lo = mddev->suspend_hi = 0;
+               mddev->sync_speed_min = mddev->sync_speed_max = 0;
+               mddev->recovery = 0;
+               mddev->in_sync = 0;
+               mddev->changed = 0;
+               mddev->degraded = 0;
+               mddev->barriers_work = 0;
+               mddev->safemode = 0;
+               kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
+               if (mddev->hold_active == UNTIL_STOP)
+                       mddev->hold_active = 0;
 
        } else if (mddev->pers)
                printk(KERN_INFO "md: %s switched to read-only mode.\n",
                        mdname(mddev));
        err = 0;
+       blk_integrity_unregister(disk);
        md_new_event(mddev);
-out:
+       sysfs_notify_dirent(mddev->sysfs_state);
        return err;
 }
 
@@ -3534,7 +4473,6 @@ out:
 static void autorun_array(mddev_t *mddev)
 {
        mdk_rdev_t *rdev;
-       struct list_head *tmp;
        int err;
 
        if (list_empty(&mddev->disks))
@@ -3542,16 +4480,16 @@ static void autorun_array(mddev_t *mddev)
 
        printk(KERN_INFO "md: running: ");
 
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
                char b[BDEVNAME_SIZE];
                printk("<%s>", bdevname(rdev->bdev,b));
        }
        printk("\n");
 
-       err = do_md_run (mddev);
+       err = do_md_run(mddev);
        if (err) {
                printk(KERN_WARNING "md: do_md_run() returned %d\n", err);
-               do_md_stop (mddev, 0);
+               do_md_stop(mddev, 0, 0);
        }
 }
 
@@ -3569,8 +4507,7 @@ static void autorun_array(mddev_t *mddev)
  */
 static void autorun_devices(int part)
 {
-       struct list_head *tmp;
-       mdk_rdev_t *rdev0, *rdev;
+       mdk_rdev_t *rdev0, *rdev, *tmp;
        mddev_t *mddev;
        char b[BDEVNAME_SIZE];
 
@@ -3585,7 +4522,7 @@ static void autorun_devices(int part)
                printk(KERN_INFO "md: considering %s ...\n",
                        bdevname(rdev0->bdev,b));
                INIT_LIST_HEAD(&candidates);
-               ITERATE_RDEV_PENDING(rdev,tmp)
+               rdev_for_each_list(rdev, tmp, &pending_raid_disks)
                        if (super_90_load(rdev, rdev0, 0) >= 0) {
                                printk(KERN_INFO "md:  adding %s ...\n",
                                        bdevname(rdev->bdev,b));
@@ -3612,8 +4549,10 @@ static void autorun_devices(int part)
 
                md_probe(dev, NULL, NULL);
                mddev = mddev_find(dev);
-               if (!mddev) {
-                       printk(KERN_ERR 
+               if (!mddev || !mddev->gendisk) {
+                       if (mddev)
+                               mddev_put(mddev);
+                       printk(KERN_ERR
                                "md: cannot allocate memory for md drive.\n");
                        break;
                }
@@ -3628,7 +4567,8 @@ static void autorun_devices(int part)
                        mddev_unlock(mddev);
                } else {
                        printk(KERN_INFO "md: created %s\n", mdname(mddev));
-                       ITERATE_RDEV_GENERIC(candidates,rdev,tmp) {
+                       mddev->persistent = 1;
+                       rdev_for_each_list(rdev, tmp, &candidates) {
                                list_del_init(&rdev->same_set);
                                if (bind_rdev_to_array(rdev, mddev))
                                        export_rdev(rdev);
@@ -3639,8 +4579,10 @@ static void autorun_devices(int part)
                /* on success, candidates will be empty, on error
                 * it won't...
                 */
-               ITERATE_RDEV_GENERIC(candidates,rdev,tmp)
+               rdev_for_each_list(rdev, tmp, &candidates) {
+                       list_del_init(&rdev->same_set);
                        export_rdev(rdev);
+               }
                mddev_put(mddev);
        }
        printk(KERN_INFO "md: ... autorun DONE.\n");
@@ -3664,19 +4606,18 @@ static int get_version(void __user * arg)
 static int get_array_info(mddev_t * mddev, void __user * arg)
 {
        mdu_array_info_t info;
-       int nr,working,active,failed,spare;
+       int nr,working,insync,failed,spare;
        mdk_rdev_t *rdev;
-       struct list_head *tmp;
 
-       nr=working=active=failed=spare=0;
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       nr=working=insync=failed=spare=0;
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
                nr++;
                if (test_bit(Faulty, &rdev->flags))
                        failed++;
                else {
                        working++;
                        if (test_bit(In_sync, &rdev->flags))
-                               active++;       
+                               insync++;       
                        else
                                spare++;
                }
@@ -3687,8 +4628,8 @@ static int get_array_info(mddev_t * mddev, void __user * arg)
        info.patch_version = MD_PATCHLEVEL_VERSION;
        info.ctime         = mddev->ctime;
        info.level         = mddev->level;
-       info.size          = mddev->size;
-       if (info.size != mddev->size) /* overflow */
+       info.size          = mddev->dev_sectors / 2;
+       if (info.size != mddev->dev_sectors / 2) /* overflow */
                info.size = -1;
        info.nr_disks      = nr;
        info.raid_disks    = mddev->raid_disks;
@@ -3701,13 +4642,13 @@ static int get_array_info(mddev_t * mddev, void __user * arg)
                info.state = (1<<MD_SB_CLEAN);
        if (mddev->bitmap && mddev->bitmap_offset)
                info.state = (1<<MD_SB_BITMAP_PRESENT);
-       info.active_disks  = active;
+       info.active_disks  = insync;
        info.working_disks = working;
        info.failed_disks  = failed;
        info.spare_disks   = spare;
 
        info.layout        = mddev->layout;
-       info.chunk_size    = mddev->chunk_size;
+       info.chunk_size    = mddev->chunk_sectors << 9;
 
        if (copy_to_user(arg, &info, sizeof(info)))
                return -EFAULT;
@@ -3721,9 +4662,11 @@ static int get_bitmap_file(mddev_t * mddev, void __user * arg)
        char *ptr, *buf = NULL;
        int err = -ENOMEM;
 
-       md_allow_write(mddev);
+       if (md_allow_write(mddev))
+               file = kmalloc(sizeof(*file), GFP_NOIO);
+       else
+               file = kmalloc(sizeof(*file), GFP_KERNEL);
 
-       file = kmalloc(sizeof(*file), GFP_KERNEL);
        if (!file)
                goto out;
 
@@ -3737,8 +4680,8 @@ static int get_bitmap_file(mddev_t * mddev, void __user * arg)
        if (!buf)
                goto out;
 
-       ptr = file_path(mddev->bitmap->file, buf, sizeof(file->pathname));
-       if (!ptr)
+       ptr = d_path(&mddev->bitmap->file->f_path, buf, sizeof(file->pathname));
+       if (IS_ERR(ptr))
                goto out;
 
        strcpy(file->pathname, ptr);
@@ -3756,15 +4699,12 @@ out:
 static int get_disk_info(mddev_t * mddev, void __user * arg)
 {
        mdu_disk_info_t info;
-       unsigned int nr;
        mdk_rdev_t *rdev;
 
        if (copy_from_user(&info, arg, sizeof(info)))
                return -EFAULT;
 
-       nr = info.number;
-
-       rdev = find_rdev_nr(mddev, nr);
+       rdev = find_rdev_nr(mddev, info.number);
        if (rdev) {
                info.major = MAJOR(rdev->bdev->bd_dev);
                info.minor = MINOR(rdev->bdev->bd_dev);
@@ -3812,7 +4752,7 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
                if (!list_empty(&mddev->disks)) {
                        mdk_rdev_t *rdev0 = list_entry(mddev->disks.next,
                                                        mdk_rdev_t, same_set);
-                       int err = super_types[mddev->major_version]
+                       err = super_types[mddev->major_version]
                                .load_super(rdev, rdev0, mddev->minor_version);
                        if (err < 0) {
                                printk(KERN_WARNING 
@@ -3868,6 +4808,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
                clear_bit(In_sync, &rdev->flags); /* just to be sure */
                if (info->state & (1<<MD_DISK_WRITEMOSTLY))
                        set_bit(WriteMostly, &rdev->flags);
+               else
+                       clear_bit(WriteMostly, &rdev->flags);
 
                rdev->raid_disk = -1;
                err = bind_rdev_to_array(rdev, mddev);
@@ -3884,8 +4826,12 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
                }
                if (err)
                        export_rdev(rdev);
+               else
+                       sysfs_notify_dirent(rdev->sysfs_state);
 
                md_update_sb(mddev, 1);
+               if (mddev->degraded)
+                       set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
                set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
                md_wakeup_thread(mddev->thread);
                return err;
@@ -3902,7 +4848,7 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
 
        if (!(info->state & (1<<MD_DISK_FAULTY))) {
                int err;
-               rdev = md_import_device (dev, -1, 0);
+               rdev = md_import_device(dev, -1, 0);
                if (IS_ERR(rdev)) {
                        printk(KERN_WARNING 
                                "md: error, md_import_device() returned %ld\n",
@@ -3915,8 +4861,6 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
                else
                        rdev->raid_disk = -1;
 
-               rdev->flags = 0;
-
                if (rdev->raid_disk < mddev->raid_disks)
                        if (info->state & (1<<MD_DISK_SYNC))
                                set_bit(In_sync, &rdev->flags);
@@ -3926,10 +4870,10 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
 
                if (!mddev->persistent) {
                        printk(KERN_INFO "md: nonpersistent superblock ...\n");
-                       rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
+                       rdev->sb_start = rdev->bdev->bd_inode->i_size / 512;
                } else 
-                       rdev->sb_offset = calc_dev_sboffset(rdev->bdev);
-               rdev->size = calc_dev_size(rdev, mddev->chunk_size);
+                       rdev->sb_start = calc_dev_sboffset(rdev->bdev);
+               rdev->sectors = rdev->sb_start;
 
                err = bind_rdev_to_array(rdev, mddev);
                if (err) {
@@ -3946,9 +4890,6 @@ static int hot_remove_disk(mddev_t * mddev, dev_t dev)
        char b[BDEVNAME_SIZE];
        mdk_rdev_t *rdev;
 
-       if (!mddev->pers)
-               return -ENODEV;
-
        rdev = find_rdev(mddev, dev);
        if (!rdev)
                return -ENXIO;
@@ -3962,7 +4903,7 @@ static int hot_remove_disk(mddev_t * mddev, dev_t dev)
 
        return 0;
 busy:
-       printk(KERN_WARNING "md: cannot remove active disk %s from %s ... \n",
+       printk(KERN_WARNING "md: cannot remove active disk %s from %s ...\n",
                bdevname(rdev->bdev,b), mdname(mddev));
        return -EBUSY;
 }
@@ -3971,7 +4912,6 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
 {
        char b[BDEVNAME_SIZE];
        int err;
-       unsigned int size;
        mdk_rdev_t *rdev;
 
        if (!mddev->pers)
@@ -3990,7 +4930,7 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
                return -EINVAL;
        }
 
-       rdev = md_import_device (dev, -1, 0);
+       rdev = md_import_device(dev, -1, 0);
        if (IS_ERR(rdev)) {
                printk(KERN_WARNING 
                        "md: error, md_import_device() returned %ld\n",
@@ -3999,13 +4939,11 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
        }
 
        if (mddev->persistent)
-               rdev->sb_offset = calc_dev_sboffset(rdev->bdev);
+               rdev->sb_start = calc_dev_sboffset(rdev->bdev);
        else
-               rdev->sb_offset =
-                       rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
+               rdev->sb_start = rdev->bdev->bd_inode->i_size / 512;
 
-       size = calc_dev_size(rdev, mddev->chunk_size);
-       rdev->size = size;
+       rdev->sectors = rdev->sb_start;
 
        if (test_bit(Faulty, &rdev->flags)) {
                printk(KERN_WARNING 
@@ -4026,13 +4964,6 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
         * noticed in interrupt contexts ...
         */
 
-       if (rdev->desc_nr == mddev->max_disks) {
-               printk(KERN_WARNING "%s: can not hot-add to full array!\n",
-                       mdname(mddev));
-               err = -EBUSY;
-               goto abort_unbind_export;
-       }
-
        rdev->raid_disk = -1;
 
        md_update_sb(mddev, 1);
@@ -4046,9 +4977,6 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
        md_new_event(mddev);
        return 0;
 
-abort_unbind_export:
-       unbind_rdev_from_array(rdev);
-
 abort_export:
        export_rdev(rdev);
        return err;
@@ -4151,7 +5079,7 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
 
        mddev->level         = info->level;
        mddev->clevel[0]     = 0;
-       mddev->size          = info->size;
+       mddev->dev_sectors   = 2 * (sector_t)info->size;
        mddev->raid_disks    = info->raid_disks;
        /* don't set md_minor, it is determined by which /dev/md* was
         * openned
@@ -4161,13 +5089,15 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
        else
                mddev->recovery_cp = 0;
        mddev->persistent    = ! info->not_persistent;
+       mddev->external      = 0;
 
        mddev->layout        = info->layout;
-       mddev->chunk_size    = info->chunk_size;
+       mddev->chunk_sectors = info->chunk_size >> 9;
 
        mddev->max_disks     = MD_SB_DISKS;
 
-       mddev->flags         = 0;
+       if (mddev->persistent)
+               mddev->flags         = 0;
        set_bit(MD_CHANGE_DEVS, &mddev->flags);
 
        mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
@@ -4181,55 +5111,60 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
        get_random_bytes(mddev->uuid, 16);
 
        mddev->new_level = mddev->level;
-       mddev->new_chunk = mddev->chunk_size;
+       mddev->new_chunk_sectors = mddev->chunk_sectors;
        mddev->new_layout = mddev->layout;
        mddev->delta_disks = 0;
 
        return 0;
 }
 
-static int update_size(mddev_t *mddev, unsigned long size)
+void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors)
 {
-       mdk_rdev_t * rdev;
+       WARN(!mddev_is_locked(mddev), "%s: unlocked mddev!\n", __func__);
+
+       if (mddev->external_size)
+               return;
+
+       mddev->array_sectors = array_sectors;
+}
+EXPORT_SYMBOL(md_set_array_sectors);
+
+static int update_size(mddev_t *mddev, sector_t num_sectors)
+{
+       mdk_rdev_t *rdev;
        int rv;
-       struct list_head *tmp;
-       int fit = (size == 0);
+       int fit = (num_sectors == 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.
+       /* The "num_sectors" is the number of sectors 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 this number if no resync or reconstruction is
+        * happening, and if the new size is acceptable. It must fit before the
+        * sb_start or, if that is <data_offset, it must fit before the size
+        * of each device.  If num_sectors is zero, we find the largest size
+        * that fits.
+
         */
        if (mddev->sync_thread)
                return -EBUSY;
-       ITERATE_RDEV(mddev,rdev,tmp) {
-               sector_t avail;
-               avail = rdev->size * 2;
+       if (mddev->bitmap)
+               /* Sorry, cannot grow a bitmap yet, just remove it,
+                * grow, and re-add.
+                */
+               return -EBUSY;
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
+               sector_t avail = rdev->sectors;
 
-               if (fit && (size == 0 || size > avail/2))
-                       size = avail/2;
-               if (avail < ((sector_t)size << 1))
+               if (fit && (num_sectors == 0 || num_sectors > avail))
+                       num_sectors = avail;
+               if (avail < num_sectors)
                        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);
-               }
-       }
+       rv = mddev->pers->resize(mddev, num_sectors);
+       if (!rv)
+               revalidate_disk(mddev->gendisk);
        return rv;
 }
 
@@ -4276,31 +5211,42 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
            mddev->level         != info->level         ||
 /*         mddev->layout        != info->layout        || */
            !mddev->persistent   != info->not_persistent||
-           mddev->chunk_size    != info->chunk_size    ||
+           mddev->chunk_sectors != info->chunk_size >> 9 ||
            /* ignore bottom 8 bits of state, and allow SB_BITMAP_PRESENT to change */
            ((state^info->state) & 0xfffffe00)
                )
                return -EINVAL;
        /* Check there is only one change */
-       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++;
-       if (cnt == 0) return 0;
-       if (cnt > 1) return -EINVAL;
+       if (info->size >= 0 && mddev->dev_sectors / 2 != 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++;
+       if (cnt == 0)
+               return 0;
+       if (cnt > 1)
+               return -EINVAL;
 
        if (mddev->layout != info->layout) {
                /* Change layout
                 * we don't need to do anything at the md level, the
                 * personality will take care of it all.
                 */
-               if (mddev->pers->reconfig == NULL)
+               if (mddev->pers->check_reshape == NULL)
                        return -EINVAL;
-               else
-                       return mddev->pers->reconfig(mddev, info->layout, -1);
+               else {
+                       mddev->new_layout = info->layout;
+                       rv = mddev->pers->check_reshape(mddev);
+                       if (rv)
+                               mddev->new_layout = mddev->layout;
+                       return rv;
+               }
        }
-       if (info->size >= 0 && mddev->size != info->size)
-               rv = update_size(mddev, info->size);
+       if (info->size >= 0 && mddev->dev_sectors / 2 != info->size)
+               rv = update_size(mddev, (sector_t)info->size * 2);
 
        if (mddev->raid_disks    != info->raid_disks)
                rv = update_raid_disks(mddev, info->raid_disks);
@@ -4353,6 +5299,12 @@ static int set_disk_faulty(mddev_t *mddev, dev_t dev)
        return 0;
 }
 
+/*
+ * We have a problem here : there is no easy way to give a CHS
+ * virtual geometry. We currently pretend that we have a 2 heads
+ * 4 sectors (with a BIG number of cylinders...). This drives
+ * dosfs just mad... ;-)
+ */
 static int md_getgeo(struct block_device *bdev, struct hd_geometry *geo)
 {
        mddev_t *mddev = bdev->bd_disk->private_data;
@@ -4363,7 +5315,7 @@ static int md_getgeo(struct block_device *bdev, struct hd_geometry *geo)
        return 0;
 }
 
-static int md_ioctl(struct inode *inode, struct file *file,
+static int md_ioctl(struct block_device *bdev, fmode_t mode,
                        unsigned int cmd, unsigned long arg)
 {
        int err = 0;
@@ -4401,7 +5353,7 @@ static int md_ioctl(struct inode *inode, struct file *file,
         * Commands creating/starting a new array:
         */
 
-       mddev = inode->i_bdev->bd_disk->private_data;
+       mddev = bdev->bd_disk->private_data;
 
        if (!mddev) {
                BUG();
@@ -4467,9 +5419,10 @@ static int md_ioctl(struct inode *inode, struct file *file,
         */
        /* if we are not initialised yet, only ADD_NEW_DISK, STOP_ARRAY,
         * RUN_ARRAY, and GET_ and SET_BITMAP_FILE are allowed */
-       if (!mddev->raid_disks && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY
-                       && cmd != RUN_ARRAY && cmd != SET_BITMAP_FILE
-                       && cmd != GET_BITMAP_FILE) {
+       if ((!mddev->raid_disks && !mddev->external)
+           && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY
+           && cmd != RUN_ARRAY && cmd != SET_BITMAP_FILE
+           && cmd != GET_BITMAP_FILE) {
                err = -ENODEV;
                goto abort_unlock;
        }
@@ -4496,19 +5449,13 @@ static int md_ioctl(struct inode *inode, struct file *file,
                        goto done_unlock;
 
                case STOP_ARRAY:
-                       err = do_md_stop (mddev, 0);
+                       err = do_md_stop(mddev, 0, 1);
                        goto done_unlock;
 
                case STOP_ARRAY_RO:
-                       err = do_md_stop (mddev, 1);
+                       err = do_md_stop(mddev, 1, 1);
                        goto done_unlock;
 
-       /*
-        * We have a problem here : there is no easy way to give a CHS
-        * virtual geometry. We currently pretend that we have a 2 heads
-        * 4 sectors (with a BIG number of cylinders...). This drives
-        * dosfs just mad... ;-)
-        */
        }
 
        /*
@@ -4518,13 +5465,12 @@ static int md_ioctl(struct inode *inode, struct file *file,
         * here and hit the 'default' below, so only disallow
         * 'md' ioctls, and switch to rw mode if started auto-readonly.
         */
-       if (_IOC_TYPE(cmd) == MD_MAJOR &&
-           mddev->ro && mddev->pers) {
+       if (_IOC_TYPE(cmd) == MD_MAJOR && mddev->ro && mddev->pers) {
                if (mddev->ro == 2) {
                        mddev->ro = 0;
-               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
-               md_wakeup_thread(mddev->thread);
-
+                       sysfs_notify_dirent(mddev->sysfs_state);
+                       set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+                       md_wakeup_thread(mddev->thread);
                } else {
                        err = -EROFS;
                        goto abort_unlock;
@@ -4556,7 +5502,7 @@ static int md_ioctl(struct inode *inode, struct file *file,
                        goto done_unlock;
 
                case RUN_ARRAY:
-                       err = do_md_run (mddev);
+                       err = do_md_run(mddev);
                        goto done_unlock;
 
                case SET_BITMAP_FILE:
@@ -4570,6 +5516,9 @@ static int md_ioctl(struct inode *inode, struct file *file,
 
 done_unlock:
 abort_unlock:
+       if (mddev->hold_active == UNTIL_IOCTL &&
+           err != -EINVAL)
+               mddev->hold_active = 0;
        mddev_unlock(mddev);
 
        return err;
@@ -4580,32 +5529,45 @@ abort:
        return err;
 }
 
-static int md_open(struct inode *inode, struct file *file)
+static int md_open(struct block_device *bdev, fmode_t mode)
 {
        /*
         * Succeed if we can lock the mddev, which confirms that
         * it isn't being stopped right now.
         */
-       mddev_t *mddev = inode->i_bdev->bd_disk->private_data;
+       mddev_t *mddev = mddev_find(bdev->bd_dev);
        int err;
 
-       if ((err = mutex_lock_interruptible_nested(&mddev->reconfig_mutex, 1)))
+       if (mddev->gendisk != bdev->bd_disk) {
+               /* we are racing with mddev_put which is discarding this
+                * bd_disk.
+                */
+               mddev_put(mddev);
+               /* Wait until bdev->bd_disk is definitely gone */
+               flush_scheduled_work();
+               /* Then retry the open from the top */
+               return -ERESTARTSYS;
+       }
+       BUG_ON(mddev != bdev->bd_disk->private_data);
+
+       if ((err = mutex_lock_interruptible(&mddev->open_mutex)))
                goto out;
 
        err = 0;
-       mddev_get(mddev);
-       mddev_unlock(mddev);
+       atomic_inc(&mddev->openers);
+       mutex_unlock(&mddev->open_mutex);
 
-       check_disk_change(inode->i_bdev);
+       check_disk_change(bdev);
  out:
        return err;
 }
 
-static int md_release(struct inode *inode, struct file * file)
+static int md_release(struct gendisk *disk, fmode_t mode)
 {
-       mddev_t *mddev = inode->i_bdev->bd_disk->private_data;
+       mddev_t *mddev = disk->private_data;
 
        BUG_ON(!mddev);
+       atomic_dec(&mddev->openers);
        mddev_put(mddev);
 
        return 0;
@@ -4625,7 +5587,7 @@ static int md_revalidate(struct gendisk *disk)
        mddev->changed = 0;
        return 0;
 }
-static struct block_device_operations md_fops =
+static const struct block_device_operations md_fops =
 {
        .owner          = THIS_MODULE,
        .open           = md_open,
@@ -4700,7 +5662,10 @@ mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev,
        thread->run = run;
        thread->mddev = mddev;
        thread->timeout = MAX_SCHEDULE_TIMEOUT;
-       thread->tsk = kthread_run(md_thread, thread, name, mdname(thread->mddev));
+       thread->tsk = kthread_run(md_thread, thread,
+                                 "%s_%s",
+                                 mdname(thread->mddev),
+                                 name ?: mddev->pers->name);
        if (IS_ERR(thread->tsk)) {
                kfree(thread);
                return NULL;
@@ -4710,7 +5675,9 @@ mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev,
 
 void md_unregister_thread(mdk_thread_t *thread)
 {
-       dprintk("interrupting MD-thread pid %d\n", thread->tsk->pid);
+       if (!thread)
+               return;
+       dprintk("interrupting MD-thread pid %d\n", task_pid_nr(thread->tsk));
 
        kthread_stop(thread->tsk);
        kfree(thread);
@@ -4725,6 +5692,9 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
 
        if (!rdev || test_bit(Faulty, &rdev->flags))
                return;
+
+       if (mddev->external)
+               set_bit(Blocked, &rdev->flags);
 /*
        dprintk("md_error dev:%s, rdev:(%d:%d), (caller: %p,%p,%p,%p).\n",
                mdname(mddev),
@@ -4737,6 +5707,9 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
        if (!mddev->pers->error_handler)
                return;
        mddev->pers->error_handler(mddev,rdev);
+       if (mddev->degraded)
+               set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
+       set_bit(StateChanged, &rdev->flags);
        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
        set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
@@ -4749,11 +5722,10 @@ static void status_unused(struct seq_file *seq)
 {
        int i = 0;
        mdk_rdev_t *rdev;
-       struct list_head *tmp;
 
        seq_printf(seq, "unused devices: ");
 
-       ITERATE_RDEV_PENDING(rdev,tmp) {
+       list_for_each_entry(rdev, &pending_raid_disks, same_set) {
                char b[BDEVNAME_SIZE];
                i++;
                seq_printf(seq, "%s ",
@@ -4768,37 +5740,38 @@ static void status_unused(struct seq_file *seq)
 
 static void status_resync(struct seq_file *seq, mddev_t * mddev)
 {
-       sector_t max_blocks, resync, res;
-       unsigned long dt, db, rt;
+       sector_t max_sectors, resync, res;
+       unsigned long dt, db;
+       sector_t rt;
        int scale;
        unsigned int per_milli;
 
-       resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2;
+       resync = mddev->curr_resync - atomic_read(&mddev->recovery_active);
 
        if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
-               max_blocks = mddev->resync_max_sectors >> 1;
+               max_sectors = mddev->resync_max_sectors;
        else
-               max_blocks = mddev->size;
+               max_sectors = mddev->dev_sectors;
 
        /*
         * Should not happen.
         */
-       if (!max_blocks) {
+       if (!max_sectors) {
                MD_BUG();
                return;
        }
        /* Pick 'scale' such that (resync>>scale)*1000 will fit
-        * in a sector_t, and (max_blocks>>scale) will fit in a
+        * in a sector_t, and (max_sectors>>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)))
+               while ( max_sectors/2 > (1ULL<<(scale+32)))
                        scale++;
        }
        res = (resync>>scale)*1000;
-       sector_div(res, (u32)((max_blocks>>scale)+1));
+       sector_div(res, (u32)((max_sectors>>scale)+1));
 
        per_milli = res;
        {
@@ -4819,25 +5792,35 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev)
                     (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ?
                      "resync" : "recovery"))),
                   per_milli/10, per_milli % 10,
-                  (unsigned long long) resync,
-                  (unsigned long long) max_blocks);
+                  (unsigned long long) resync/2,
+                  (unsigned long long) max_sectors/2);
 
        /*
-        * We do not want to overflow, so the order of operands and
-        * the * 100 / 100 trick are important. We do a +1 to be
-        * safe against division by zero. We only estimate anyway.
-        *
         * dt: time from mark until now
         * db: blocks written from mark until now
         * rt: remaining time
+        *
+        * rt is a sector_t, so could be 32bit or 64bit.
+        * So we divide before multiply in case it is 32bit and close
+        * to the limit.
+        * We scale the divisor (db) by 32 to avoid loosing precision
+        * near the end of resync when the number of remaining sectors
+        * is close to 'db'.
+        * We then divide rt by 32 after multiplying by db to compensate.
+        * The '+1' avoids division by zero if db is very small.
         */
        dt = ((jiffies - mddev->resync_mark) / HZ);
        if (!dt) dt++;
        db = (mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active))
                - mddev->resync_mark_cnt;
-       rt = (dt * ((unsigned long)(max_blocks-resync) / (db/2/100+1)))/100;
 
-       seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6);
+       rt = max_sectors - resync;    /* number of remaining sectors */
+       sector_div(rt, db/32+1);
+       rt *= dt;
+       rt >>= 5;
+
+       seq_printf(seq, " finish=%lu.%lumin", (unsigned long)rt / 60,
+                  ((unsigned long)rt % 60)/6);
 
        seq_printf(seq, " speed=%ldK/sec", db/2/dt);
 }
@@ -4911,8 +5894,7 @@ struct mdstat_info {
 static int md_seq_show(struct seq_file *seq, void *v)
 {
        mddev_t *mddev = v;
-       sector_t size;
-       struct list_head *tmp2;
+       sector_t sectors;
        mdk_rdev_t *rdev;
        struct mdstat_info *mi = seq->private;
        struct bitmap *bitmap;
@@ -4944,12 +5926,12 @@ static int md_seq_show(struct seq_file *seq, void *v)
                        if (mddev->ro==1)
                                seq_printf(seq, " (read-only)");
                        if (mddev->ro==2)
-                               seq_printf(seq, "(auto-read-only)");
+                               seq_printf(seq, " (auto-read-only)");
                        seq_printf(seq, " %s", mddev->pers->name);
                }
 
-               size = 0;
-               ITERATE_RDEV(mddev,rdev,tmp2) {
+               sectors = 0;
+               list_for_each_entry(rdev, &mddev->disks, same_set) {
                        char b[BDEVNAME_SIZE];
                        seq_printf(seq, " %s[%d]",
                                bdevname(rdev->bdev,b), rdev->desc_nr);
@@ -4960,16 +5942,17 @@ static int md_seq_show(struct seq_file *seq, void *v)
                                continue;
                        } else if (rdev->raid_disk < 0)
                                seq_printf(seq, "(S)"); /* spare */
-                       size += rdev->size;
+                       sectors += rdev->sectors;
                }
 
                if (!list_empty(&mddev->disks)) {
                        if (mddev->pers)
                                seq_printf(seq, "\n      %llu blocks",
-                                       (unsigned long long)mddev->array_size);
+                                          (unsigned long long)
+                                          mddev->array_sectors / 2);
                        else
                                seq_printf(seq, "\n      %llu blocks",
-                                       (unsigned long long)size);
+                                          (unsigned long long)sectors / 2);
                }
                if (mddev->persistent) {
                        if (mddev->major_version != 0 ||
@@ -4978,15 +5961,18 @@ static int md_seq_show(struct seq_file *seq, void *v)
                                           mddev->major_version,
                                           mddev->minor_version);
                        }
-               } else
+               } else if (mddev->external)
+                       seq_printf(seq, " super external:%s",
+                                  mddev->metadata_type);
+               else
                        seq_printf(seq, " super non-persistent");
 
                if (mddev->pers) {
-                       mddev->pers->status (seq, mddev);
+                       mddev->pers->status(seq, mddev);
                        seq_printf(seq, "\n      ");
                        if (mddev->pers->sync_request) {
                                if (mddev->curr_resync > 2) {
-                                       status_resync (seq, mddev);
+                                       status_resync(seq, mddev);
                                        seq_printf(seq, "\n      ");
                                } else if (mddev->curr_resync == 1 || mddev->curr_resync == 2)
                                        seq_printf(seq, "\tresync=DELAYED\n      ");
@@ -5011,8 +5997,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
                                chunk_kb ? "KB" : "B");
                        if (bitmap->file) {
                                seq_printf(seq, ", file: ");
-                               seq_path(seq, bitmap->file->f_path.mnt,
-                                        bitmap->file->f_path.dentry," \t\n");
+                               seq_path(seq, &bitmap->file->f_path, " \t\n");
                        }
 
                        seq_printf(seq, "\n");
@@ -5026,7 +6011,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
        return 0;
 }
 
-static struct seq_operations md_seq_ops = {
+static const struct seq_operations md_seq_ops = {
        .start  = md_seq_start,
        .next   = md_seq_next,
        .stop   = md_seq_stop,
@@ -5094,19 +6079,19 @@ int unregister_md_personality(struct mdk_personality *p)
        return 0;
 }
 
-static int is_mddev_idle(mddev_t *mddev)
+static int is_mddev_idle(mddev_t *mddev, int init)
 {
        mdk_rdev_t * rdev;
-       struct list_head *tmp;
        int idle;
-       long curr_events;
+       int curr_events;
 
        idle = 1;
-       ITERATE_RDEV(mddev,rdev,tmp) {
+       rcu_read_lock();
+       rdev_for_each_rcu(rdev, mddev) {
                struct gendisk *disk = rdev->bdev->bd_contains->bd_disk;
-               curr_events = disk_stat_read(disk, sectors[0]) + 
-                               disk_stat_read(disk, sectors[1]) - 
-                               atomic_read(&disk->sync_io);
+               curr_events = (int)part_stat_read(&disk->part0, sectors[0]) +
+                             (int)part_stat_read(&disk->part0, sectors[1]) -
+                             atomic_read(&disk->sync_io);
                /* sync IO will cause sync_io to increase before the disk_stats
                 * as sync_io is counted when a request starts, and
                 * disk_stats is counted when it completes.
@@ -5129,11 +6114,12 @@ static int is_mddev_idle(mddev_t *mddev)
                 * always make curr_events less than last_events.
                 *
                 */
-               if (curr_events - rdev->last_events > 4096) {
+               if (init || curr_events - rdev->last_events > 64) {
                        rdev->last_events = curr_events;
                        idle = 0;
                }
        }
+       rcu_read_unlock();
        return idle;
 }
 
@@ -5143,7 +6129,7 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok)
        atomic_sub(blocks, &mddev->recovery_active);
        wake_up(&mddev->recovery_wait);
        if (!ok) {
-               set_bit(MD_RECOVERY_ERR, &mddev->recovery);
+               set_bit(MD_RECOVERY_INTR, &mddev->recovery);
                md_wakeup_thread(mddev->thread);
                // stop recovery, signal do_sync ....
        }
@@ -5157,6 +6143,7 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok)
  */
 void md_write_start(mddev_t *mddev, struct bio *bi)
 {
+       int did_change = 0;
        if (bio_data_dir(bi) != WRITE)
                return;
 
@@ -5166,18 +6153,27 @@ void md_write_start(mddev_t *mddev, struct bio *bi)
                mddev->ro = 0;
                set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
                md_wakeup_thread(mddev->thread);
+               md_wakeup_thread(mddev->sync_thread);
+               did_change = 1;
        }
        atomic_inc(&mddev->writes_pending);
+       if (mddev->safemode == 1)
+               mddev->safemode = 0;
        if (mddev->in_sync) {
                spin_lock_irq(&mddev->write_lock);
                if (mddev->in_sync) {
                        mddev->in_sync = 0;
                        set_bit(MD_CHANGE_CLEAN, &mddev->flags);
                        md_wakeup_thread(mddev->thread);
+                       did_change = 1;
                }
                spin_unlock_irq(&mddev->write_lock);
        }
-       wait_event(mddev->sb_wait, mddev->flags==0);
+       if (did_change)
+               sysfs_notify_dirent(mddev->sysfs_state);
+       wait_event(mddev->sb_wait,
+                  !test_bit(MD_CHANGE_CLEAN, &mddev->flags) &&
+                  !test_bit(MD_CHANGE_PENDING, &mddev->flags));
 }
 
 void md_write_end(mddev_t *mddev)
@@ -5195,13 +6191,18 @@ void md_write_end(mddev_t *mddev)
  * may proceed without blocking.  It is important to call this before
  * attempting a GFP_KERNEL allocation while holding the mddev lock.
  * Must be called with mddev_lock held.
+ *
+ * In the ->external case MD_CHANGE_CLEAN can not be cleared until mddev->lock
+ * is dropped, so return -EAGAIN after notifying userspace.
  */
-void md_allow_write(mddev_t *mddev)
+int md_allow_write(mddev_t *mddev)
 {
        if (!mddev->pers)
-               return;
+               return 0;
        if (mddev->ro)
-               return;
+               return 0;
+       if (!mddev->pers->sync_request)
+               return 0;
 
        spin_lock_irq(&mddev->write_lock);
        if (mddev->in_sync) {
@@ -5212,13 +6213,17 @@ void md_allow_write(mddev_t *mddev)
                        mddev->safemode = 1;
                spin_unlock_irq(&mddev->write_lock);
                md_update_sb(mddev, 0);
+               sysfs_notify_dirent(mddev->sysfs_state);
        } else
                spin_unlock_irq(&mddev->write_lock);
+
+       if (test_bit(MD_CHANGE_CLEAN, &mddev->flags))
+               return -EAGAIN;
+       else
+               return 0;
 }
 EXPORT_SYMBOL_GPL(md_allow_write);
 
-static DECLARE_WAIT_QUEUE_HEAD(resync_wait);
-
 #define SYNC_MARKS     10
 #define        SYNC_MARK_STEP  (3*HZ)
 void md_do_sync(mddev_t *mddev)
@@ -5233,7 +6238,6 @@ void md_do_sync(mddev_t *mddev)
        struct list_head *tmp;
        sector_t last_check;
        int skipped = 0;
-       struct list_head *rtmp;
        mdk_rdev_t *rdev;
        char *desc;
 
@@ -5279,11 +6283,12 @@ void md_do_sync(mddev_t *mddev)
                        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
                        goto skip;
                }
-               ITERATE_MDDEV(mddev2,tmp) {
+               for_each_mddev(mddev2, tmp) {
                        if (mddev2 == mddev)
                                continue;
-                       if (mddev2->curr_resync && 
-                           match_mddev_units(mddev,mddev2)) {
+                       if (!mddev->parallel_resync
+                       &&  mddev2->curr_resync
+                       &&  match_mddev_units(mddev, mddev2)) {
                                DEFINE_WAIT(wq);
                                if (mddev < mddev2 && mddev->curr_resync == 2) {
                                        /* arbitrarily yield */
@@ -5295,7 +6300,11 @@ void md_do_sync(mddev_t *mddev)
                                         * time 'round when curr_resync == 2
                                         */
                                        continue;
-                               prepare_to_wait(&resync_wait, &wq, TASK_UNINTERRUPTIBLE);
+                               /* We need to wait 'interruptible' so as not to
+                                * contribute to the load average, and not to
+                                * be caught by 'softlockup'
+                                */
+                               prepare_to_wait(&resync_wait, &wq, TASK_INTERRUPTIBLE);
                                if (!kthread_should_stop() &&
                                    mddev2->curr_resync >= mddev->curr_resync) {
                                        printk(KERN_INFO "md: delaying %s of %s"
@@ -5303,6 +6312,8 @@ void md_do_sync(mddev_t *mddev)
                                               " share one or more physical units)\n",
                                               desc, mdname(mddev), mdname(mddev2));
                                        mddev_put(mddev2);
+                                       if (signal_pending(current))
+                                               flush_signals(current);
                                        schedule();
                                        finish_wait(&resync_wait, &wq);
                                        goto try_again;
@@ -5320,16 +6331,18 @@ void md_do_sync(mddev_t *mddev)
                max_sectors = mddev->resync_max_sectors;
                mddev->resync_mismatches = 0;
                /* we don't use the checkpoint if there's a bitmap */
-               if (!mddev->bitmap &&
-                   !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+               if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+                       j = mddev->resync_min;
+               else if (!mddev->bitmap)
                        j = mddev->recovery_cp;
+
        } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
-               max_sectors = mddev->size << 1;
+               max_sectors = mddev->dev_sectors;
        else {
                /* recovery follows the physical size of devices */
-               max_sectors = mddev->size << 1;
+               max_sectors = mddev->dev_sectors;
                j = MaxSector;
-               ITERATE_RDEV(mddev,rdev,rtmp)
+               list_for_each_entry(rdev, &mddev->disks, same_set)
                        if (rdev->raid_disk >= 0 &&
                            !test_bit(Faulty, &rdev->flags) &&
                            !test_bit(In_sync, &rdev->flags) &&
@@ -5344,7 +6357,7 @@ void md_do_sync(mddev_t *mddev)
               "(but not more than %d KB/sec) for %s.\n",
               speed_max(mddev), desc);
 
-       is_mddev_idle(mddev); /* this also initializes IO event counters */
+       is_mddev_idle(mddev, 1); /* this initializes IO event counters */
 
        io_sectors = 0;
        for (m = 0; m < SYNC_MARKS; m++) {
@@ -5363,7 +6376,6 @@ void md_do_sync(mddev_t *mddev)
                window/2,(unsigned long long) max_sectors/2);
 
        atomic_set(&mddev->recovery_active, 0);
-       init_waitqueue_head(&mddev->recovery_wait);
        last_check = 0;
 
        if (j>2) {
@@ -5377,10 +6389,42 @@ void md_do_sync(mddev_t *mddev)
                sector_t sectors;
 
                skipped = 0;
+
+               if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
+                   ((mddev->curr_resync > mddev->curr_resync_completed &&
+                     (mddev->curr_resync - mddev->curr_resync_completed)
+                     > (max_sectors >> 4)) ||
+                    (j - mddev->curr_resync_completed)*2
+                    >= mddev->resync_max - mddev->curr_resync_completed
+                           )) {
+                       /* time to update curr_resync_completed */
+                       blk_unplug(mddev->queue);
+                       wait_event(mddev->recovery_wait,
+                                  atomic_read(&mddev->recovery_active) == 0);
+                       mddev->curr_resync_completed =
+                               mddev->curr_resync;
+                       set_bit(MD_CHANGE_CLEAN, &mddev->flags);
+                       sysfs_notify(&mddev->kobj, NULL, "sync_completed");
+               }
+
+               while (j >= mddev->resync_max && !kthread_should_stop()) {
+                       /* As this condition is controlled by user-space,
+                        * we can block indefinitely, so use '_interruptible'
+                        * to avoid triggering warnings.
+                        */
+                       flush_signals(current); /* just in case */
+                       wait_event_interruptible(mddev->recovery_wait,
+                                                mddev->resync_max > j
+                                                || kthread_should_stop());
+               }
+
+               if (kthread_should_stop())
+                       goto interrupted;
+
                sectors = mddev->pers->sync_request(mddev, j, &skipped,
-                                           currspeed < speed_min(mddev));
+                                                 currspeed < speed_min(mddev));
                if (sectors == 0) {
-                       set_bit(MD_RECOVERY_ERR, &mddev->recovery);
+                       set_bit(MD_RECOVERY_INTR, &mddev->recovery);
                        goto out;
                }
 
@@ -5403,8 +6447,7 @@ void md_do_sync(mddev_t *mddev)
 
                last_check = io_sectors;
 
-               if (test_bit(MD_RECOVERY_INTR, &mddev->recovery) ||
-                   test_bit(MD_RECOVERY_ERR, &mddev->recovery))
+               if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
                        break;
 
        repeat:
@@ -5420,15 +6463,9 @@ void md_do_sync(mddev_t *mddev)
                }
 
 
-               if (kthread_should_stop()) {
-                       /*
-                        * got a signal, exit.
-                        */
-                       printk(KERN_INFO 
-                               "md: md_do_sync() got signal ... exiting\n");
-                       set_bit(MD_RECOVERY_INTR, &mddev->recovery);
-                       goto out;
-               }
+               if (kthread_should_stop())
+                       goto interrupted;
+
 
                /*
                 * this loop exits only if either when we are slower than
@@ -5438,7 +6475,7 @@ void md_do_sync(mddev_t *mddev)
                 * about not overloading the IO subsystem. (things like an
                 * e2fsck being done on the RAID array should execute fast)
                 */
-               mddev->queue->unplug_fn(mddev->queue);
+               blk_unplug(mddev->queue);
                cond_resched();
 
                currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2
@@ -5446,7 +6483,7 @@ void md_do_sync(mddev_t *mddev)
 
                if (currspeed > speed_min(mddev)) {
                        if ((currspeed > speed_max(mddev)) ||
-                                       !is_mddev_idle(mddev)) {
+                                       !is_mddev_idle(mddev, 0)) {
                                msleep(500);
                                goto repeat;
                        }
@@ -5457,15 +6494,14 @@ void md_do_sync(mddev_t *mddev)
         * this also signals 'finished resyncing' to md_stop
         */
  out:
-       mddev->queue->unplug_fn(mddev->queue);
+       blk_unplug(mddev->queue);
 
        wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active));
 
        /* tell personality that we are finished */
        mddev->pers->sync_request(mddev, max_sectors, &skipped, 1);
 
-       if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) &&
-           !test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&
+       if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&
            mddev->curr_resync > 2) {
                if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
                        if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
@@ -5480,7 +6516,7 @@ void md_do_sync(mddev_t *mddev)
                } else {
                        if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))
                                mddev->curr_resync = MaxSector;
-                       ITERATE_RDEV(mddev,rdev,rtmp)
+                       list_for_each_entry(rdev, &mddev->disks, same_set)
                                if (rdev->raid_disk >= 0 &&
                                    !test_bit(Faulty, &rdev->flags) &&
                                    !test_bit(In_sync, &rdev->flags) &&
@@ -5492,9 +6528,25 @@ void md_do_sync(mddev_t *mddev)
 
  skip:
        mddev->curr_resync = 0;
+       mddev->curr_resync_completed = 0;
+       if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+               /* We completed so max setting can be forgotten. */
+               mddev->resync_max = MaxSector;
+       sysfs_notify(&mddev->kobj, NULL, "sync_completed");
        wake_up(&resync_wait);
        set_bit(MD_RECOVERY_DONE, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
+       return;
+
+ interrupted:
+       /*
+        * got a signal, exit.
+        */
+       printk(KERN_INFO
+              "md: md_do_sync() got signal ... exiting\n");
+       set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+       goto out;
+
 }
 EXPORT_SYMBOL_GPL(md_do_sync);
 
@@ -5502,11 +6554,13 @@ EXPORT_SYMBOL_GPL(md_do_sync);
 static int remove_and_add_spares(mddev_t *mddev)
 {
        mdk_rdev_t *rdev;
-       struct list_head *rtmp;
        int spares = 0;
 
-       ITERATE_RDEV(mddev,rdev,rtmp)
+       mddev->curr_resync_completed = 0;
+
+       list_for_each_entry(rdev, &mddev->disks, same_set)
                if (rdev->raid_disk >= 0 &&
+                   !test_bit(Blocked, &rdev->flags) &&
                    (test_bit(Faulty, &rdev->flags) ||
                     ! test_bit(In_sync, &rdev->flags)) &&
                    atomic_read(&rdev->nr_pending)==0) {
@@ -5519,12 +6573,17 @@ static int remove_and_add_spares(mddev_t *mddev)
                        }
                }
 
-       if (mddev->degraded) {
-               ITERATE_RDEV(mddev,rdev,rtmp)
+       if (mddev->degraded && ! mddev->ro && !mddev->recovery_disabled) {
+               list_for_each_entry(rdev, &mddev->disks, same_set) {
+                       if (rdev->raid_disk >= 0 &&
+                           !test_bit(In_sync, &rdev->flags) &&
+                           !test_bit(Blocked, &rdev->flags))
+                               spares++;
                        if (rdev->raid_disk < 0
                            && !test_bit(Faulty, &rdev->flags)) {
                                rdev->recovery_offset = 0;
-                               if (mddev->pers->hot_add_disk(mddev,rdev)) {
+                               if (mddev->pers->
+                                   hot_add_disk(mddev, rdev) == 0) {
                                        char nm[20];
                                        sprintf(nm, "rd%d", rdev->raid_disk);
                                        if (sysfs_create_link(&mddev->kobj,
@@ -5538,6 +6597,7 @@ static int remove_and_add_spares(mddev_t *mddev)
                                } else
                                        break;
                        }
+               }
        }
        return spares;
 }
@@ -5551,7 +6611,7 @@ static int remove_and_add_spares(mddev_t *mddev)
  * to do that as needed.
  * When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in
  * "->recovery" and create a thread at ->sync_thread.
- * When the thread finishes it sets MD_RECOVERY_DONE (and might set MD_RECOVERY_ERR)
+ * When the thread finishes it sets MD_RECOVERY_DONE
  * and wakeups up this thread which will reap the thread and finish up.
  * This thread also removes any faulty devices (with nr_pending == 0).
  *
@@ -5566,7 +6626,6 @@ static int remove_and_add_spares(mddev_t *mddev)
 void md_check_recovery(mddev_t *mddev)
 {
        mdk_rdev_t *rdev;
-       struct list_head *rtmp;
 
 
        if (mddev->bitmap)
@@ -5576,7 +6635,7 @@ void md_check_recovery(mddev_t *mddev)
                return;
 
        if (signal_pending(current)) {
-               if (mddev->pers->sync_request) {
+               if (mddev->pers->sync_request && !mddev->external) {
                        printk(KERN_INFO "md: %s in immediate safe mode\n",
                               mdname(mddev));
                        mddev->safemode = 2;
@@ -5584,11 +6643,13 @@ void md_check_recovery(mddev_t *mddev)
                flush_signals(current);
        }
 
+       if (mddev->ro && !test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
+               return;
        if ( ! (
-               mddev->flags ||
+               (mddev->flags && !mddev->external) ||
                test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
                test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
-               (mddev->safemode == 1) ||
+               (mddev->external == 0 && mddev->safemode == 1) ||
                (mddev->safemode == 2 && ! atomic_read(&mddev->writes_pending)
                 && !mddev->in_sync && mddev->recovery_cp == MaxSector)
                ))
@@ -5597,19 +6658,41 @@ void md_check_recovery(mddev_t *mddev)
        if (mddev_trylock(mddev)) {
                int spares = 0;
 
-               spin_lock_irq(&mddev->write_lock);
-               if (mddev->safemode && !atomic_read(&mddev->writes_pending) &&
-                   !mddev->in_sync && mddev->recovery_cp == MaxSector) {
-                       mddev->in_sync = 1;
-                       set_bit(MD_CHANGE_CLEAN, &mddev->flags);
+               if (mddev->ro) {
+                       /* Only thing we do on a ro array is remove
+                        * failed devices.
+                        */
+                       remove_and_add_spares(mddev);
+                       clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+                       goto unlock;
+               }
+
+               if (!mddev->external) {
+                       int did_change = 0;
+                       spin_lock_irq(&mddev->write_lock);
+                       if (mddev->safemode &&
+                           !atomic_read(&mddev->writes_pending) &&
+                           !mddev->in_sync &&
+                           mddev->recovery_cp == MaxSector) {
+                               mddev->in_sync = 1;
+                               did_change = 1;
+                               if (mddev->persistent)
+                                       set_bit(MD_CHANGE_CLEAN, &mddev->flags);
+                       }
+                       if (mddev->safemode == 1)
+                               mddev->safemode = 0;
+                       spin_unlock_irq(&mddev->write_lock);
+                       if (did_change)
+                               sysfs_notify_dirent(mddev->sysfs_state);
                }
-               if (mddev->safemode == 1)
-                       mddev->safemode = 0;
-               spin_unlock_irq(&mddev->write_lock);
 
                if (mddev->flags)
                        md_update_sb(mddev, 0);
 
+               list_for_each_entry(rdev, &mddev->disks, same_set)
+                       if (test_and_clear_bit(StateChanged, &rdev->flags))
+                               sysfs_notify_dirent(rdev->sysfs_state);
+
 
                if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
                    !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) {
@@ -5621,32 +6704,41 @@ void md_check_recovery(mddev_t *mddev)
                        /* resync has finished, collect result */
                        md_unregister_thread(mddev->sync_thread);
                        mddev->sync_thread = NULL;
-                       if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) &&
-                           !test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
+                       if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
+                           !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
                                /* success...*/
                                /* activate any spares */
-                               mddev->pers->spare_active(mddev);
+                               if (mddev->pers->spare_active(mddev))
+                                       sysfs_notify(&mddev->kobj, NULL,
+                                                    "degraded");
                        }
+                       if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
+                           mddev->pers->finish_reshape)
+                               mddev->pers->finish_reshape(mddev);
                        md_update_sb(mddev, 1);
 
                        /* if array is no-longer degraded, then any saved_raid_disk
                         * information must be scrapped
                         */
                        if (!mddev->degraded)
-                               ITERATE_RDEV(mddev,rdev,rtmp)
+                               list_for_each_entry(rdev, &mddev->disks, same_set)
                                        rdev->saved_raid_disk = -1;
 
                        mddev->recovery = 0;
                        /* flag recovery needed just to double check */
                        set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+                       sysfs_notify_dirent(mddev->sysfs_action);
                        md_new_event(mddev);
                        goto unlock;
                }
+               /* Set RUNNING before clearing NEEDED to avoid
+                * any transients in the value of "sync_action".
+                */
+               set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+               clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
                /* Clear some bits that don't mean anything, but
                 * might be left set
                 */
-               clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
-               clear_bit(MD_RECOVERY_ERR, &mddev->recovery);
                clear_bit(MD_RECOVERY_INTR, &mddev->recovery);
                clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
 
@@ -5660,21 +6752,25 @@ void md_check_recovery(mddev_t *mddev)
                 */
 
                if (mddev->reshape_position != MaxSector) {
-                       if (mddev->pers->check_reshape(mddev) != 0)
+                       if (mddev->pers->check_reshape == NULL ||
+                           mddev->pers->check_reshape(mddev) != 0)
                                /* Cannot proceed */
                                goto unlock;
                        set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
+                       clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
                } else if ((spares = remove_and_add_spares(mddev))) {
                        clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
                        clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+                       clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+                       set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
                } else if (mddev->recovery_cp < MaxSector) {
                        set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+                       clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
                } else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
                        /* nothing to be done ... */
                        goto unlock;
 
                if (mddev->pers->sync_request) {
-                       set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
                        if (spares && mddev->bitmap && ! mddev->bitmap->file) {
                                /* We are adding a device or devices to an array
                                 * which has the bitmap stored on all devices.
@@ -5684,7 +6780,7 @@ void md_check_recovery(mddev_t *mddev)
                        }
                        mddev->sync_thread = md_register_thread(md_do_sync,
                                                                mddev,
-                                                               "%s_resync");
+                                                               "resync");
                        if (!mddev->sync_thread) {
                                printk(KERN_ERR "%s: could not start resync"
                                        " thread...\n", 
@@ -5693,13 +6789,31 @@ void md_check_recovery(mddev_t *mddev)
                                mddev->recovery = 0;
                        } else
                                md_wakeup_thread(mddev->sync_thread);
+                       sysfs_notify_dirent(mddev->sysfs_action);
                        md_new_event(mddev);
                }
        unlock:
+               if (!mddev->sync_thread) {
+                       clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+                       if (test_and_clear_bit(MD_RECOVERY_RECOVER,
+                                              &mddev->recovery))
+                               if (mddev->sysfs_action)
+                                       sysfs_notify_dirent(mddev->sysfs_action);
+               }
                mddev_unlock(mddev);
        }
 }
 
+void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
+{
+       sysfs_notify_dirent(rdev->sysfs_state);
+       wait_event_timeout(rdev->blocked_wait,
+                          !test_bit(Blocked, &rdev->flags),
+                          msecs_to_jiffies(5000));
+       rdev_dec_pending(rdev, mddev);
+}
+EXPORT_SYMBOL(md_wait_for_blocked_rdev);
+
 static int md_notify_reboot(struct notifier_block *this,
                            unsigned long code, void *x)
 {
@@ -5710,9 +6824,13 @@ static int md_notify_reboot(struct notifier_block *this,
 
                printk(KERN_INFO "md: stopping all md devices.\n");
 
-               ITERATE_MDDEV(mddev,tmp)
+               for_each_mddev(mddev, tmp)
                        if (mddev_trylock(mddev)) {
-                               do_md_stop (mddev, 1);
+                               /* Force a switch to readonly even array
+                                * appears to still be in use.  Hence
+                                * the '100'.
+                                */
+                               do_md_stop(mddev, 1, 100);
                                mddev_unlock(mddev);
                        }
                /*
@@ -5734,24 +6852,20 @@ static struct notifier_block md_notifier = {
 
 static void md_geninit(void)
 {
-       struct proc_dir_entry *p;
-
        dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t));
 
-       p = create_proc_entry("mdstat", S_IRUGO, NULL);
-       if (p)
-               p->proc_fops = &md_seq_fops;
+       proc_create("mdstat", S_IRUGO, NULL, &md_seq_fops);
 }
 
 static int __init md_init(void)
 {
-       if (register_blkdev(MAJOR_NR, "md"))
+       if (register_blkdev(MD_MAJOR, "md"))
                return -1;
        if ((mdp_major=register_blkdev(0, "mdp"))<=0) {
-               unregister_blkdev(MAJOR_NR, "md");
+               unregister_blkdev(MD_MAJOR, "md");
                return -1;
        }
-       blk_register_region(MKDEV(MAJOR_NR, 0), 1UL<<MINORBITS, THIS_MODULE,
+       blk_register_region(MKDEV(MD_MAJOR, 0), 1UL<<MINORBITS, THIS_MODULE,
                            md_probe, NULL, NULL);
        blk_register_region(MKDEV(mdp_major, 0), 1UL<<MINORBITS, THIS_MODULE,
                            md_probe, NULL, NULL);
@@ -5760,7 +6874,7 @@ static int __init md_init(void)
        raid_table_header = register_sysctl_table(raid_root_table);
 
        md_geninit();
-       return (0);
+       return 0;
 }
 
 
@@ -5819,6 +6933,7 @@ static void autostart_arrays(int part)
                        MD_BUG();
                        continue;
                }
+               set_bit(AutoDetected, &rdev->flags);
                list_add(&rdev->same_set, &pending_raid_disks);
                i_passed++;
        }
@@ -5836,23 +6951,17 @@ static __exit void md_exit(void)
        mddev_t *mddev;
        struct list_head *tmp;
 
-       blk_unregister_region(MKDEV(MAJOR_NR,0), 1U << MINORBITS);
+       blk_unregister_region(MKDEV(MD_MAJOR,0), 1U << MINORBITS);
        blk_unregister_region(MKDEV(mdp_major,0), 1U << MINORBITS);
 
-       unregister_blkdev(MAJOR_NR,"md");
+       unregister_blkdev(MD_MAJOR,"md");
        unregister_blkdev(mdp_major, "mdp");
        unregister_reboot_notifier(&md_notifier);
        unregister_sysctl_table(raid_table_header);
        remove_proc_entry("mdstat", NULL);
-       ITERATE_MDDEV(mddev,tmp) {
-               struct gendisk *disk = mddev->gendisk;
-               if (!disk)
-                       continue;
+       for_each_mddev(mddev, tmp) {
                export_array(mddev);
-               del_gendisk(disk);
-               put_disk(disk);
-               mddev->gendisk = NULL;
-               mddev_put(mddev);
+               mddev->hold_active = 0;
        }
 }
 
@@ -5877,6 +6986,7 @@ static int set_ro(const char *val, struct kernel_param *kp)
 module_param_call(start_ro, set_ro, get_ro, NULL, S_IRUSR|S_IWUSR);
 module_param(start_dirty_degraded, int, S_IRUGO|S_IWUSR);
 
+module_param_call(new_array, add_named_array, NULL, NULL, S_IWUSR);
 
 EXPORT_SYMBOL(register_md_personality);
 EXPORT_SYMBOL(unregister_md_personality);