Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile
[pandora-kernel.git] / drivers / md / md.c
index 01233d8..1c2f904 100644 (file)
@@ -402,6 +402,7 @@ void mddev_resume(struct mddev *mddev)
        wake_up(&mddev->sb_wait);
        mddev->pers->quiesce(mddev, 0);
 
+       set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
        md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
 }
@@ -452,7 +453,7 @@ static void submit_flushes(struct work_struct *ws)
                        atomic_inc(&rdev->nr_pending);
                        atomic_inc(&rdev->nr_pending);
                        rcu_read_unlock();
-                       bi = bio_alloc_mddev(GFP_KERNEL, 0, mddev);
+                       bi = bio_alloc_mddev(GFP_NOIO, 0, mddev);
                        bi->bi_end_io = md_end_flush;
                        bi->bi_private = rdev;
                        bi->bi_bdev = rdev->bdev;
@@ -607,6 +608,7 @@ void mddev_init(struct mddev *mddev)
        init_waitqueue_head(&mddev->sb_wait);
        init_waitqueue_head(&mddev->recovery_wait);
        mddev->reshape_position = MaxSector;
+       mddev->reshape_backwards = 0;
        mddev->resync_min = 0;
        mddev->resync_max = MaxSector;
        mddev->level = LEVEL_NONE;
@@ -802,7 +804,7 @@ static int alloc_disk_sb(struct md_rdev * rdev)
        return 0;
 }
 
-static void free_disk_sb(struct md_rdev * rdev)
+void md_rdev_clear(struct md_rdev *rdev)
 {
        if (rdev->sb_page) {
                put_page(rdev->sb_page);
@@ -815,8 +817,10 @@ static void free_disk_sb(struct md_rdev * rdev)
                put_page(rdev->bb_page);
                rdev->bb_page = NULL;
        }
+       kfree(rdev->badblocks.page);
+       rdev->badblocks.page = NULL;
 }
-
+EXPORT_SYMBOL_GPL(md_rdev_clear);
 
 static void super_written(struct bio *bio, int error)
 {
@@ -887,6 +891,10 @@ int sync_page_io(struct md_rdev *rdev, sector_t sector, int size,
                rdev->meta_bdev : rdev->bdev;
        if (metadata_op)
                bio->bi_sector = sector + rdev->sb_start;
+       else if (rdev->mddev->reshape_position != MaxSector &&
+                (rdev->mddev->reshape_backwards ==
+                 (sector >= rdev->mddev->reshape_position)))
+               bio->bi_sector = sector + rdev->new_data_offset;
        else
                bio->bi_sector = sector + rdev->data_offset;
        bio_add_page(bio, page, size, 0);
@@ -1034,12 +1042,17 @@ static unsigned int calc_sb_csum(mdp_super_t * sb)
 struct super_type  {
        char                *name;
        struct module       *owner;
-       int                 (*load_super)(struct md_rdev *rdev, struct md_rdev *refdev,
+       int                 (*load_super)(struct md_rdev *rdev,
+                                         struct md_rdev *refdev,
                                          int minor_version);
-       int                 (*validate_super)(struct mddev *mddev, struct md_rdev *rdev);
-       void                (*sync_super)(struct mddev *mddev, struct md_rdev *rdev);
+       int                 (*validate_super)(struct mddev *mddev,
+                                             struct md_rdev *rdev);
+       void                (*sync_super)(struct mddev *mddev,
+                                         struct md_rdev *rdev);
        unsigned long long  (*rdev_size_change)(struct md_rdev *rdev,
                                                sector_t num_sectors);
+       int                 (*allow_new_offset)(struct md_rdev *rdev,
+                                               unsigned long long new_offset);
 };
 
 /*
@@ -1111,6 +1124,7 @@ static int super_90_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor
 
        rdev->preferred_minor = sb->md_minor;
        rdev->data_offset = 0;
+       rdev->new_data_offset = 0;
        rdev->sb_size = MD_SB_BYTES;
        rdev->badblocks.shift = -1;
 
@@ -1184,7 +1198,11 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)
                mddev->dev_sectors = ((sector_t)sb->size) * 2;
                mddev->events = ev1;
                mddev->bitmap_info.offset = 0;
+               mddev->bitmap_info.space = 0;
+               /* bitmap can use 60 K after the 4K superblocks */
                mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
+               mddev->bitmap_info.default_space = 64*2 - (MD_SB_BYTES >> 9);
+               mddev->reshape_backwards = 0;
 
                if (mddev->minor_version >= 91) {
                        mddev->reshape_position = sb->reshape_position;
@@ -1192,6 +1210,8 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)
                        mddev->new_level = sb->new_level;
                        mddev->new_layout = sb->new_layout;
                        mddev->new_chunk_sectors = sb->new_chunk >> 9;
+                       if (mddev->delta_disks < 0)
+                               mddev->reshape_backwards = 1;
                } else {
                        mddev->reshape_position = MaxSector;
                        mddev->delta_disks = 0;
@@ -1218,9 +1238,12 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)
                mddev->max_disks = MD_SB_DISKS;
 
                if (sb->state & (1<<MD_SB_BITMAP_PRESENT) &&
-                   mddev->bitmap_info.file == NULL)
+                   mddev->bitmap_info.file == NULL) {
                        mddev->bitmap_info.offset =
                                mddev->bitmap_info.default_offset;
+                       mddev->bitmap_info.space =
+                               mddev->bitmap_info.space;
+               }
 
        } else if (mddev->pers == NULL) {
                /* Insist on good event counter while assembling, except
@@ -1434,6 +1457,12 @@ super_90_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors)
        return num_sectors;
 }
 
+static int
+super_90_allow_new_offset(struct md_rdev *rdev, unsigned long long new_offset)
+{
+       /* non-zero offset changes not possible with v0.90 */
+       return new_offset == 0;
+}
 
 /*
  * version 1 superblock
@@ -1469,6 +1498,7 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
        struct mdp_superblock_1 *sb;
        int ret;
        sector_t sb_start;
+       sector_t sectors;
        char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
        int bmask;
 
@@ -1523,9 +1553,18 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
                       bdevname(rdev->bdev,b));
                return -EINVAL;
        }
+       if (sb->pad0 ||
+           sb->pad3[0] ||
+           memcmp(sb->pad3, sb->pad3+1, sizeof(sb->pad3) - sizeof(sb->pad3[1])))
+               /* Some padding is non-zero, might be a new feature */
+               return -EINVAL;
 
        rdev->preferred_minor = 0xffff;
        rdev->data_offset = le64_to_cpu(sb->data_offset);
