if (offset_setable) {
st->ss->getinfo_super(st, mdi, NULL);
sysfs_init(mdi, fd, -1);
- rv = sysfs_set_num(mdi, NULL, "bitmap/location",
- mdi->bitmap_offset);
+ rv = sysfs_set_num_signed(mdi, NULL, "bitmap/location",
+ mdi->bitmap_offset);
} else {
array.state |= (1<<MD_SB_BITMAP_PRESENT);
rv = ioctl(fd, SET_ARRAY_INFO, &array);
if (sra)
sysfs_set_str(sra, NULL, "sync_action", "idle");
- else
- fprintf(stderr, Name ": failed to unfreeze array\n");
sysfs_free(sra);
}
}
static int reshape_super(struct supertype *st, long long size, int level,
int layout, int chunksize, int raid_disks,
int delta_disks, char *backup_file, char *dev,
- int verbose)
+ int direction, int verbose)
{
/* nothing extra to check in the native case */
if (!st->ss->external)
return st->ss->reshape_super(st, size, level, layout, chunksize,
raid_disks, delta_disks, backup_file, dev,
- verbose);
+ direction, verbose);
}
static void sync_metadata(struct supertype *st)
return NULL;
}
+static int set_array_size(struct supertype *st, struct mdinfo *sra,
+ char *text_version)
+{
+ struct mdinfo *info;
+ char *subarray;
+ int ret_val = -1;
+
+ if ((st == NULL) || (sra == NULL))
+ return ret_val;
+
+ if (text_version == NULL)
+ text_version = sra->text_version;
+ subarray = strchr(text_version+1, '/')+1;
+ info = st->ss->container_content(st, subarray);
+ if (info) {
+ unsigned long long current_size = 0;
+ unsigned long long new_size =
+ info->custom_array_size/2;
+
+ if (sysfs_get_ll(sra, NULL, "array_size", ¤t_size) == 0 &&
+ new_size > current_size) {
+ if (sysfs_set_num(sra, NULL, "array_size", new_size)
+ < 0)
+ dprintf("Error: Cannot set array size");
+ else {
+ ret_val = 0;
+ dprintf("Array size changed");
+ }
+ dprintf(" from %llu to %llu.\n",
+ current_size, new_size);
+ }
+ sysfs_free(info);
+ } else
+ dprintf("Error: set_array_size(): info pointer in NULL\n");
+
+ return ret_val;
+}
+
static int reshape_array(char *container, int fd, char *devname,
struct supertype *st, struct mdinfo *info,
int force, struct mddev_dev *devlist,
long long orig_size = get_component_size(fd)/2;
long long min_csize;
struct mdinfo *mdi;
+ int raid0_takeover = 0;
if (orig_size == 0)
orig_size = array.size;
if (reshape_super(st, size, UnSet, UnSet, 0, 0, UnSet, NULL,
- devname, !quiet)) {
+ devname, APPLY_METADATA_CHANGES, !quiet)) {
rv = 1;
goto release;
}
sync_metadata(st);
+ if (st->ss->external) {
+ /* metadata can have size limitation
+ * update size value according to metadata information
+ */
+ struct mdinfo *sizeinfo =
+ st->ss->container_content(st, subarray);
+ if (sizeinfo) {
+ unsigned long long new_size =
+ sizeinfo->custom_array_size/2;
+ int data_disks = get_data_disks(
+ sizeinfo->array.level,
+ sizeinfo->array.layout,
+ sizeinfo->array.raid_disks);
+ new_size /= data_disks;
+ dprintf("Metadata size correction from %llu to "
+ "%llu (%llu)\n", orig_size, new_size,
+ new_size * data_disks);
+ size = new_size;
+ sysfs_free(sizeinfo);
+ }
+ }
/* Update the size of each member device in case
* they have been resized. This will never reduce
* understands '0' to mean 'max'.
*/
min_csize = 0;
+ rv = 0;
for (mdi = sra->devs; mdi; mdi = mdi->next) {
- if (sysfs_set_num(sra, mdi, "size", size) < 0)
+ if (sysfs_set_num(sra, mdi, "size", size) < 0) {
+ /* Probably kernel refusing to let us
+ * reduce the size - not an error.
+ */
break;
+ }
if (array.not_persistent == 0 &&
array.major_version == 0 &&
get_linux_version() < 3001000) {
}
}
}
+ if (rv) {
+ fprintf(stderr, Name ": Cannot set size on "
+ "array members.\n");
+ goto size_change_error;
+ }
if (min_csize && size > min_csize) {
fprintf(stderr, Name ": Cannot safely make this array "
"use more than 2TB per device on this kernel.\n");
rv = 1;
- goto release;
+ goto size_change_error;
}
if (min_csize && size == 0) {
/* Don't let the kernel choose a size - it will get
"2TB per device\n");
size = min_csize;
}
+ if (st->ss->external) {
+ if (sra->array.level == 0) {
+ rv = sysfs_set_str(sra, NULL, "level",
+ "raid5");
+ if (!rv) {
+ raid0_takeover = 1;
+ /* get array parametes after takeover
+ * to chane one parameter at time only
+ */
+ rv = ioctl(fd, GET_ARRAY_INFO, &array);
+ }
+ }
+ /* make sure mdmon is
+ * aware of the new level */
+ if (!mdmon_running(st->container_dev))
+ start_mdmon(st->container_dev);
+ ping_monitor(container);
+ if (mdmon_running(st->container_dev) &&
+ st->update_tail == NULL)
+ st->update_tail = &st->updates;
+ }
array.size = size;
if (array.size != size) {
"component_size", size);
else
rv = -1;
- } else
+ } else {
rv = ioctl(fd, SET_ARRAY_INFO, &array);
+
+ /* manage array size when it is managed externally
+ */
+ if ((rv == 0) && st->ss->external)
+ rv = set_array_size(st, sra, sra->text_version);
+ }
+
+ if (raid0_takeover) {
+ /* do not recync non-existing parity,
+ * we will drop it anyway
+ */
+ sysfs_set_str(sra, NULL, "sync_action", "frozen");
+ /* go back to raid0, drop parity disk
+ */
+ sysfs_set_str(sra, NULL, "level", "raid0");
+ ioctl(fd, GET_ARRAY_INFO, &array);
+ }
+
+size_change_error:
if (rv != 0) {
int err = errno;
/* restore metadata */
if (reshape_super(st, orig_size, UnSet, UnSet, 0, 0,
- UnSet, NULL, devname, !quiet) == 0)
+ UnSet, NULL, devname,
+ ROLLBACK_METADATA_CHANGES,
+ !quiet) == 0)
sync_metadata(st);
fprintf(stderr, Name ": Cannot set device size for %s: %s\n",
devname, strerror(err));
*h = 0;
info.new_layout = map_name(r6layout, l);
}
+ } else {
+ fprintf(stderr, Name
+ ": %s is only meaningful when reshaping"
+ " a RAID6 array.\n", layout_str);
+ rv = 1;
+ goto release;
}
} else if (strcmp(layout_str, "preserve") == 0) {
- info.new_layout = UnSet;
+ /* This means that a non-standard RAID6 layout
+ * is OK.
+ * In particular:
+ * - When reshape a RAID6 (e.g. adding a device)
+ * which is in a non-standard layout, it is OK
+ * to preserve that layout.
+ * - When converting a RAID5 to RAID6, leave it in
+ * the XXX-6 layout, don't re-layout.
+ */
+ if (info.array.level == 6 && info.new_level == UnSet)
+ info.new_layout = info.array.layout;
+ else if (info.array.level == 5 && info.new_level == 6) {
+ char l[40];
+ strcpy(l, map_num(r5layout, info.array.layout));
+ strcat(l, "-6");
+ info.new_layout = map_name(r6layout, l);
+ } else {
+ fprintf(stderr, Name
+ ": %s in only meaningful when reshaping"
+ " to RAID6\n", layout_str);
+ rv = 1;
+ goto release;
+ }
} else {
int l = info.new_level;
if (l == UnSet)
/* Impose these changes on a single array. First
* check that the metadata is OK with the change. */
- if (reshape_super(st, info.component_size, info.new_level,
+ if (reshape_super(st, -1, info.new_level,
info.new_layout, info.new_chunk,
info.array.raid_disks, info.delta_disks,
- backup_file, devname, quiet)) {
+ backup_file, devname, APPLY_METADATA_CHANGES,
+ quiet)) {
rv = 1;
goto release;
}
*/
if (reshape.before.data_disks !=
reshape.after.data_disks &&
- info->custom_array_size) {
- struct mdinfo *info2;
- char *subarray = strchr(info->text_version+1, '/')+1;
-
- info2 = st->ss->container_content(st, subarray);
- if (info2) {
- unsigned long long current_size = 0;
- unsigned long long new_size =
- info2->custom_array_size/2;
-
- if (sysfs_get_ll(sra,
- NULL,
- "array_size",
- ¤t_size) == 0 &&
- new_size > current_size) {
- if (sysfs_set_num(sra, NULL,
- "array_size", new_size)
- < 0)
- dprintf("Error: Cannot"
- " set array size");
- else
- dprintf("Array size "
- "changed");
- dprintf(" from %llu to %llu.\n",
- current_size, new_size);
- }
- sysfs_free(info2);
- }
- }
+ info->custom_array_size)
+ set_array_size(st, info, info->text_version);
if (info->new_level != reshape.level) {
reshape_super(st, -1, info->new_level,
info->new_layout, info->new_chunk,
info->array.raid_disks, info->delta_disks,
- backup_file, devname, quiet)) {
+ backup_file, devname, APPLY_METADATA_CHANGES,
+ quiet)) {
unfreeze(st);
return 1;
}