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);
        return 1;
 }
 
+static int strict_blocks_to_sectors(const char *buf, sector_t *sectors)
+{
+       unsigned long long blocks;
+       sector_t new;
+
+       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;
-       unsigned long long sectors;
+       sector_t sectors;
 
-       if (strict_strtoull(buf, 10, §ors) < 0)
+       if (strict_blocks_to_sectors(buf, §ors) < 0)
                return -EINVAL;
-       sectors *= 2;
        if (my_mddev->pers && rdev->raid_disk >= 0) {
                if (my_mddev->persistent) {
                        sectors = super_types[my_mddev->major_version].
         * not increase it (except from 0).
         * If array is active, we can try an on-line resize
         */
-       unsigned long long sectors;
-       int err = strict_strtoull(buf, 10, §ors);
+       sector_t sectors;
+       int err = strict_blocks_to_sectors(buf, §ors);
 
        if (err < 0)
                return err;
-       sectors *= 2;
        if (mddev->pers) {
                err = update_size(mddev, sectors);
                md_update_sb(mddev, 1);
 __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, §ors) < 0)
+                       return -EINVAL;
+               if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors)
+                       return -EINVAL;
+
+               mddev->external_size = 1;
+       }
+
+       mddev->array_sectors = sectors;
+       set_capacity(mddev->gendisk, mddev->array_sectors);
+       if (mddev->pers) {
+               struct block_device *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_sectors << 9);
+                       mutex_unlock(&bdev->bd_inode->i_mutex);
+                       bdput(bdev);
+               }
+       }
+
+       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,
        &md_safe_delay.attr,
        &md_array_state.attr,
        &md_reshape_position.attr,
+       &md_array_size.attr,
        NULL,
 };
 
        err = mddev->pers->run(mddev);
        if (err)
                printk(KERN_ERR "md: pers->run() failed ...\n");
-       else if (mddev->pers->sync_request) {
+       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",
                export_array(mddev);
 
                mddev->array_sectors = 0;
+               mddev->external_size = 0;
                mddev->dev_sectors = 0;
                mddev->raid_disks = 0;
                mddev->recovery_cp = 0;
 
 void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors)
 {
+       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);
 
+void md_set_array_sectors_lock(mddev_t *mddev, sector_t array_sectors)
+{
+       mddev_lock(mddev);
+       md_set_array_sectors(mddev, array_sectors);
+       mddev_unlock(mddev);
+}
+EXPORT_SYMBOL(md_set_array_sectors_lock);
+
 static int update_size(mddev_t *mddev, sector_t num_sectors)
 {
        mdk_rdev_t *rdev;
 
        return 0;
 }
 
+static sector_t raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks);
+
 static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped)
 {
        /* reshaping is quite different to recovery/resync so it is
                            j == sh->qd_idx)
                                continue;
                        s = compute_blocknr(sh, j);
-                       if (s < mddev->array_sectors) {
+                       if (s < raid5_size(mddev, 0, 0)) {
                                skipped = 1;
                                continue;
                        }
        sectors &= ~((sector_t)mddev->chunk_size/512 - 1);
        md_set_array_sectors(mddev, raid5_size(mddev, sectors,
                                               mddev->raid_disks));
+       if (mddev->array_sectors >
+           raid5_size(mddev, sectors, mddev->raid_disks))
+               return -EINVAL;
        set_capacity(mddev->gendisk, mddev->array_sectors);
        mddev->changed = 1;
        if (sectors > mddev->dev_sectors && mddev->recovery_cp == MaxSector) {
        if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) {
                mddev_t *mddev = conf->mddev;
 
-               md_set_array_sectors(mddev, raid5_size(mddev, 0,
+               md_set_array_sectors_lock(mddev, raid5_size(mddev, 0,
                                                       conf->raid_disks));
                set_capacity(mddev->gendisk, mddev->array_sectors);
                mddev->changed = 1;