+       rdev->new_data_offset = rdev->data_offset;
+       if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE) &&
+           (le32_to_cpu(sb->feature_map) & MD_FEATURE_NEW_OFFSET))
+               rdev->new_data_offset += (s32)le32_to_cpu(sb->new_offset);
        atomic_set(&rdev->corrected_errors, le32_to_cpu(sb->cnt_corrected_read));
 
        rdev->sb_size = le32_to_cpu(sb->max_dev) * 2 + 256;
@@ -1536,6 +1575,9 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
        if (minor_version
            && rdev->data_offset < sb_start + (rdev->sb_size/512))
                return -EINVAL;
+       if (minor_version
+           && rdev->new_data_offset < sb_start + (rdev->sb_size/512))
+               return -EINVAL;
 
        if (sb->level == cpu_to_le32(LEVEL_MULTIPATH))
                rdev->desc_nr = -1;
@@ -1607,16 +1649,14 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
                else
                        ret = 0;
        }
-       if (minor_version)
-               rdev->sectors = (i_size_read(rdev->bdev->bd_inode) >> 9) -
-                       le64_to_cpu(sb->data_offset);
-       else
-               rdev->sectors = rdev->sb_start;
-       if (rdev->sectors < le64_to_cpu(sb->data_size))
+       if (minor_version) {
+               sectors = (i_size_read(rdev->bdev->bd_inode) >> 9);
+               sectors -= rdev->data_offset;
+       else
+               sectors = rdev->sb_start;
+       if (sectors < le64_to_cpu(sb->data_size))
                return -EINVAL;
        rdev->sectors = le64_to_cpu(sb->data_size);
-       if (le64_to_cpu(sb->size) > rdev->sectors)
-               return -EINVAL;
        return ret;
 }
 
@@ -1644,17 +1684,37 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
                mddev->dev_sectors = le64_to_cpu(sb->size);
                mddev->events = ev1;
                mddev->bitmap_info.offset = 0;
