/*
* mdadm - manage Linux "md" devices aka RAID arrays.
*
- * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
+ * Copyright (C) 2001-2012 Neil Brown <neilb@suse.de>
*
*
* This program is free software; you can redistribute it and/or modify
st = super_by_fd(fd, &subarray);
if (!st) {
- pr_err("cannot handle arrays with superblock version %d\n", info.array.major_version);
+ pr_err("cannot handle arrays with superblock version %d\n",
+ info.array.major_version);
return 1;
}
free(st);
return 1;
}
- /* now check out all the devices and make sure we can read the superblock */
+ /* now check out all the devices and make sure we can read the
+ * superblock */
for (d=0 ; d < info.array.raid_disks ; d++) {
mdu_disk_info_t disk;
char *dv;
return 0;
}
-int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int write_behind, int force)
+int Grow_addbitmap(char *devname, int fd, struct context *c, struct shape *s)
{
/*
* First check that array doesn't have a bitmap
return 1;
}
if (bmf.pathname[0]) {
- if (strcmp(file,"none")==0) {
+ if (strcmp(s->bitmap_file,"none")==0) {
if (ioctl(fd, SET_BITMAP_FILE, -1)!= 0) {
pr_err("failed to remove bitmap %s\n",
bmf.pathname);
return 1;
}
if (array.state & (1<<MD_SB_BITMAP_PRESENT)) {
- if (strcmp(file, "none")==0) {
+ if (strcmp(s->bitmap_file, "none")==0) {
array.state &= ~(1<<MD_SB_BITMAP_PRESENT);
if (ioctl(fd, SET_ARRAY_INFO, &array)!= 0) {
pr_err("failed to remove internal bitmap.\n");
return 1;
}
- if (strcmp(file, "none") == 0) {
+ if (strcmp(s->bitmap_file, "none") == 0) {
pr_err("no bitmap found on %s\n", devname);
return 1;
}
free(st);
return 1;
}
- if (strcmp(file, "internal") == 0) {
+ if (strcmp(s->bitmap_file, "internal") == 0) {
int rv;
int d;
int offset_setable = 0;
"with %s metadata\n", st->ss->name);
return 1;
}
- mdi = sysfs_read(fd, -1, GET_BITMAP_LOCATION);
+ mdi = sysfs_read(fd, NULL, GET_BITMAP_LOCATION);
if (mdi)
offset_setable = 1;
for (d=0; d< st->max_devs; d++) {
if (st->ss->load_super(st, fd2, NULL)==0) {
if (st->ss->add_internal_bitmap(
st,
- &chunk, delay, write_behind,
+ &s->bitmap_chunk, c->delay, s->write_behind,
bitmapsize, offset_setable,
major)
)
st->ss->write_bitmap(st, fd2);
else {
- pr_err("failed "
- "to create internal bitmap - chunksize problem.\n");
+ pr_err("failed to create internal bitmap"
+ " - chunksize problem.\n");
close(fd2);
return 1;
}
}
if (offset_setable) {
st->ss->getinfo_super(st, mdi, NULL);
- sysfs_init(mdi, fd, -1);
+ sysfs_init(mdi, fd, NULL);
rv = sysfs_set_num_signed(mdi, NULL, "bitmap/location",
mdi->bitmap_offset);
} else {
int max_devs = st->max_devs;
/* try to load a superblock */
- for (d=0; d<max_devs; d++) {
+ for (d = 0; d < max_devs; d++) {
mdu_disk_info_t disk;
char *dv;
int fd2;
(disk.state & (1<<MD_DISK_REMOVED)))
continue;
dv = map_dev(disk.major, disk.minor, 1);
- if (!dv) continue;
+ if (!dv)
+ continue;
fd2 = dev_open(dv, O_RDONLY);
if (fd2 >= 0) {
if (st->ss->load_super(st, fd2, NULL) == 0) {
pr_err("cannot find UUID for array!\n");
return 1;
}
- if (CreateBitmap(file, force, (char*)uuid, chunk,
- delay, write_behind, bitmapsize, major)) {
+ if (CreateBitmap(s->bitmap_file, c->force, (char*)uuid, s->bitmap_chunk,
+ c->delay, s->write_behind, bitmapsize, major)) {
return 1;
}
- bitmap_fd = open(file, O_RDWR);
+ bitmap_fd = open(s->bitmap_file, O_RDWR);
if (bitmap_fd < 0) {
pr_err("weird: %s cannot be opened\n",
- file);
+ s->bitmap_file);
return 1;
}
if (ioctl(fd, SET_BITMAP_FILE, bitmap_fd) < 0) {
{
int i;
int csum = 0;
- for (i=0; i<len; i++)
+ for (i = 0; i < len; i++)
csum = (csum<<3) + buf[0];
return __cpu_to_le32(csum);
}
/* Check that all member arrays for this container, or the
* container of this array, are idle
*/
- int container_dev = (st->container_dev != NoMdDev
- ? st->container_dev : st->devnum);
- char container[40];
+ char *container = (st->container_devnm[0]
+ ? st->container_devnm : st->devnm);
struct mdstat_ent *ent, *e;
int is_idle = 1;
- fmt_devname(container, container_dev);
ent = mdstat_read(0, 0);
for (e = ent ; e; e = e->next) {
if (!is_container_member(e, container))
static int freeze_container(struct supertype *st)
{
- int container_dev = (st->container_dev != NoMdDev
- ? st->container_dev : st->devnum);
- char container[40];
+ char *container = (st->container_devnm[0]
+ ? st->container_devnm : st->devnm);
if (!check_idle(st))
return -1;
- fmt_devname(container, container_dev);
-
if (block_monitor(container, 1)) {
pr_err("failed to freeze container\n");
return -2;
static void unfreeze_container(struct supertype *st)
{
- int container_dev = (st->container_dev != NoMdDev
- ? st->container_dev : st->devnum);
- char container[40];
-
- fmt_devname(container, container_dev);
+ char *container = (st->container_devnm[0]
+ ? st->container_devnm : st->devnm);
unblock_monitor(container, 1);
}
if (st->ss->external)
return freeze_container(st);
else {
- struct mdinfo *sra = sysfs_read(-1, st->devnum, GET_VERSION);
+ struct mdinfo *sra = sysfs_read(-1, st->devnm, GET_VERSION);
int err;
char buf[20];
if (st->ss->external)
return unfreeze_container(st);
else {
- struct mdinfo *sra = sysfs_read(-1, st->devnum, GET_VERSION);
+ struct mdinfo *sra = sysfs_read(-1, st->devnm, GET_VERSION);
if (sra)
sysfs_set_str(sra, NULL, "sync_action", "idle");
close(fd);
}
-static int reshape_super(struct supertype *st, long long size, int level,
- int layout, int chunksize, int raid_disks,
+static int reshape_super(struct supertype *st, unsigned long long size,
+ int level, int layout, int chunksize, int raid_disks,
int delta_disks, char *backup_file, char *dev,
int direction, int verbose)
{
return 1;
}
+unsigned long GCD(unsigned long a, unsigned long b)
+{
+ while (a != b) {
+ if (a < b)
+ b -= a;
+ if (b < a)
+ a -= b;
+ }
+ return a;
+}
+
unsigned long compute_backup_blocks(int nchunk, int ochunk,
unsigned int ndata, unsigned int odata)
{
a = (ochunk/512) * odata;
b = (nchunk/512) * ndata;
/* Find GCD */
- while (a != b) {
- if (a < b)
- b -= a;
- if (b < a)
- a -= b;
- }
+ a = GCD(a, b);
/* LCM == product / GCD */
blocks = (ochunk/512) * (nchunk/512) * odata * ndata / a;
* This can be called as part of starting a reshape, or
* when assembling an array that is undergoing reshape.
*/
+ int near, far, offset, copies;
int new_disks;
+ int old_chunk, new_chunk;
/* delta_parity records change in number of devices
* caused by level change
*/
return "Impossibly level change request for RAID1";
case 10:
- /* RAID10 can only be converted from near mode to
- * RAID0 by removing some devices
+ /* RAID10 can be converted from near mode to
+ * RAID0 by removing some devices.
+ * It can also be reshaped if the kernel supports
+ * new_data_offset.
*/
- if ((info->array.layout & ~0xff) != 0x100)
- return "Cannot Grow RAID10 with far/offset layout";
- /* number of devices must be multiple of number of copies */
- if (info->array.raid_disks % (info->array.layout & 0xff))
- return "RAID10 layout too complex for Grow operation";
+ switch (info->new_level) {
+ case 0:
+ if ((info->array.layout & ~0xff) != 0x100)
+ return "Cannot Grow RAID10 with far/offset layout";
+ /* number of devices must be multiple of number of copies */
+ if (info->array.raid_disks % (info->array.layout & 0xff))
+ return "RAID10 layout too complex for Grow operation";
+
+ new_disks = (info->array.raid_disks
+ / (info->array.layout & 0xff));
+ if (info->delta_disks == UnSet)
+ info->delta_disks = (new_disks
+ - info->array.raid_disks);
- if (info->new_level != 0)
- return "RAID10 can only be changed to RAID0";
- new_disks = (info->array.raid_disks
- / (info->array.layout & 0xff));
- if (info->delta_disks == UnSet)
- info->delta_disks = (new_disks
- - info->array.raid_disks);
-
- if (info->delta_disks != new_disks - info->array.raid_disks)
- return "New number of raid-devices impossible for RAID10";
- if (info->new_chunk &&
- info->new_chunk != info->array.chunk_size)
- return "Cannot change chunk-size with RAID10 Grow";
-
- /* looks good */
- re->level = 0;
- re->parity = 0;
- re->before.data_disks = new_disks;
- re->after.data_disks = re->before.data_disks;
- re->before.layout = 0;
- re->backup_blocks = 0;
- return NULL;
+ if (info->delta_disks != new_disks - info->array.raid_disks)
+ return "New number of raid-devices impossible for RAID10";
+ if (info->new_chunk &&
+ info->new_chunk != info->array.chunk_size)
+ return "Cannot change chunk-size with RAID10 Grow";
+
+ /* looks good */
+ re->level = 0;
+ re->parity = 0;
+ re->before.data_disks = new_disks;
+ re->after.data_disks = re->before.data_disks;
+ re->before.layout = 0;
+ re->backup_blocks = 0;
+ return NULL;
+
+ case 10:
+ near = info->array.layout & 0xff;
+ far = (info->array.layout >> 8) & 0xff;
+ offset = info->array.layout & 0x10000;
+ if (far > 1 && !offset)
+ return "Cannot reshape RAID10 in far-mode";
+ copies = near * far;
+
+ old_chunk = info->array.chunk_size * far;
+
+ if (info->new_layout == UnSet)
+ info->new_layout = info->array.layout;
+ else {
+ near = info->new_layout & 0xff;
+ far = (info->new_layout >> 8) & 0xff;
+ offset = info->new_layout & 0x10000;
+ if (far > 1 && !offset)
+ return "Cannot reshape RAID10 to far-mode";
+ if (near * far != copies)
+ return "Cannot change number of copies"
+ " when reshaping RAID10";
+ }
+ if (info->delta_disks == UnSet)
+ info->delta_disks = 0;
+ new_disks = (info->array.raid_disks +
+ info->delta_disks);
+
+ new_chunk = info->new_chunk * far;
+
+ re->level = 10;
+ re->parity = 0;
+ re->before.layout = info->array.layout;
+ re->before.data_disks = info->array.raid_disks;
+ re->after.layout = info->new_layout;
+ re->after.data_disks = new_disks;
+ /* For RAID10 we don't do backup, and there is
+ * no need to synchronise stripes on both
+ * 'old' and 'new'. So the important
+ * number is the minimum data_offset difference
+ * which is the larger of (offset copies * chunk).
+ */
+
+ re->backup_blocks = max(old_chunk, new_chunk) / 512;
+ if (new_disks < re->before.data_disks &&
+ info->space_after < re->backup_blocks)
+ /* Reduce component size by one chunk */
+ re->new_size = (info->component_size -
+ re->backup_blocks);
+ else
+ re->new_size = info->component_size;
+ re->new_size = re->new_size * new_disks / copies;
+ return NULL;
+ default:
+ return "RAID10 can only be changed to RAID0";
+ }
case 0:
/* RAID0 can be converted to RAID10, or to RAID456 */
if (info->new_level == 10) {
return "Cannot set raid_disk when "
"converting RAID5->RAID1";
re->level = 1;
- break;
+ re->backup_blocks = 0;
+ info->new_chunk = 0;
+ return NULL;
default:
return "Impossible level change requested";
}
switch (re->level) {
case 4:
- re->after.layout = 0 ; break;
+ re->before.layout = 0;
+ re->after.layout = 0;
+ break;
case 5:
- re->after.layout = ALGORITHM_PARITY_N; break;
+ re->after.layout = ALGORITHM_PARITY_N;
+ break;
}
break;
switch (re->level) {
case 4:
- re->after.layout = 0 ; break;
+ re->before.layout = 0;
+ re->after.layout = 0;
+ break;
case 5:
- re->after.layout = ALGORITHM_PARITY_N; break;
+ re->after.layout = ALGORITHM_PARITY_N;
+ break;
}
break;
+ info->delta_disks
- delta_parity);
switch (re->level) {
- case 6: re->parity = 2; break;
+ case 6: re->parity = 2;
+ break;
case 4:
- case 5: re->parity = 1; break;
- default: re->parity = 0; break;
+ case 5: re->parity = 1;
+ break;
+ default: re->parity = 0;
+ break;
}
/* So we have a restripe operation, we need to calculate the number
* of blocks per reshape operation.
if (re->after.data_disks == re->before.data_disks &&
re->after.layout == re->before.layout &&
info->new_chunk == info->array.chunk_size) {
- /* Nothing to change */
+ /* Nothing to change, can change level immediately. */
+ re->level = info->new_level;
re->backup_blocks = 0;
return NULL;
}
if (re->after.data_disks == 1 && re->before.data_disks == 1) {
/* chunk and layout changes make no difference */
+ re->level = info->new_level;
re->backup_blocks = 0;
return NULL;
}
static int reshape_array(char *container, int fd, char *devname,
struct supertype *st, struct mdinfo *info,
int force, struct mddev_dev *devlist,
+ unsigned long long data_offset,
char *backup_file, int verbose, int forked,
int restart, int freeze_reshape);
static int reshape_container(char *container, char *devname,
int Grow_reshape(char *devname, int fd,
struct mddev_dev *devlist,
+ unsigned long long data_offset,
struct context *c, struct shape *s)
{
/* Make some changes in the shape of an array.
int frozen;
int changed = 0;
char *container = NULL;
- char container_buf[20];
int cfd = -1;
struct mddev_dev *dv;
struct mdinfo info;
struct mdinfo *sra;
+
if (ioctl(fd, GET_ARRAY_INFO, &array) < 0) {
- pr_err("%s is not an active md array - aborting\n",
+ fprintf(stderr, Name ": %s is not an active md array - aborting\n",
devname);
return 1;
}
+ if (data_offset != INVALID_SECTORS && array.level != 10) {
+ pr_err("--grow --data-offset not yet supported\n");
+ return 1;
+ }
if (s->size > 0 &&
(s->chunk || s->level!= UnSet || s->layout_str || s->raiddisks)) {
* pre-requisite spare devices (mdmon owns final validation)
*/
if (st->ss->external) {
- int container_dev;
int rv;
if (subarray) {
- container_dev = st->container_dev;
- cfd = open_dev_excl(st->container_dev);
+ container = st->container_devnm;
+ cfd = open_dev_excl(st->container_devnm);
} else {
- container_dev = st->devnum;
+ container = st->devnm;
close(fd);
- cfd = open_dev_excl(st->devnum);
+ cfd = open_dev_excl(st->devnm);
fd = cfd;
}
if (cfd < 0) {
return 1;
}
- fmt_devname(container_buf, container_dev);
- container = container_buf;
-
rv = st->ss->load_container(st, cfd, NULL);
if (rv) {
pr_err("cannot reshape arrays in"
" container with unsupported"
" metadata: %s(%s)\n",
- devname, container_buf);
+ devname, container);
sysfs_free(cc);
free(subarray);
return 1;
}
sysfs_free(cc);
}
- if (mdmon_running(container_dev))
+ if (mdmon_running(container))
st->update_tail = &st->updates;
}
return 1;
}
- sra = sysfs_read(fd, 0, GET_LEVEL | GET_DISKS | GET_DEVS
+ sra = sysfs_read(fd, NULL, GET_LEVEL | GET_DISKS | GET_DEVS
| GET_STATE | GET_VERSION);
if (sra) {
if (st->ss->external && subarray == NULL) {
}
/* ========= set size =============== */
- if (s->size > 0 && (s->size == MAX_DISKS || s->size != (unsigned)array.size)) {
+ if (s->size > 0 && (s->size == MAX_SIZE || s->size != (unsigned)array.size)) {
unsigned long long orig_size = get_component_size(fd)/2;
unsigned long long min_csize;
struct mdinfo *mdi;
if (orig_size == 0)
orig_size = (unsigned) array.size;
+ if (orig_size == 0) {
+ pr_err("Cannot set device size in this type of array.\n");
+ rv = 1;
+ goto release;
+ }
+
if (reshape_super(st, s->size, UnSet, UnSet, 0, 0, UnSet, NULL,
devname, APPLY_METADATA_CHANGES, c->verbose > 0)) {
rv = 1;
}
/* make sure mdmon is
* aware of the new level */
- if (!mdmon_running(st->container_dev))
- start_mdmon(st->container_dev);
+ if (!mdmon_running(st->container_devnm))
+ start_mdmon(st->container_devnm);
ping_monitor(container);
- if (mdmon_running(st->container_dev) &&
+ if (mdmon_running(st->container_devnm) &&
st->update_tail == NULL)
st->update_tail = &st->updates;
}
- array.size = s->size == MAX_SIZE ? 0 : s->size;
+ if (s->size == MAX_SIZE)
+ s->size = 0;
+ array.size = s->size;
if ((unsigned)array.size != s->size) {
/* got truncated to 32bit, write to
* component_size instead
goto release;
}
if (s->assume_clean) {
- /* This will fail on kernels newer than 3.0 unless
+ /* This will fail on kernels older than 3.0 unless
* a backport has been arranged.
*/
if (sra == NULL ||
sysfs_set_str(sra, NULL, "resync_start", "none") < 0)
- pr_err("--assume-clean not support with --grow on this kernel\n");
+ pr_err("--assume-clean not supported with --grow on this kernel\n");
}
ioctl(fd, GET_ARRAY_INFO, &array);
s->size = get_component_size(fd)/2;
memset(&info, 0, sizeof(info));
info.array = array;
- sysfs_init(&info, fd, NoMdDev);
+ sysfs_init(&info, fd, NULL);
strcpy(info.text_version, sra->text_version);
info.component_size = s->size*2;
info.new_level = s->level;
/* Impose these changes on a single array. First
* check that the metadata is OK with the change. */
- if (reshape_super(st, -1, info.new_level,
+ if (reshape_super(st, 0, info.new_level,
info.new_layout, info.new_chunk,
info.array.raid_disks, info.delta_disks,
c->backup_file, devname, APPLY_METADATA_CHANGES,
}
sync_metadata(st);
rv = reshape_array(container, fd, devname, st, &info, c->force,
- devlist, c->backup_file, c->verbose, 0, 0, 0);
+ devlist, data_offset, c->backup_file, c->verbose,
+ 0, 0, 0);
frozen = 0;
}
release:
return ret_val;
}
+static int raid10_reshape(char *container, int fd, char *devname,
+ struct supertype *st, struct mdinfo *info,
+ struct reshape *reshape,
+ unsigned long long data_offset,
+ int force, int verbose)
+{
+ /* Changing raid_disks, layout, chunksize or possibly
+ * just data_offset for a RAID10.
+ * We must always change data_offset. We change by at least
+ * ->backup_blocks which is the largest of the old and new
+ * chunk sizes.
+ * If raid_disks is increasing, then data_offset must decrease
+ * by at least this copy size.
+ * If raid_disks is unchanged, data_offset must increase or
+ * decrease by at least backup_blocks but preferably by much more.
+ * We choose half of the available space.
+ * If raid_disks is decreasing, data_offset must increase by
+ * at least backup_blocks. To allow of this, component_size
+ * must be decreased by the same amount.
+ *
+ * So we calculate the required minimum and direction, possibly
+ * reduce the component_size, then iterate through the devices
+ * and set the new_data_offset.
+ * If that all works, we set chunk_size, layout, raid_disks, and start
+ * 'reshape'
+ */
+ struct mdinfo *sra, *sd;
+ unsigned long long min;
+ int dir = 0;
+ int err = 0;
+
+ sra = sysfs_read(fd, NULL,
+ GET_COMPONENT|GET_DEVS|GET_OFFSET|GET_STATE|GET_CHUNK
+ );
+ if (!sra) {
+ fprintf(stderr, Name ": %s: Cannot get array details from sysfs\n",
+ devname);
+ goto release;
+ }
+ min = reshape->backup_blocks;
+
+ if (info->delta_disks)
+ sysfs_set_str(sra, NULL, "reshape_direction",
+ info->delta_disks < 0 ? "backwards" : "forwards");
+ if (info->delta_disks < 0 &&
+ info->space_after < reshape->backup_blocks) {
+ int rv = sysfs_set_num(sra, NULL, "component_size",
+ (sra->component_size -
+ reshape->backup_blocks)/2);
+ if (rv) {
+ fprintf(stderr, Name ": cannot reduce component size\n");
+ goto release;
+ }
+ }
+ for (sd = sra->devs; sd; sd = sd->next) {
+ char *dn;
+ int dfd;
+ int rv;
+ struct supertype *st2;
+ struct mdinfo info2;
+
+ if (sd->disk.state & (1<<MD_DISK_FAULTY))
+ continue;
+ dn = map_dev(sd->disk.major, sd->disk.minor, 0);
+ dfd = dev_open(dn, O_RDONLY);
+ if (dfd < 0) {
+ fprintf(stderr,
+ Name ": %s: cannot open component %s\n",
+ devname, dn ? dn : "-unknown-");
+ rv = -1;
+ goto release;
+ }
+ st2 = dup_super(st);
+ rv = st2->ss->load_super(st2,dfd, NULL);
+ close(dfd);
+ if (rv) {
+ free(st2);
+ fprintf(stderr, ": %s: cannot get superblock from %s\n",
+ devname, dn);
+ goto release;
+ }
+ st2->ss->getinfo_super(st2, &info2, NULL);
+ st2->ss->free_super(st2);
+ free(st2);
+ if (info->delta_disks < 0) {
+ /* Don't need any space as array is shrinking
+ * just move data_offset up by min
+ */
+ if (data_offset == 1)
+ info2.new_data_offset = info2.data_offset + min;
+ else {
+ if ((unsigned long long)data_offset
+ < info2.data_offset + min) {
+ fprintf(stderr, Name ": --data-offset too small for %s\n",
+ dn);
+ goto release;
+ }
+ info2.new_data_offset = data_offset;
+ }
+ } else if (info->delta_disks > 0) {
+ /* need space before */
+ if (info2.space_before < min) {
+ fprintf(stderr, Name ": Insufficient head-space for reshape on %s\n",
+ dn);
+ goto release;
+ }
+ if (data_offset == 1)
+ info2.new_data_offset = info2.data_offset - min;
+ else {
+ if ((unsigned long long)data_offset
+ > info2.data_offset - min) {
+ fprintf(stderr, Name ": --data-offset too large for %s\n",
+ dn);
+ goto release;
+ }
+ info2.new_data_offset = data_offset;
+ }
+ } else {
+ if (dir == 0) {
+ /* can move up or down. 'data_offset'
+ * might guide us, otherwise choose
+ * direction with most space
+ */
+ if (data_offset == 1) {
+ if (info2.space_before > info2.space_after)
+ dir = -1;
+ else
+ dir = 1;
+ } else if (data_offset < info2.data_offset)
+ dir = -1;
+ else
+ dir = 1;
+ sysfs_set_str(sra, NULL, "reshape_direction",
+ dir == 1 ? "backwards" : "forwards");
+ }
+ switch (dir) {
+ case 1: /* Increase data offset */
+ if (info2.space_after < min) {
+ fprintf(stderr, Name ": Insufficient tail-space for reshape on %s\n",
+ dn);
+ goto release;
+ }
+ if (data_offset != 1 &&
+ data_offset < info2.data_offset + min) {
+ fprintf(stderr, Name ": --data-offset too small on %s\n",
+ dn);
+ goto release;
+ }
+ if (data_offset != 1)
+ info2.new_data_offset = data_offset;
+ else {
+ unsigned long long off =
+ info2.space_after / 2;
+ off &= ~7ULL;
+ if (off < min)
+ off = min;
+ info2.new_data_offset =
+ info2.data_offset + off;
+ }
+ break;
+ case -1: /* Decrease data offset */
+ if (info2.space_before < min) {
+ fprintf(stderr, Name ": insufficient head-room on %s\n",
+ dn);
+ goto release;
+ }
+ if (data_offset != 1 &&
+ data_offset < info2.data_offset - min) {
+ fprintf(stderr, Name ": --data-offset too small on %s\n",
+ dn);
+ goto release;
+ }
+ if (data_offset != 1)
+ info2.new_data_offset = data_offset;
+ else {
+ unsigned long long off =
+ info2.space_before / 2;
+ off &= ~7ULL;
+ if (off < min)
+ off = min;
+ info2.new_data_offset =
+ info2.data_offset - off;
+ }
+ break;
+ }
+ }
+ if (sysfs_set_num(sra, sd, "new_offset",
+ info2.new_data_offset) < 0) {
+ err = errno;
+ fprintf(stderr, Name ": Cannot set new_offset for %s\n",
+ dn);
+ break;
+ }
+ }
+ if (!err && sysfs_set_num(sra, NULL, "chunk_size", info->new_chunk) < 0)
+ err = errno;
+ if (!err && sysfs_set_num(sra, NULL, "layout", reshape->after.layout) < 0)
+ err = errno;
+ if (!err && sysfs_set_num(sra, NULL, "raid_disks",
+ info->array.raid_disks + info->delta_disks) < 0)
+ err = errno;
+ if (!err && sysfs_set_str(sra, NULL, "sync_action", "reshape") < 0)
+ err = errno;
+ if (err) {
+ fprintf(stderr, Name ": Cannot set array shape for %s\n",
+ devname);
+ if (err == EBUSY &&
+ (info->array.state & (1<<MD_SB_BITMAP_PRESENT)))
+ fprintf(stderr,
+ " Bitmap must be removed before"
+ " shape can be changed\n");
+ goto release;
+ }
+ sysfs_free(sra);
+ return 0;
+release:
+ sysfs_free(sra);
+ return 1;
+}
+
+static void get_space_after(int fd, struct supertype *st, struct mdinfo *info)
+{
+ struct mdinfo *sra, *sd;
+ /* Initialisation to silence compiler warning */
+ unsigned long long min_space_before = 0, min_space_after = 0;
+ int first = 1;
+
+ sra = sysfs_read(fd, NULL, GET_DEVS);
+ if (!sra)
+ return;
+ for (sd = sra->devs; sd; sd = sd->next) {
+ char *dn;
+ int dfd;
+ struct supertype *st2;
+ struct mdinfo info2;
+
+ if (sd->disk.state & (1<<MD_DISK_FAULTY))
+ continue;
+ dn = map_dev(sd->disk.major, sd->disk.minor, 0);
+ dfd = dev_open(dn, O_RDONLY);
+ if (dfd < 0)
+ break;
+ st2 = dup_super(st);
+ if (st2->ss->load_super(st2,dfd, NULL)) {
+ close(dfd);
+ free(st2);
+ break;
+ }
+ close(dfd);
+ st2->ss->getinfo_super(st2, &info2, NULL);
+ st2->ss->free_super(st2);
+ free(st2);
+ if (first ||
+ min_space_before > info2.space_before)
+ min_space_before = info2.space_before;
+ if (first ||
+ min_space_after > info2.space_after)
+ min_space_after = info2.space_after;
+ first = 0;
+ }
+ if (sd == NULL && !first) {
+ info->space_after = min_space_after;
+ info->space_before = min_space_before;
+ }
+ sysfs_free(sra);
+}
+
static int reshape_array(char *container, int fd, char *devname,
struct supertype *st, struct mdinfo *info,
int force, struct mddev_dev *devlist,
+ unsigned long long data_offset,
char *backup_file, int verbose, int forked,
int restart, int freeze_reshape)
{
char *msg;
int orig_level = UnSet;
int disks, odisks;
+ int delayed;
struct mdu_array_info_s array;
char *c;
info->component_size = array_size / array.raid_disks;
}
+ if (array.level == 10)
+ /* Need space_after info */
+ get_space_after(fd, st, info);
+
if (info->reshape_active) {
int new_level = info->new_level;
info->new_level = UnSet;
if (reshape.level > 0 && st->ss->external) {
/* make sure mdmon is aware of the new level */
- if (mdmon_running(st->container_dev))
+ if (mdmon_running(container))
flush_mdmon(container);
- if (!mdmon_running(st->container_dev))
- start_mdmon(st->container_dev);
+ if (!mdmon_running(container))
+ start_mdmon(container);
ping_monitor(container);
- if (mdmon_running(st->container_dev) &&
+ if (mdmon_running(container) &&
st->update_tail == NULL)
st->update_tail = &st->updates;
}
struct mdinfo *d;
if (info2) {
- sysfs_init(info2, fd, st->devnum);
+ sysfs_init(info2, fd, st->devnm);
/* When increasing number of devices, we need to set
* new raid_disks before adding these, or they might
* be rejected.
* - request the shape change.
* - fork to handle backup etc.
*/
-started:
/* Check that we can hold all the data */
get_dev_size(fd, NULL, &array_size);
if (reshape.new_size < (array_size/512)) {
goto release;
}
- sra = sysfs_read(fd, 0,
+started:
+
+ if (array.level == 10) {
+ /* Reshaping RAID10 does not require and data backup by
+ * user-space. Instead it requires that the data_offset
+ * is changed to avoid the need for backup.
+ * So this is handled very separately
+ */
+ if (restart)
+ /* Nothing to do. */
+ return 0;
+ return raid10_reshape(container, fd, devname, st, info,
+ &reshape, data_offset,
+ force, verbose);
+ }
+ sra = sysfs_read(fd, NULL,
GET_COMPONENT|GET_DEVS|GET_OFFSET|GET_STATE|GET_CHUNK|
GET_CACHE);
if (!sra) {
break;
}
+ /* If another array on the same devices is busy, the
+ * reshape will wait for them. This would mean that
+ * the first section that we suspend will stay suspended
+ * for a long time. So check on that possibility
+ * by looking for "DELAYED" in /proc/mdstat, and if found,
+ * wait a while
+ */
+ do {
+ struct mdstat_ent *mds, *m;
+ delayed = 0;
+ mds = mdstat_read(0, 0);
+ for (m = mds; m; m = m->next)
+ if (strcmp(m->devnm, sra->sys_name) == 0) {
+ if (m->resync &&
+ m->percent == RESYNC_DELAYED)
+ delayed = 1;
+ if (m->resync == 0)
+ /* Haven't started the reshape thread
+ * yet, wait a bit
+ */
+ delayed = 2;
+ break;
+ }
+ free_mdstat(mds);
+ if (delayed == 1 && get_linux_version() < 3007000) {
+ pr_err("Reshape is delayed, but cannot wait carefully with this kernel.\n"
+ " You might experience problems until other reshapes complete.\n");
+ delayed = 0;
+ }
+ if (delayed)
+ sleep(30 - (delayed-1) * 25);
+ } while (delayed);
+
close(fd);
if (check_env("MDADM_GROW_VERIFY"))
fd = open(devname, O_RDONLY | O_DIRECT);
if (st->ss->external) {
/* Re-load the metadata as much could have changed */
- int cfd = open_dev(st->container_dev);
+ int cfd = open_dev(st->container_devnm);
if (cfd >= 0) {
flush_mdmon(container);
st->ss->free_super(st);
{
struct mdinfo *cc = NULL;
int rv = restart;
- int last_devnum = -1;
+ char last_devnm[32] = "";
/* component_size is not meaningful for a container,
- * so pass '-1' meaning 'no change'
+ * so pass '0' meaning 'no change'
*/
if (!restart &&
- reshape_super(st, -1, info->new_level,
+ reshape_super(st, 0, info->new_level,
info->new_layout, info->new_chunk,
info->array.raid_disks, info->delta_disks,
backup_file, devname, APPLY_METADATA_CHANGES,
int fd;
struct mdstat_ent *mdstat;
char *adev;
+ int devid;
sysfs_free(cc);
continue;
subarray = strchr(content->text_version+1, '/')+1;
- mdstat = mdstat_by_subdev(subarray,
- devname2devnum(container));
+ mdstat = mdstat_by_subdev(subarray, container);
if (!mdstat)
continue;
if (mdstat->active == 0) {
- pr_err("Skipping inactive "
- "array md%i.\n", mdstat->devnum);
+ pr_err("Skipping inactive array %s.\n",
+ mdstat->devnm);
free_mdstat(mdstat);
mdstat = NULL;
continue;
if (!content)
break;
- adev = map_dev(dev2major(mdstat->devnum),
- dev2minor(mdstat->devnum),
- 0);
+ devid = devnm2devid(mdstat->devnm);
+ adev = map_dev(major(devid), minor(devid), 0);
if (!adev)
adev = content->text_version;
- fd = open_dev(mdstat->devnum);
+ fd = open_dev(mdstat->devnm);
if (fd < 0) {
printf(Name ": Device %s cannot be opened for reshape.",
adev);
break;
}
- if (last_devnum == mdstat->devnum) {
+ if (strcmp(last_devnm, mdstat->devnm) == 0) {
/* Do not allow for multiple reshape_array() calls for
* the same array.
* It can happen when reshape_array() returns without
close(fd);
break;
}
- last_devnum = mdstat->devnum;
+ strcpy(last_devnm, mdstat->devnm);
- sysfs_init(content, fd, mdstat->devnum);
+ sysfs_init(content, fd, mdstat->devnm);
- if (mdmon_running(devname2devnum(container)))
+ if (mdmon_running(container))
flush_mdmon(container);
rv = reshape_array(container, fd, adev, st,
- content, force, NULL,
+ content, force, NULL, 0ULL,
backup_file, verbose, 1, restart,
freeze_reshape);
close(fd);
if (rv)
break;
- if (mdmon_running(devname2devnum(container)))
+ if (mdmon_running(container))
flush_mdmon(container);
}
if (!rv)
(unsigned long)__le64_to_cpu(bsb.mtime),
(unsigned long)info->array.utime);
} else {
- if (verbose)
- pr_err("too-old timestamp on "
- "backup-metadata on %s\n", devname);
+ pr_err("too-old timestamp on backup-metadata on %s\n", devname);
+ pr_err("If you think it is should be safe, try 'export MDADM_GROW_ALLOW_OLD=1'\n");
continue; /* time stamp is too bad */
}
}
}
}
for (j=0; j<info->array.raid_disks; j++) {
- if (fdlist[j] < 0) continue;
+ if (fdlist[j] < 0)
+ continue;
if (st->ss->load_super(st, fdlist[j], NULL))
continue;
st->ss->getinfo_super(st, &dinfo, NULL);
char *subarray = NULL;
struct mdinfo *cc = NULL;
struct mdstat_ent *mdstat = NULL;
- char buf[40];
int cfd = -1;
int fd2 = -1;
}
dprintf("Grow continue is run for ");
if (st->ss->external == 0) {
+ int d;
dprintf("native array (%s)\n", devname);
- if (ioctl(fd, GET_ARRAY_INFO, &array) < 0) {
+ if (ioctl(fd, GET_ARRAY_INFO, &array.array) < 0) {
pr_err("%s is not an active md array -"
" aborting\n", devname);
ret_val = 1;
goto Grow_continue_command_exit;
}
content = &array;
- sysfs_init(content, fd, st->devnum);
+ /* Need to load a superblock.
+ * FIXME we should really get what we need from
+ * sysfs
+ */
+ for (d = 0; d < MAX_DISKS; d++) {
+ mdu_disk_info_t disk;
+ char *dv;
+ int err;
+ disk.number = d;
+ if (ioctl(fd, GET_DISK_INFO, &disk) < 0)
+ continue;
+ if (disk.major == 0 && disk.minor == 0)
+ continue;
+ if ((disk.state & (1 << MD_DISK_ACTIVE)) == 0)
+ continue;
+ dv = map_dev(disk.major, disk.minor, 1);
+ if (!dv)
+ continue;
+ fd2 = dev_open(dv, O_RDONLY);
+ if (fd2 < 0)
+ continue;
+ err = st->ss->load_super(st, fd2, NULL);
+ close(fd2);
+ if (err)
+ continue;
+ break;
+ }
+ if (d == MAX_DISKS) {
+ pr_err("Unable to load metadata for %s\n",
+ devname);
+ ret_val = 1;
+ goto Grow_continue_command_exit;
+ }
+ st->ss->getinfo_super(st, content, NULL);
} else {
- int container_dev;
+ char *container;
if (subarray) {
dprintf("subarray (%s)\n", subarray);
- container_dev = st->container_dev;
- cfd = open_dev_excl(st->container_dev);
+ container = st->container_devnm;
+ cfd = open_dev_excl(st->container_devnm);
} else {
- container_dev = st->devnum;
+ container = st->devnm;
close(fd);
- cfd = open_dev_excl(st->devnum);
- dprintf("container (%i)\n", container_dev);
+ cfd = open_dev_excl(st->devnm);
+ dprintf("container (%s)\n", container);
fd = cfd;
}
if (cfd < 0) {
ret_val = 1;
goto Grow_continue_command_exit;
}
- fmt_devname(buf, container_dev);
/* find in container array under reshape
*/
pr_err("cannot continue reshape of an array"
" in container with unsupported"
" metadata: %s(%s)\n",
- devname, buf);
+ devname, container);
ret_val = 1;
goto Grow_continue_command_exit;
}
array = strchr(content->text_version+1, '/')+1;
- mdstat = mdstat_by_subdev(array, container_dev);
+ mdstat = mdstat_by_subdev(array, container);
if (!mdstat)
continue;
if (mdstat->active == 0) {
- pr_err("Skipping inactive "
- "array md%i.\n", mdstat->devnum);
+ pr_err("Skipping inactive array %s.\n",
+ mdstat->devnm);
free_mdstat(mdstat);
mdstat = NULL;
continue;
ret_val = 1;
goto Grow_continue_command_exit;
}
- fd2 = open_dev(mdstat->devnum);
+ fd2 = open_dev(mdstat->devnm);
if (fd2 < 0) {
- pr_err("cannot open (md%i)\n",
- mdstat->devnum);
+ pr_err("cannot open (%s)\n", mdstat->devnm);
ret_val = 1;
goto Grow_continue_command_exit;
}
- sysfs_init(content, fd2, mdstat->devnum);
+ sysfs_init(content, fd2, mdstat->devnm);
/* start mdmon in case it is not running
*/
- if (!mdmon_running(container_dev))
- start_mdmon(container_dev);
- ping_monitor(buf);
+ if (!mdmon_running(container))
+ start_mdmon(container);
+ ping_monitor(container);
- if (mdmon_running(container_dev))
+ if (mdmon_running(container))
st->update_tail = &st->updates;
else {
pr_err("No mdmon found. "
/* verify that array under reshape is started from
* correct position
*/
- if (verify_reshape_position(content,
- map_name(pers, mdstat->level)) < 0) {
+ if (verify_reshape_position(content, content->array.level) < 0) {
ret_val = 1;
goto Grow_continue_command_exit;
}
return ret_val;
if (st->ss->external) {
- char container[40];
- int cfd = open_dev(st->container_dev);
+ int cfd = open_dev(st->container_devnm);
if (cfd < 0)
return 1;
- fmt_devname(container, st->container_dev);
- st->ss->load_container(st, cfd, container);
+ st->ss->load_container(st, cfd, st->container_devnm);
close(cfd);
- ret_val = reshape_container(container, NULL, mdfd,
+ ret_val = reshape_container(st->container_devnm, NULL, mdfd,
st, info, 0, backup_file,
0, 1, freeze_reshape);
} else
ret_val = reshape_array(NULL, mdfd, "array", st, info, 1,
- NULL, backup_file, 0, 0, 1,
+ NULL, 0ULL, backup_file, 0, 0, 1,
freeze_reshape);
return ret_val;