|
@@ -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, §ors) < 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);
|
|
|
|