+               mddev->bitmap_info.space = 0;
+               /* Default location for bitmap is 1K after superblock
+                * using 3K - total of 4K
+                */
                mddev->bitmap_info.default_offset = 1024 >> 9;
-               
+               mddev->bitmap_info.default_space = (4096-1024) >> 9;
+               mddev->reshape_backwards = 0;
+
                mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
                memcpy(mddev->uuid, sb->set_uuid, 16);
 
                mddev->max_disks =  (4096-256)/2;
 
                if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET) &&
-                   mddev->bitmap_info.file == NULL )
+                   mddev->bitmap_info.file == NULL) {
                        mddev->bitmap_info.offset =
                                (__s32)le32_to_cpu(sb->bitmap_offset);
+                       /* Metadata doesn't record how much space is available.
+                        * For 1.0, we assume we can use up to the superblock
+                        * if before, else to 4K beyond superblock.
+                        * For others, assume no change is possible.
+                        */
+                       if (mddev->minor_version > 0)
+                               mddev->bitmap_info.space = 0;
+                       else if (mddev->bitmap_info.offset > 0)
+                               mddev->bitmap_info.space =
+                                       8 - mddev->bitmap_info.offset;
+                       else
+                               mddev->bitmap_info.space =
+                                       -mddev->bitmap_info.offset;
+               }
 
                if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) {
                        mddev->reshape_position = le64_to_cpu(sb->reshape_position);
@@ -1662,6 +1722,11 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
                        mddev->new_level = le32_to_cpu(sb->new_level);
                        mddev->new_layout = le32_to_cpu(sb->new_layout);
                        mddev->new_chunk_sectors = le32_to_cpu(sb->new_chunk);
+                       if (mddev->delta_disks < 0 ||
+                           (mddev->delta_disks == 0 &&
+                            (le32_to_cpu(sb->feature_map)
+                             & MD_FEATURE_RESHAPE_BACKWARDS)))
+                               mddev->reshape_backwards = 1;
                } else {
                        mddev->reshape_position = MaxSector;
                        mddev->delta_disks = 0;
@@ -1735,7 +1800,6 @@ static void super_1_sync(struct mddev *mddev, struct md_rdev *rdev)
        sb->feature_map = 0;
        sb->pad0 = 0;
        sb->recovery_offset = cpu_to_le64(0);
-       memset(sb->pad1, 0, sizeof(sb->pad1));
        memset(sb->pad3, 0, sizeof(sb->pad3));
 
        sb->utime = cpu_to_le64((__u64)mddev->utime);
@@ -1757,6 +1821,8 @@ static void super_1_sync(struct mddev *mddev, struct md_rdev *rdev)
                sb->devflags |= WriteMostly1;
        else
                sb->devflags &= ~WriteMostly1;
+       sb->data_offset = cpu_to_le64(rdev->data_offset);
+       sb->data_size = cpu_to_le64(rdev->sectors);
 
        if (mddev->bitmap && mddev->bitmap_info.file == NULL) {
                sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_info.offset);
@@ -1781,6 +1847,16 @@ static void super_1_sync(struct mddev *mddev, struct md_rdev *rdev)
                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_sectors);
+               if (mddev->delta_disks == 0 &&
+                   mddev->reshape_backwards)
+                       sb->feature_map
+                               |= cpu_to_le32(MD_FEATURE_RESHAPE_BACKWARDS);
+               if (rdev->new_data_offset != rdev->data_offset) {
+                       sb->feature_map
+                               |= cpu_to_le32(MD_FEATURE_NEW_OFFSET);
+                       sb->new_offset = cpu_to_le32((__u32)(rdev->new_data_offset
+                                                            - rdev->data_offset));
+               }
        }
 
        if (rdev->badblocks.count == 0)
@@ -1857,6 +1933,8 @@ super_1_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors)
        sector_t max_sectors;
        if (num_sectors && num_sectors < rdev->mddev->dev_sectors)
                return 0; /* component must fit device */
+       if (rdev->data_offset != rdev->new_data_offset)
+               return 0; /* too confusing */
        if (rdev->sb_start < rdev->data_offset) {
                /* minor versions 1 and 2; superblock before data */
                max_sectors = i_size_read(rdev->bdev->bd_inode) >> 9;
@@ -1884,6 +1962,40 @@ super_1_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors)
                       rdev->sb_page);
        md_super_wait(rdev->mddev);
        return num_sectors;
