diff options
-rw-r--r-- | drivers/md/md.c | 112 | ||||
-rw-r--r-- | drivers/md/md.h | 3 | ||||
-rw-r--r-- | drivers/md/raid0.c | 2 | ||||
-rw-r--r-- | drivers/md/raid1.c | 6 | ||||
-rw-r--r-- | drivers/md/raid10.c | 2 | ||||
-rw-r--r-- | drivers/md/raid5.c | 9 |
6 files changed, 121 insertions, 13 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index 76ba69b31d6a..923d1250b9a9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -387,6 +387,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); @@ -2282,16 +2287,34 @@ static int overlaps(sector_t s1, sector_t l1, sector_t s2, sector_t l2) 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]. @@ -3182,12 +3205,11 @@ 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 */ - 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); @@ -3627,6 +3649,57 @@ 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, §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, @@ -3640,6 +3713,7 @@ static struct attribute *md_default_attrs[] = { &md_safe_delay.attr, &md_array_state.attr, &md_reshape_position.attr, + &md_array_size.attr, NULL, }; @@ -4045,7 +4119,17 @@ static int do_md_run(mddev_t * mddev) 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", @@ -4281,6 +4365,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) export_array(mddev); mddev->array_sectors = 0; + mddev->external_size = 0; mddev->dev_sectors = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; @@ -4979,10 +5064,23 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) 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; diff --git a/drivers/md/md.h b/drivers/md/md.h index ce89dda24074..d13e34f842e2 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -147,6 +147,8 @@ struct mddev_s sector_t dev_sectors; /* used size of * component devices */ sector_t array_sectors; /* exported array size */ + int external_size; /* size managed + * externally */ __u64 events; char uuid[16]; @@ -431,3 +433,4 @@ extern void md_new_event(mddev_t *mddev); extern int md_allow_write(mddev_t *mddev); extern void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev); extern void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors); +extern void md_set_array_sectors_lock(mddev_t *mddev, sector_t array_sectors); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 6f7e538c8763..c08d7559be55 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -313,7 +313,7 @@ static int raid0_run (mddev_t *mddev) printk(KERN_INFO "raid0 : conf->spacing is %llu sectors.\n", (unsigned long long)conf->spacing); { - sector_t s = mddev->array_sectors; + sector_t s = raid0_size(mddev, 0, 0); sector_t space = conf->spacing; int round; conf->sector_shift = 0; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 044116b53f7a..b4f4badc0068 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2125,14 +2125,16 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors) * worth it. */ md_set_array_sectors(mddev, raid1_size(mddev, sectors, 0)); + if (mddev->array_sectors > raid1_size(mddev, sectors, 0)) + return -EINVAL; set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; - if (mddev->array_sectors > mddev->dev_sectors && + if (sectors > mddev->dev_sectors && mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->dev_sectors; set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); } - mddev->dev_sectors = mddev->array_sectors; + mddev->dev_sectors = sectors; mddev->resync_max_sectors = sectors; return 0; } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index ad153b24ea0d..e293d92641ac 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2194,7 +2194,7 @@ static int run(mddev_t *mddev) * Ok, everything is just fine now */ md_set_array_sectors(mddev, raid10_size(mddev, 0, 0)); - mddev->resync_max_sectors = mddev->array_sectors; + mddev->resync_max_sectors = raid10_size(mddev, 0, 0); mddev->queue->unplug_fn = raid10_unplug; mddev->queue->backing_dev_info.congested_fn = raid10_congested; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 2930fc26a852..1aebd3ef80b7 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3703,6 +3703,8 @@ static int make_request(struct request_queue *q, struct bio * bi) 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 @@ -3781,7 +3783,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped 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; } @@ -4700,6 +4702,9 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors) 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) { @@ -4837,7 +4842,7 @@ static void end_reshape(raid5_conf_t *conf) 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; |