+
+}
+
+static int
+super_1_allow_new_offset(struct md_rdev *rdev,
+                        unsigned long long new_offset)
+{
+       /* All necessary checks on new >= old have been done */
+       struct bitmap *bitmap;
+       if (new_offset >= rdev->data_offset)
+               return 1;
+
+       /* with 1.0 metadata, there is no metadata to tread on
+        * so we can always move back */
+       if (rdev->mddev->minor_version == 0)
+               return 1;
+
+       /* otherwise we must be sure not to step on
+        * any metadata, so stay:
+        * 36K beyond start of superblock
+        * beyond end of badblocks
+        * beyond write-intent bitmap
+        */
+       if (rdev->sb_start + (32+4)*2 > new_offset)
+               return 0;
+       bitmap = rdev->mddev->bitmap;
+       if (bitmap && !rdev->mddev->bitmap_info.file &&
+           rdev->sb_start + rdev->mddev->bitmap_info.offset +
+           bitmap->storage.file_pages * (PAGE_SIZE>>9) > new_offset)
+               return 0;
+       if (rdev->badblocks.sector + rdev->badblocks.size > new_offset)
+               return 0;
+
+       return 1;
 }
 
 static struct super_type super_types[] = {
@@ -1894,6 +2006,7 @@ static struct super_type super_types[] = {
                .validate_super     = super_90_validate,
                .sync_super         = super_90_sync,
                .rdev_size_change   = super_90_rdev_size_change,
+               .allow_new_offset   = super_90_allow_new_offset,
        },
        [1] = {
                .name   = "md-1",
@@ -1902,6 +2015,7 @@ static struct super_type super_types[] = {
                .validate_super     = super_1_validate,
                .sync_super         = super_1_sync,
                .rdev_size_change   = super_1_rdev_size_change,
+               .allow_new_offset   = super_1_allow_new_offset,
        },
 };
 
@@ -2105,9 +2219,7 @@ static void unbind_rdev_from_array(struct md_rdev * rdev)
        sysfs_remove_link(&rdev->kobj, "block");
        sysfs_put(rdev->sysfs_state);
        rdev->sysfs_state = NULL;
-       kfree(rdev->badblocks.page);
        rdev->badblocks.count = 0;
-       rdev->badblocks.page = NULL;
        /* We need to delay this, otherwise we can deadlock when
         * writing to 'remove' to "dev/state".  We also need
         * to delay it due to rcu usage.
@@ -2158,7 +2270,7 @@ static void export_rdev(struct md_rdev * rdev)
                bdevname(rdev->bdev,b));
        if (rdev->mddev)
                MD_BUG();
-       free_disk_sb(rdev);
+       md_rdev_clear(rdev);
 #ifndef MODULE
        if (test_bit(AutoDetected, &rdev->flags))
                md_autodetect_dev(rdev->bdev->bd_dev);
@@ -2809,9 +2921,8 @@ offset_show(struct md_rdev *rdev, char *page)
 static ssize_t
 offset_store(struct md_rdev *rdev, const char *buf, size_t len)
 {
-       char *e;
-       unsigned long long offset = simple_strtoull(buf, &e, 10);
-       if (e==buf || (*e && *e != '\n'))
+       unsigned long long offset;
+       if (strict_strtoull(buf, 10, &offset) < 0)
                return -EINVAL;
        if (rdev->mddev->pers && rdev->raid_disk >= 0)
                return -EBUSY;
@@ -2826,6 +2937,63 @@ offset_store(struct md_rdev *rdev, const char *buf, size_t len)
 static struct rdev_sysfs_entry rdev_offset =
 __ATTR(offset, S_IRUGO|S_IWUSR, offset_show, offset_store);
 
+static ssize_t new_offset_show(struct md_rdev *rdev, char *page)
+{
+       return sprintf(page, "%llu\n",
+                      (unsigned long long)rdev->new_data_offset);
+}
+
+static ssize_t new_offset_store(struct md_rdev *rdev,
+                               const char *buf, size_t len)
+{
+       unsigned long long new_offset;
+       struct mddev *mddev = rdev->mddev;
+
+       if (strict_strtoull(buf, 10, &new_offset) < 0)
+               return -EINVAL;
+
+       if (mddev->sync_thread)
+               return -EBUSY;
+       if (new_offset == rdev->data_offset)
+               /* reset is always permitted */
+               ;
+       else if (new_offset > rdev->data_offset) {
+               /* must not push array size beyond rdev_sectors */
+               if (new_offset - rdev->data_offset
+                   + mddev->dev_sectors > rdev->sectors)
+                               return -E2BIG;
+       }
+       /* Metadata worries about other space details. */
+
+       /* decreasing the offset is inconsistent with a backwards
+        * reshape.
+        */
+       if (new_offset < rdev->data_offset &&
+           mddev->reshape_backwards)
+               return -EINVAL;
+       /* Increasing offset is inconsistent with forwards
+        * reshape.  reshape_direction should be set to
+        * 'backwards' first.
+        */
+       if (new_offset > rdev->data_offset &&
+           !mddev->reshape_backwards)
+               return -EINVAL;
+
+       if (mddev->pers && mddev->persistent &&
+           !super_types[mddev->major_version]
+           .allow_new_offset(rdev, new_offset))
+               return -E2BIG;
+       rdev->new_data_offset = new_offset;
+       if (new_offset > rdev->data_offset)
+               mddev->reshape_backwards = 1;
+       else if (new_offset < rdev->data_offset)
+               mddev->reshape_backwards = 0;
+
+       return len;
+}
+static struct rdev_sysfs_entry rdev_new_offset =
+__ATTR(new_offset, S_IRUGO|S_IWUSR, new_offset_show, new_offset_store);
+
 static ssize_t
 rdev_size_show(struct md_rdev *rdev, char *page)
 {
@@ -2870,6 +3038,8 @@ rdev_size_store(struct md_rdev *rdev, const char *buf, size_t len)
 
        if (strict_blocks_to_sectors(buf, &sectors) < 0)
                return -EINVAL;
+       if (rdev->data_offset != rdev->new_data_offset)
+               return -EINVAL; /* too confusing */
        if (my_mddev->pers && rdev->raid_disk >= 0) {
                if (my_mddev->persistent) {
                        sectors = super_types[my_mddev->major_version].
@@ -3006,6 +3176,7 @@ static struct attribute *rdev_default_attrs[] = {
        &rdev_errors.attr,
        &rdev_slot.attr,
        &rdev_offset.attr,
+       &rdev_new_offset.attr,
        &rdev_size.attr,
        &rdev_recovery_start.attr,
        &rdev_bad_blocks.attr,
@@ -3080,6 +3251,7 @@ int md_rdev_init(struct md_rdev *rdev)
        rdev->raid_disk = -1;
        rdev->flags = 0;
        rdev->data_offset = 0;
+       rdev->new_data_offset = 0;
        rdev->sb_events = 0;
        rdev->last_read_error.tv_sec  = 0;
        rdev->last_read_error.tv_nsec = 0;
@@ -3178,8 +3350,7 @@ static struct md_rdev *md_import_device(dev_t newdev, int super_format, int supe
 abort_free:
        if (rdev->bdev)
                unlock_rdev(rdev);
-       free_disk_sb(rdev);
-       kfree(rdev->badblocks.page);
+       md_rdev_clear(rdev);
        kfree(rdev);
        return ERR_PTR(err);
 }
@@ -3419,6 +3590,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
                mddev->new_chunk_sectors = mddev->chunk_sectors;
                mddev->raid_disks -= mddev->delta_disks;
                mddev->delta_disks = 0;
+               mddev->reshape_backwards = 0;
                module_put(pers->owner);
                printk(KERN_WARNING "md: %s: %s would not accept array\n",
                       mdname(mddev), clevel);
@@ -3492,6 +3664,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
        mddev->layout = mddev->new_layout;
        mddev->chunk_sectors = mddev->new_chunk_sectors;
        mddev->delta_disks = 0;
+       mddev->reshape_backwards = 0;
        mddev->degraded = 0;
        if (mddev->pers->sync_request == NULL) {
                /* this is now an array without redundancy, so
@@ -3501,10 +3674,8 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
                del_timer_sync(&mddev->safemode_timer);
        }
        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);
+       mddev_resume(mddev);
        sysfs_notify(&mddev->kobj, NULL, "level");
        md_new_event(mddev);
        return rv;
@@ -3582,9 +3753,20 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
        if (mddev->pers)
                rv = update_raid_disks(mddev, n);
        else if (mddev->reshape_position != MaxSector) {
+               struct md_rdev *rdev;
                int olddisks = mddev->raid_disks - mddev->delta_disks;
+
+               rdev_for_each(rdev, mddev) {
+                       if (olddisks < n &&
+                           rdev->data_offset < rdev->new_data_offset)
+                               return -EINVAL;
+                       if (olddisks > n &&
+                           rdev->data_offset > rdev->new_data_offset)
+                               return -EINVAL;
+               }
                mddev->delta_disks = n - olddisks;
                mddev->raid_disks = n;
+               mddev->reshape_backwards = (mddev->delta_disks < 0);
        } else
                mddev->raid_disks = n;
        return rv ? rv : len;
@@ -4266,7 +4448,8 @@ sync_completed_show(struct mddev *mddev, char *page)
        if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
                return sprintf(page, "none\n");
 
-       if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
+       if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
+           test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
                max_sectors = mddev->resync_max_sectors;
        else
                max_sectors = mddev->dev_sectors;
@@ -4428,6 +4611,7 @@ reshape_position_show(struct mddev *mddev, char *page)
 static ssize_t
 reshape_position_store(struct mddev *mddev, const char *buf, size_t len)
 {
+       struct md_rdev *rdev;
        char *e;
        unsigned long long new = simple_strtoull(buf, &e, 10);
        if (mddev->pers)
@@ -4436,9 +4620,12 @@ reshape_position_store(struct mddev *mddev, const char *buf, size_t len)
                return -EINVAL;
        mddev->reshape_position = new;
        mddev->delta_disks = 0;
+       mddev->reshape_backwards = 0;
        mddev->new_level = mddev->level;
        mddev->new_layout = mddev->layout;
        mddev->new_chunk_sectors = mddev->chunk_sectors;
+       rdev_for_each(rdev, mddev)
+               rdev->new_data_offset = rdev->data_offset;
        return len;
 }
 
@@ -4446,6 +4633,42 @@ static struct md_sysfs_entry md_reshape_position =
 __ATTR(reshape_position, S_IRUGO|S_IWUSR, reshape_position_show,
        reshape_position_store);
 
+static ssize_t
+reshape_direction_show(struct mddev *mddev, char *page)
+{
+       return sprintf(page, "%s\n",
+                      mddev->reshape_backwards ? "backwards" : "forwards");
+}
+
+static ssize_t
+reshape_direction_store(struct mddev *mddev, const char *buf, size_t len)
+{
+       int backwards = 0;
+       if (cmd_match(buf, "forwards"))
+               backwards = 0;
+       else if (cmd_match(buf, "backwards"))
+               backwards = 1;
+       else
+               return -EINVAL;
+       if (mddev->reshape_backwards == backwards)
+               return len;
+
+       /* check if we are allowed to change */
+       if (mddev->delta_disks)
+               return -EBUSY;
+
+       if (mddev->persistent &&
+           mddev->major_version == 0)
+               return -EINVAL;
+
+       mddev->reshape_backwards = backwards;
+       return len;
+}
+
+static struct md_sysfs_entry md_reshape_direction =
+__ATTR(reshape_direction, S_IRUGO|S_IWUSR, reshape_direction_show,
+       reshape_direction_store);
+
 static ssize_t
 array_size_show(struct mddev *mddev, char *page)
 {
@@ -4501,6 +4724,7 @@ static struct attribute *md_default_attrs[] = {
        &md_safe_delay.attr,
        &md_array_state.attr,
        &md_reshape_position.attr,
+       &md_reshape_direction.attr,
        &md_array_size.attr,
        &max_corr_read_errors.attr,
        NULL,
@@ -4914,7 +5138,8 @@ int md_run(struct mddev *mddev)
                err = -EINVAL;
                mddev->pers->stop(mddev);
        }
-       if (err == 0 && mddev->pers->sync_request) {
+       if (err == 0 && mddev->pers->sync_request &&
+           (mddev->bitmap_info.file || mddev->bitmap_info.offset)) {
                err = bitmap_create(mddev);
                if (err) {
                        printk(KERN_ERR "%s: failed to create bitmap (%d)\n",
@@ -5064,6 +5289,7 @@ static void md_clean(struct mddev *mddev)
        mddev->events = 0;
        mddev->can_decrease_events = 0;
        mddev->delta_disks = 0;
+       mddev->reshape_backwards = 0;
        mddev->new_level = LEVEL_NONE;
        mddev->new_layout = 0;
        mddev->new_chunk_sectors = 0;
@@ -5079,6 +5305,7 @@ static void md_clean(struct mddev *mddev)
        mddev->merge_check_needed = 0;
        mddev->bitmap_info.offset = 0;
        mddev->bitmap_info.default_offset = 0;
+       mddev->bitmap_info.default_space = 0;
        mddev->bitmap_info.chunksize = 0;
        mddev->bitmap_info.daemon_sleep = 0;
        mddev->bitmap_info.max_write_behind = 0;
@@ -5421,7 +5648,7 @@ static int get_bitmap_file(struct mddev * mddev, void __user * arg)
                goto out;
 
        /* bitmap disabled, zero the first byte and copy out */
-       if (!mddev->bitmap || !mddev->bitmap->file) {
+       if (!mddev->bitmap || !mddev->bitmap->storage.file) {
                file->pathname[0] = '\0';
                goto copy_out;
        }
@@ -5430,7 +5657,8 @@ static int get_bitmap_file(struct mddev * mddev, void __user * arg)
        if (!buf)
                goto out;
 
-       ptr = d_path(&mddev->bitmap->file->f_path, buf, sizeof(file->pathname));
+       ptr = d_path(&mddev->bitmap->storage.file->f_path,
+                    buf, sizeof(file->pathname));
        if (IS_ERR(ptr))
                goto out;
 
@@ -5875,6 +6103,7 @@ static int set_array_info(struct mddev * mddev, mdu_array_info_t *info)
        set_bit(MD_CHANGE_DEVS, &mddev->flags);
 
        mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
+       mddev->bitmap_info.default_space = 64*2 - (MD_SB_BYTES >> 9);
        mddev->bitmap_info.offset = 0;
 
        mddev->reshape_position = MaxSector;
@@ -5888,6 +6117,7 @@ static int set_array_info(struct mddev * mddev, mdu_array_info_t *info)
        mddev->new_chunk_sectors = mddev->chunk_sectors;
        mddev->new_layout = mddev->layout;
        mddev->delta_disks = 0;
+       mddev->reshape_backwards = 0;
 
        return 0;
 }
@@ -5922,11 +6152,7 @@ static int update_size(struct mddev *mddev, sector_t num_sectors)
         */
        if (mddev->sync_thread)
                return -EBUSY;
-       if (mddev->bitmap)
-               /* Sorry, cannot grow a bitmap yet, just remove it,
-                * grow, and re-add.
-                */
-               return -EBUSY;
+
        rdev_for_each(rdev, mddev) {
                sector_t avail = rdev->sectors;
 
@@ -5944,6 +6170,7 @@ static int update_size(struct mddev *mddev, sector_t num_sectors)
 static int update_raid_disks(struct mddev *mddev, int raid_disks)
 {
        int rv;
+       struct md_rdev *rdev;
        /* change the number of raid disks */
        if (mddev->pers->check_reshape == NULL)
                return -EINVAL;
@@ -5952,11 +6179,27 @@ static int update_raid_disks(struct mddev *mddev, int raid_disks)
                return -EINVAL;
        if (mddev->sync_thread || mddev->reshape_position != MaxSector)
                return -EBUSY;
+
+       rdev_for_each(rdev, mddev) {
+               if (mddev->raid_disks < raid_disks &&
+                   rdev->data_offset < rdev->new_data_offset)
+                       return -EINVAL;
+               if (mddev->raid_disks > raid_disks &&
+                   rdev->data_offset > rdev->new_data_offset)
+                       return -EINVAL;
+       }
+
        mddev->delta_disks = raid_disks - mddev->raid_disks;
+       if (mddev->delta_disks < 0)
+               mddev->reshape_backwards = 1;
+       else if (mddev->delta_disks > 0)
+               mddev->reshape_backwards = 0;
 
        rv = mddev->pers->check_reshape(mddev);
-       if (rv < 0)
+       if (rv < 0) {
                mddev->delta_disks = 0;
+               mddev->reshape_backwards = 0;
+       }
        return rv;
 }
 
@@ -6039,6 +6282,8 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
                                return -EINVAL;
                        mddev->bitmap_info.offset =
                                mddev->bitmap_info.default_offset;
+                       mddev->bitmap_info.space =
+                               mddev->bitmap_info.default_space;
                        mddev->pers->quiesce(mddev, 1);
                        rv = bitmap_create(mddev);
                        if (!rv)
@@ -6050,7 +6295,7 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
                        /* remove the bitmap */
                        if (!mddev->bitmap)
                                return -ENOENT;
-                       if (mddev->bitmap->file)
+                       if (mddev->bitmap->storage.file)
                                return -EINVAL;
                        mddev->pers->quiesce(mddev, 1);
                        bitmap_destroy(mddev);
@@ -6373,6 +6618,9 @@ static int md_open(struct block_device *bdev, fmode_t mode)
        struct mddev *mddev = mddev_find(bdev->bd_dev);
        int err;
 
+       if (!mddev)
+               return -ENODEV;
+
        if (mddev->gendisk != bdev->bd_disk) {
                /* we are racing with mddev_put which is discarding this
                 * bd_disk.
@@ -6584,7 +6832,8 @@ static void status_resync(struct seq_file *seq, struct mddev * mddev)
 
        resync = mddev->curr_resync - atomic_read(&mddev->recovery_active);
 
-       if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
+       if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
+           test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
                max_sectors = mddev->resync_max_sectors;
        else
                max_sectors = mddev->dev_sectors;
@@ -7147,7 +7396,7 @@ void md_do_sync(struct mddev *mddev)
                        j = mddev->recovery_cp;
 
        } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
-               max_sectors = mddev->dev_sectors;
+               max_sectors = mddev->resync_max_sectors;
        else {
                /* recovery follows the physical size of devices */
                max_sectors = mddev->dev_sectors;
@@ -7598,7 +7847,7 @@ void md_check_recovery(struct mddev *mddev)
                        goto unlock;
 
                if (mddev->pers->sync_request) {
-                       if (spares && mddev->bitmap && ! mddev->bitmap->file) {
+                       if (spares) {
                                /* We are adding a device or devices to an array
                                 * which has the bitmap stored on all devices.
                                 * So make sure all bitmap pages get written
@@ -7646,6 +7895,20 @@ void md_wait_for_blocked_rdev(struct md_rdev *rdev, struct mddev *mddev)
 }
 EXPORT_SYMBOL(md_wait_for_blocked_rdev);
 
+void md_finish_reshape(struct mddev *mddev)
+{
+       /* called be personality module when reshape completes. */
+       struct md_rdev *rdev;
+
+       rdev_for_each(rdev, mddev) {
+               if (rdev->data_offset > rdev->new_data_offset)
+                       rdev->sectors += rdev->data_offset - rdev->new_data_offset;
+               else
+                       rdev->sectors -= rdev->new_data_offset - rdev->data_offset;
+               rdev->data_offset = rdev->new_data_offset;
+       }
+}
+EXPORT_SYMBOL(md_finish_reshape);
 
 /* Bad block management.
  * We can record which blocks on each device are 'bad' and so just
@@ -7894,10 +8157,15 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
 }
 
 int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
-                      int acknowledged)
+                      int is_new)
 {
-       int rv = md_set_badblocks(&rdev->badblocks,
-                                 s + rdev->data_offset, sectors, acknowledged);
+       int rv;
+       if (is_new)
+               s += rdev->new_data_offset;
+       else
+               s += rdev->data_offset;
+       rv = md_set_badblocks(&rdev->badblocks,
+                             s, sectors, 0);
        if (rv) {
                /* Make sure they get written out promptly */
                sysfs_notify_dirent_safe(rdev->sysfs_state);
@@ -8003,11 +8271,15 @@ out:
        return rv;
 }
 
-int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors)
+int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
+                        int is_new)
 {
+       if (is_new)
+               s += rdev->new_data_offset;
+       else
+               s += rdev->data_offset;
        return md_clear_badblocks(&rdev->badblocks,
-                                 s + rdev->data_offset,
-                                 sectors);
+                                 s, sectors);
 }
 EXPORT_SYMBOL_GPL(rdev_clear_badblocks);