X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=Grow.c;h=8aa0a1392241a6afd052d3cd42a7d3b4108422dd;hp=9fa2d6b420aae39f37373c22f527e3e967610520;hb=503975b9d5f0696b5d2ee20ea903b859e3f60662;hpb=cc7f63e55319b5c372af20ce528e7e7230746d92 diff --git a/Grow.c b/Grow.c index 9fa2d6b4..8aa0a139 100644 --- a/Grow.c +++ b/Grow.c @@ -49,12 +49,8 @@ int restore_backup(struct supertype *st, int disk_count = next_spare + working_disks; dprintf("Called restore_backup()\n"); - fdlist = malloc(sizeof(int) * disk_count); - if (fdlist == NULL) { - fprintf(stderr, - Name ": cannot allocate memory for disk list\n"); - return 1; - } + fdlist = xmalloc(sizeof(int) * disk_count); + for (i = 0; i < next_spare; i++) fdlist[i] = -1; for (dev = content->devs; dev; dev = dev->next) { @@ -84,10 +80,10 @@ int restore_backup(struct supertype *st, } free(fdlist); if (err) { - fprintf(stderr, Name ": Failed to restore critical" - " section for reshape - sorry.\n"); + pr_err("Failed to restore critical" + " section for reshape - sorry.\n"); if (!backup_file) - fprintf(stderr, Name ": Possibly you need" + pr_err("Possibly you need" " to specify a --backup-file\n"); return 1; } @@ -115,36 +111,37 @@ int Grow_Add_device(char *devname, int fd, char *newdev) char *subarray = NULL; if (ioctl(fd, GET_ARRAY_INFO, &info.array) < 0) { - fprintf(stderr, Name ": cannot get array info for %s\n", devname); + pr_err("cannot get array info for %s\n", devname); return 1; } if (info.array.level != -1) { - fprintf(stderr, Name ": can only add devices to linear arrays\n"); + pr_err("can only add devices to linear arrays\n"); return 1; } st = super_by_fd(fd, &subarray); if (!st) { - fprintf(stderr, Name ": 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; } if (subarray) { - fprintf(stderr, Name ": Cannot grow linear sub-arrays yet\n"); + pr_err("Cannot grow linear sub-arrays yet\n"); free(subarray); free(st); + return 1; } nfd = open(newdev, O_RDWR|O_EXCL|O_DIRECT); if (nfd < 0) { - fprintf(stderr, Name ": cannot open %s\n", newdev); + pr_err("cannot open %s\n", newdev); free(st); return 1; } fstat(nfd, &stb); if ((stb.st_mode & S_IFMT) != S_IFBLK) { - fprintf(stderr, Name ": %s is not a block device!\n", newdev); + pr_err("%s is not a block device!\n", newdev); close(nfd); free(st); return 1; @@ -158,7 +155,7 @@ int Grow_Add_device(char *devname, int fd, char *newdev) disk.number = d; if (ioctl(fd, GET_DISK_INFO, &disk) < 0) { - fprintf(stderr, Name ": cannot get device detail for device %d\n", + pr_err("cannot get device detail for device %d\n", d); close(nfd); free(st); @@ -166,22 +163,22 @@ int Grow_Add_device(char *devname, int fd, char *newdev) } dv = map_dev(disk.major, disk.minor, 1); if (!dv) { - fprintf(stderr, Name ": cannot find device file for device %d\n", + pr_err("cannot find device file for device %d\n", d); close(nfd); free(st); return 1; } fd2 = dev_open(dv, O_RDWR); - if (!fd2) { - fprintf(stderr, Name ": cannot open device file %s\n", dv); + if (fd2 < 0) { + pr_err("cannot open device file %s\n", dv); close(nfd); free(st); return 1; } if (st->ss->load_super(st, fd2, NULL)) { - fprintf(stderr, Name ": cannot find super block on %s\n", dv); + pr_err("cannot find super block on %s\n", dv); close(nfd); close(fd2); free(st); @@ -202,7 +199,7 @@ int Grow_Add_device(char *devname, int fd, char *newdev) 0, 0, NULL); if (st->ss->store_super(st, nfd)) { - fprintf(stderr, Name ": Cannot store new superblock on %s\n", + pr_err("Cannot store new superblock on %s\n", newdev); close(nfd); return 1; @@ -210,7 +207,7 @@ int Grow_Add_device(char *devname, int fd, char *newdev) close(nfd); if (ioctl(fd, ADD_NEW_DISK, &info.disk) != 0) { - fprintf(stderr, Name ": Cannot add new disk to this array\n"); + pr_err("Cannot add new disk to this array\n"); return 1; } /* Well, that seems to have worked. @@ -218,7 +215,7 @@ int Grow_Add_device(char *devname, int fd, char *newdev) */ if (ioctl(fd, GET_ARRAY_INFO, &info.array) < 0) { - fprintf(stderr, Name ": cannot get array info for %s\n", devname); + pr_err("cannot get array info for %s\n", devname); return 1; } @@ -229,23 +226,23 @@ int Grow_Add_device(char *devname, int fd, char *newdev) disk.number = d; if (ioctl(fd, GET_DISK_INFO, &disk) < 0) { - fprintf(stderr, Name ": cannot get device detail for device %d\n", + pr_err("cannot get device detail for device %d\n", d); return 1; } dv = map_dev(disk.major, disk.minor, 1); if (!dv) { - fprintf(stderr, Name ": cannot find device file for device %d\n", + pr_err("cannot find device file for device %d\n", d); return 1; } fd2 = dev_open(dv, O_RDWR); if (fd2 < 0) { - fprintf(stderr, Name ": cannot open device file %s\n", dv); + pr_err("cannot open device file %s\n", dv); return 1; } if (st->ss->load_super(st, fd2, NULL)) { - fprintf(stderr, Name ": cannot find super block on %s\n", dv); + pr_err("cannot find super block on %s\n", dv); close(fd); return 1; } @@ -258,7 +255,7 @@ int Grow_Add_device(char *devname, int fd, char *newdev) 0, 0, NULL); if (st->ss->store_super(st, fd2)) { - fprintf(stderr, Name ": Cannot store new superblock on %s\n", dv); + pr_err("Cannot store new superblock on %s\n", dv); close(fd2); return 1; } @@ -289,7 +286,7 @@ int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int if (vers < 9003) { major = BITMAP_MAJOR_HOSTENDIAN; - fprintf(stderr, Name ": Warning - bitmaps created on this kernel" + pr_err("Warning - bitmaps created on this kernel" " are not portable\n" " between different architectures. Consider upgrading" " the Linux kernel.\n"); @@ -297,48 +294,48 @@ int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int if (ioctl(fd, GET_BITMAP_FILE, &bmf) != 0) { if (errno == ENOMEM) - fprintf(stderr, Name ": Memory allocation failure.\n"); + pr_err("Memory allocation failure.\n"); else - fprintf(stderr, Name ": bitmaps not supported by this kernel.\n"); + pr_err("bitmaps not supported by this kernel.\n"); return 1; } if (bmf.pathname[0]) { if (strcmp(file,"none")==0) { if (ioctl(fd, SET_BITMAP_FILE, -1)!= 0) { - fprintf(stderr, Name ": failed to remove bitmap %s\n", + pr_err("failed to remove bitmap %s\n", bmf.pathname); return 1; } return 0; } - fprintf(stderr, Name ": %s already has a bitmap (%s)\n", + pr_err("%s already has a bitmap (%s)\n", devname, bmf.pathname); return 1; } if (ioctl(fd, GET_ARRAY_INFO, &array) != 0) { - fprintf(stderr, Name ": cannot get array status for %s\n", devname); + pr_err("cannot get array status for %s\n", devname); return 1; } if (array.state & (1<ss->add_internal_bitmap == NULL) { - fprintf(stderr, Name ": Internal bitmaps not supported " + pr_err("Internal bitmaps not supported " "with %s metadata\n", st->ss->name); return 1; } + mdi = sysfs_read(fd, -1, GET_BITMAP_LOCATION); + if (mdi) + offset_setable = 1; for (d=0; d< st->max_devs; d++) { mdu_disk_info_t disk; char *dv; @@ -400,11 +403,13 @@ int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int if (st->ss->add_internal_bitmap( st, &chunk, delay, write_behind, - bitmapsize, 0, major) + bitmapsize, offset_setable, + major) ) st->ss->write_bitmap(st, fd2); else { - fprintf(stderr, Name ": failed to create internal bitmap - chunksize problem.\n"); + pr_err("failed " + "to create internal bitmap - chunksize problem.\n"); close(fd2); return 1; } @@ -412,13 +417,20 @@ int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int close(fd2); } } - array.state |= (1<ss->getinfo_super(st, mdi, NULL); + sysfs_init(mdi, fd, -1); + rv = sysfs_set_num_signed(mdi, NULL, "bitmap/location", + mdi->bitmap_offset); + } else { + array.state |= (1<= 0 && - st->ss->load_super(st, fd2, NULL) == 0) { + if (fd2 >= 0) { + if (st->ss->load_super(st, fd2, NULL) == 0) { + close(fd2); + st->ss->uuid_from_super(st, uuid); + break; + } close(fd2); - st->ss->uuid_from_super(st, uuid); - break; } - close(fd2); } if (d == max_devs) { - fprintf(stderr, Name ": cannot find UUID for array!\n"); + pr_err("cannot find UUID for array!\n"); return 1; } if (CreateBitmap(file, force, (char*)uuid, chunk, @@ -459,17 +472,16 @@ int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int } bitmap_fd = open(file, O_RDWR); if (bitmap_fd < 0) { - fprintf(stderr, Name ": weird: %s cannot be opened\n", + pr_err("weird: %s cannot be opened\n", file); return 1; } if (ioctl(fd, SET_BITMAP_FILE, bitmap_fd) < 0) { int err = errno; if (errno == EBUSY) - fprintf(stderr, Name - ": Cannot add bitmap while array is" - " resyncing or reshaping etc.\n"); - fprintf(stderr, Name ": Cannot set bitmap file for %s: %s\n", + pr_err("Cannot add bitmap while array is" + " resyncing or reshaping etc.\n"); + pr_err("Cannot set bitmap file for %s: %s\n", devname, strerror(err)); return 1; } @@ -478,7 +490,6 @@ int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int return 0; } - /* * When reshaping an array we might need to backup some data. * This is written to all spares with a 'super_block' describing it. @@ -524,7 +535,7 @@ static int check_idle(struct supertype *st) char container[40]; 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) { @@ -547,11 +558,11 @@ static int freeze_container(struct supertype *st) if (!check_idle(st)) return -1; - + fmt_devname(container, container_dev); if (block_monitor(container, 1)) { - fprintf(stderr, Name ": failed to freeze container\n"); + pr_err("failed to freeze container\n"); return -2; } @@ -563,7 +574,7 @@ 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); unblock_monitor(container, 1); @@ -606,8 +617,6 @@ static void unfreeze(struct supertype *st) if (sra) sysfs_set_str(sra, NULL, "sync_action", "idle"); - else - fprintf(stderr, Name ": failed to unfreeze array\n"); sysfs_free(sra); } } @@ -633,21 +642,21 @@ static void wait_reshape(struct mdinfo *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 0; if (!st->ss->reshape_super || !st->ss->manage_reshape) { - fprintf(stderr, Name ": %s metadata does not support reshape\n", + pr_err("%s metadata does not support reshape\n", st->ss->name); return 1; } 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) @@ -696,15 +705,24 @@ static int subarray_set_num(char *container, struct mdinfo *sra, char *name, int return rc; } -int start_reshape(struct mdinfo *sra, int already_running) +int start_reshape(struct mdinfo *sra, int already_running, + int before_data_disks, int data_disks) { int err; + unsigned long long sync_max_to_set; + sysfs_set_num(sra, NULL, "suspend_lo", 0x7FFFFFFFFFFFFFFFULL); - err = sysfs_set_num(sra, NULL, "suspend_hi", 0); - err = err ?: sysfs_set_num(sra, NULL, "suspend_lo", 0); + err = sysfs_set_num(sra, NULL, "suspend_hi", sra->reshape_progress); + err = err ?: sysfs_set_num(sra, NULL, "suspend_lo", + sra->reshape_progress); + if (before_data_disks <= data_disks) + sync_max_to_set = sra->reshape_progress / data_disks; + else + sync_max_to_set = (sra->component_size * data_disks + - sra->reshape_progress) / data_disks; if (!already_running) - sysfs_set_num(sra, NULL, "sync_min", 0); - err = err ?: sysfs_set_num(sra, NULL, "sync_max", 0); + sysfs_set_num(sra, NULL, "sync_min", sync_max_to_set); + err = err ?: sysfs_set_num(sra, NULL, "sync_max", sync_max_to_set); if (!already_running) err = err ?: sysfs_set_str(sra, NULL, "sync_action", "reshape"); @@ -837,9 +855,8 @@ int reshape_prepare_fdlist(char *devname, = dev_open(dn, O_RDONLY); offsets[sd->disk.raid_disk] = sd->data_offset*512; if (fdlist[sd->disk.raid_disk] < 0) { - fprintf(stderr, - Name ": %s: cannot open component %s\n", - devname, dn ? dn : "-unknown-"); + pr_err("%s: cannot open component %s\n", + devname, dn ? dn : "-unknown-"); d = -1; goto release; } @@ -850,7 +867,7 @@ int reshape_prepare_fdlist(char *devname, fdlist[d] = dev_open(dn, O_RDWR); offsets[d] = (sd->data_offset + sra->component_size - blocks - 8)*512; if (fdlist[d] < 0) { - fprintf(stderr, Name ": %s: cannot open component %s\n", + pr_err("%s: cannot open component %s\n", devname, dn ? dn : "-unknown-"); d = -1; goto release; @@ -881,7 +898,7 @@ int reshape_open_backup_file(char *backup_file, S_IRUSR | S_IWUSR); *offsets = 8 * 512; if (*fdlist < 0) { - fprintf(stderr, Name ": %s: cannot create backup file %s: %s\n", + pr_err("%s: cannot create backup file %s: %s\n", devname, backup_file, strerror(errno)); return 0; } @@ -894,7 +911,7 @@ int reshape_open_backup_file(char *backup_file, dev = stb.st_dev; fstat(fd, &stb); if (stb.st_rdev == dev) { - fprintf(stderr, Name ": backup file must NOT be" + pr_err("backup file must NOT be" " on the array being reshaped.\n"); close(*fdlist); return 0; @@ -903,14 +920,14 @@ int reshape_open_backup_file(char *backup_file, memset(buf, 0, 512); for (i=0; i < blocks + 8 ; i++) { if (write(*fdlist, buf, 512) != 512) { - fprintf(stderr, Name ": %s: cannot create" + pr_err("%s: cannot create" " backup file %s: %s\n", devname, backup_file, strerror(errno)); return 0; } } if (fsync(*fdlist) != 0) { - fprintf(stderr, Name ": %s: cannot create backup file %s: %s\n", + pr_err("%s: cannot create backup file %s: %s\n", devname, backup_file, strerror(errno)); return 0; } @@ -997,6 +1014,10 @@ char *analyse_change(struct mdinfo *info, struct reshape *re) * raid5 with 2 disks, or * raid0 with 1 disk */ + if (info->new_level > 1 && + (info->component_size & 7)) + return "Cannot convert RAID1 of this size - " + "reduce size to multiple of 4K first."; if (info->new_level == 0) { if (info->delta_disks != UnSet && info->delta_disks != 0) @@ -1249,7 +1270,7 @@ char *analyse_change(struct mdinfo *info, struct reshape *re) break; case 5: - /* We get to RAID5 for RAID5 or RAID6 */ + /* We get to RAID5 from RAID5 or RAID6 */ if (re->level != 5 && re->level != 6) return "Cannot convert to RAID5 from this level"; @@ -1271,11 +1292,27 @@ char *analyse_change(struct mdinfo *info, struct reshape *re) char layout[40]; char *ls = map_num(r5layout, info->new_layout); int l; - strcat(strcpy(layout, ls), "-6"); - l = map_name(r6layout, layout); - if (l == UnSet) - return "Cannot find RAID6 layout" - " to convert to"; + if (ls) { + /* Current RAID6 layout has a RAID5 + * equivalent - good + */ + strcat(strcpy(layout, ls), "-6"); + l = map_name(r6layout, layout); + if (l == UnSet) + return "Cannot find RAID6 layout" + " to convert to"; + } else { + /* Current RAID6 has no equivalent. + * If it is already a '-6' layout we + * can leave it unchanged, else we must + * fail + */ + ls = map_num(r6layout, info->new_layout); + if (!ls || + strcmp(ls+strlen(ls)-2, "-6") != 0) + return "Please specify new layout"; + l = info->new_layout; + } re->after.layout = l; } } @@ -1340,13 +1377,52 @@ char *analyse_change(struct mdinfo *info, struct reshape *re) 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, char *backup_file, int quiet, int forked, int restart, int freeze_reshape); static int reshape_container(char *container, char *devname, - struct supertype *st, + int mdfd, + struct supertype *st, struct mdinfo *info, int force, char *backup_file, @@ -1393,14 +1469,14 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, struct mdinfo *sra; if (ioctl(fd, GET_ARRAY_INFO, &array) < 0) { - fprintf(stderr, Name ": %s is not an active md array - aborting\n", + pr_err("%s is not an active md array - aborting\n", devname); return 1; } if (size >= 0 && (chunksize || level!= UnSet || layout_str || raid_disks)) { - fprintf(stderr, Name ": cannot change component size at the same time " + pr_err("cannot change component size at the same time " "as other changes.\n" " Change size first, then check data is intact before " "making other changes.\n"); @@ -1410,18 +1486,18 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, if (raid_disks && raid_disks < array.raid_disks && array.level > 1 && get_linux_version() < 2006032 && !check_env("MDADM_FORCE_FEWER")) { - fprintf(stderr, Name ": reducing the number of devices is not safe before Linux 2.6.32\n" + pr_err("reducing the number of devices is not safe before Linux 2.6.32\n" " Please use a newer kernel\n"); return 1; } st = super_by_fd(fd, &subarray); if (!st) { - fprintf(stderr, Name ": Unable to determine metadata format for %s\n", devname); + pr_err("Unable to determine metadata format for %s\n", devname); return 1; } if (raid_disks > st->max_devs) { - fprintf(stderr, Name ": Cannot increase raid-disks on this array" + pr_err("Cannot increase raid-disks on this array" " beyond %d\n", st->max_devs); return 1; } @@ -1444,7 +1520,7 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, fd = cfd; } if (cfd < 0) { - fprintf(stderr, Name ": Unable to open container for %s\n", + pr_err("Unable to open container for %s\n", devname); free(subarray); return 1; @@ -1456,12 +1532,41 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, rv = st->ss->load_container(st, cfd, NULL); if (rv) { - fprintf(stderr, Name ": Cannot read superblock for %s\n", + pr_err("Cannot read superblock for %s\n", devname); free(subarray); return 1; } + /* check if operation is supported for metadata handler */ + if (st->ss->container_content) { + struct mdinfo *cc = NULL; + struct mdinfo *content = NULL; + + cc = st->ss->container_content(st, subarray); + for (content = cc; content ; content = content->next) { + int allow_reshape = 1; + + /* check if reshape is allowed based on metadata + * indications stored in content.array.status + */ + if (content->array.state & (1<array.state + & (1<update_tail = &st->updates; } @@ -1472,35 +1577,36 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, if (raid_disks > array.raid_disks && array.spare_disks +added_disks < (raid_disks - array.raid_disks) && !force) { - fprintf(stderr, - Name ": Need %d spare%s to avoid degraded array," - " and only have %d.\n" - " Use --force to over-ride this check.\n", - raid_disks - array.raid_disks, - raid_disks - array.raid_disks == 1 ? "" : "s", - array.spare_disks + added_disks); + pr_err("Need %d spare%s to avoid degraded array," + " and only have %d.\n" + " Use --force to over-ride this check.\n", + raid_disks - array.raid_disks, + raid_disks - array.raid_disks == 1 ? "" : "s", + array.spare_disks + added_disks); return 1; } sra = sysfs_read(fd, 0, GET_LEVEL | GET_DISKS | GET_DEVS | GET_STATE | GET_VERSION); - if (sra) { + if (sra) { if (st->ss->external && subarray == NULL) { array.level = LEVEL_CONTAINER; sra->array.level = LEVEL_CONTAINER; } } else { - fprintf(stderr, Name ": failed to read sysfs parameters for %s\n", + pr_err("failed to read sysfs parameters for %s\n", devname); return 1; } frozen = freeze(st); if (frozen < -1) { /* freeze() already spewed the reason */ + sysfs_free(sra); return 1; } else if (frozen < 0) { - fprintf(stderr, Name ": %s is performing resync/recovery and cannot" + pr_err("%s is performing resync/recovery and cannot" " be reshaped\n", devname); + sysfs_free(sra); return 1; } @@ -1509,16 +1615,38 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, 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 @@ -1526,9 +1654,14 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, * 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) { @@ -1543,20 +1676,46 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, } } } + if (rv) { + pr_err("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 " + pr_err("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 * it wrong */ - fprintf(stderr, Name ": Limited v0.90 array to " - "2TB per device\n"); + pr_err("Limited v0.90 array to " + "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) { @@ -1568,20 +1727,41 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, "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", + pr_err("Cannot set device size for %s: %s\n", devname, strerror(err)); - if (err == EBUSY && + if (err == EBUSY && (array.state & (1< Raid0 conversion =============== * current implementation assumes that following conditions must be met: * - RAID10: - * - far_copies == 1 - * - near_copies == 2 + * - far_copies == 1 + * - near_copies == 2 */ if ((level == 0 && array.level == 10 && sra && - array.layout == ((1 << 8) + 2) && !(array.raid_disks & 1)) || + array.layout == ((1 << 8) + 2) && !(array.raid_disks & 1)) || (level == 0 && array.level == 1 && sra)) { int err; err = remove_disks_for_takeover(st, sra, array.layout); @@ -1644,8 +1824,12 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, rv = 1; goto release; } - /* FIXME this is added with no justification - why is it here */ - ping_monitor(container); + /* Make sure mdmon has seen the device removal + * and updated metadata before we continue with + * level change + */ + if (container) + ping_monitor(container); } memset(&info, 0, sizeof(info)); @@ -1667,19 +1851,17 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, if (info.array.level == 6 && (info.new_level == 6 || info.new_level == UnSet) && info.array.layout >= 16) { - fprintf(stderr, Name - ": %s has a non-standard layout. If you" - " wish to preserve this\n" - " during the reshape, please specify" - " --layout=preserve\n" - " If you want to change it, specify a" - " layout or use --layout=normalise\n", - devname); + pr_err("%s has a non-standard layout. If you" + " wish to preserve this\n", devname); + cont_err("during the reshape, please specify" + " --layout=preserve\n"); + cont_err("If you want to change it, specify a" + " layout or use --layout=normalise\n"); rv = 1; goto release; } } else if (strcmp(layout_str, "normalise") == 0 || - strcmp(layout_str, "normalize") == 0) { + strcmp(layout_str, "normalize") == 0) { /* If we have a -6 RAID6 layout, remove the '-6'. */ info.new_layout = UnSet; if (info.array.level == 6 && info.new_level == UnSet) { @@ -1690,9 +1872,35 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, *h = 0; info.new_layout = map_name(r6layout, l); } + } else { + pr_err("%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 { + pr_err("%s in only meaningful when reshaping" + " to RAID6\n", layout_str); + rv = 1; + goto release; + } } else { int l = info.new_level; if (l == UnSet) @@ -1711,13 +1919,13 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, info.new_layout = parse_layout_faulty(layout_str); break; default: - fprintf(stderr, Name ": layout not meaningful" + pr_err("layout not meaningful" " with this level\n"); rv = 1; goto release; } if (info.new_layout == UnSet) { - fprintf(stderr, Name ": layout %s not understood" + pr_err("layout %s not understood" " for this level\n", layout_str); rv = 1; @@ -1727,15 +1935,15 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, if (array.level == LEVEL_FAULTY) { if (level != UnSet && level != array.level) { - fprintf(stderr, Name ": cannot change level of Faulty device\n"); + pr_err("cannot change level of Faulty device\n"); rv =1 ; } if (chunksize) { - fprintf(stderr, Name ": cannot set chunksize of Faulty device\n"); + pr_err("cannot set chunksize of Faulty device\n"); rv =1 ; } if (raid_disks && raid_disks != 1) { - fprintf(stderr, Name ": cannot set raid_disks of Faulty device\n"); + pr_err("cannot set raid_disks of Faulty device\n"); rv =1 ; } if (layout_str) { @@ -1745,7 +1953,7 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, } array.layout = info.new_layout; if (ioctl(fd, SET_ARRAY_INFO, &array) != 0) { - fprintf(stderr, Name ": failed to set new layout\n"); + pr_err("failed to set new layout\n"); rv = 1; } else if (!quiet) printf("layout for %s set to %d\n", @@ -1760,7 +1968,7 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, * number of devices (On-Line Capacity Expansion) must be * performed at the level of the container */ - rv = reshape_container(container, devname, st, &info, + rv = reshape_container(container, devname, -1, st, &info, force, backup_file, quiet, 0, 0); frozen = 0; } else { @@ -1780,10 +1988,11 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, /* 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; } @@ -1793,11 +2002,69 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file, frozen = 0; } release: + sysfs_free(sra); if (frozen > 0) unfreeze(st); return rv; } +/* verify_reshape_position() + * Function checks if reshape position in metadata is not farther + * than position in md. + * Return value: + * 0 : not valid sysfs entry + * it can be caused by not started reshape, it should be started + * by reshape array or raid0 array is before takeover + * -1 : error, reshape position is obviously wrong + * 1 : success, reshape progress correct or updated +*/ +static int verify_reshape_position(struct mdinfo *info, int level) +{ + int ret_val = 0; + char buf[40]; + int rv; + + /* read sync_max, failure can mean raid0 array */ + rv = sysfs_get_str(info, NULL, "sync_max", buf, 40); + + if (rv > 0) { + char *ep; + unsigned long long position = strtoull(buf, &ep, 0); + + dprintf(Name": Read sync_max sysfs entry is: %s\n", buf); + if (!(ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))) { + position *= get_data_disks(level, + info->new_layout, + info->array.raid_disks); + if (info->reshape_progress < position) { + dprintf("Corrected reshape progress (%llu) to " + "md position (%llu)\n", + info->reshape_progress, position); + info->reshape_progress = position; + ret_val = 1; + } else if (info->reshape_progress > position) { + pr_err("Fatal error: array " + "reshape was not properly frozen " + "(expected reshape position is %llu, " + "but reshape progress is %llu.\n", + position, info->reshape_progress); + ret_val = -1; + } else { + dprintf("Reshape position in md and metadata " + "are the same;"); + ret_val = 1; + } + } + } else if (rv == 0) { + /* for valid sysfs entry, 0-length content + * should be indicated as error + */ + ret_val = -1; + } + + 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, @@ -1816,8 +2083,8 @@ static int reshape_array(char *container, int fd, char *devname, struct mddev_dev *dv; int added_disks; - int *fdlist; - unsigned long long *offsets; + int *fdlist = NULL; + unsigned long long *offsets = NULL; int d; int nrdisks; int err; @@ -1842,29 +2109,43 @@ static int reshape_array(char *container, int fd, char *devname, if (info->reshape_active) { int new_level = info->new_level; info->new_level = UnSet; - info->array.raid_disks -= info->delta_disks; + if (info->delta_disks > 0) + info->array.raid_disks -= info->delta_disks; msg = analyse_change(info, &reshape); info->new_level = new_level; - info->array.raid_disks += info->delta_disks; + if (info->delta_disks > 0) + info->array.raid_disks += info->delta_disks; if (!restart) /* Make sure the array isn't read-only */ ioctl(fd, RESTART_ARRAY_RW, 0); } else msg = analyse_change(info, &reshape); if (msg) { - fprintf(stderr, Name ": %s\n", msg); + pr_err("%s\n", msg); goto release; } if (restart && (reshape.level != info->array.level || reshape.before.layout != info->array.layout || reshape.before.data_disks + reshape.parity - != info->array.raid_disks - info->delta_disks)) { - fprintf(stderr, Name ": reshape info is not in native format -" + != info->array.raid_disks - max(0, info->delta_disks))) { + pr_err("reshape info is not in native format -" " cannot continue.\n"); goto release; } + if (st->ss->external && restart && (info->reshape_progress == 0)) { + /* When reshape is restarted from '0', very begin of array + * it is possible that for external metadata reshape and array + * configuration doesn't happen. + * Check if md has the same opinion, and reshape is restarted + * from 0. If so, this is regular reshape start after reshape + * switch in metadata to next array only. + */ + if ((verify_reshape_position(info, reshape.level) >= 0) && + (info->reshape_progress == 0)) + restart = 0; + } if (restart) { /* reshape already started. just skip to monitoring the reshape */ if (reshape.backup_blocks == 0) @@ -1888,13 +2169,12 @@ static int reshape_array(char *container, int fd, char *devname, if (!force && info->new_level > 1 && info->array.level > 1 && spares_needed > info->array.spare_disks + added_disks) { - fprintf(stderr, - Name ": Need %d spare%s to avoid degraded array," - " and only have %d.\n" - " Use --force to over-ride this check.\n", - spares_needed, - spares_needed == 1 ? "" : "s", - info->array.spare_disks + added_disks); + pr_err("Need %d spare%s to avoid degraded array," + " and only have %d.\n" + " Use --force to over-ride this check.\n", + spares_needed, + spares_needed == 1 ? "" : "s", + info->array.spare_disks + added_disks); goto release; } /* Check we have enough spares to not fail */ @@ -1903,12 +2183,11 @@ static int reshape_array(char *container, int fd, char *devname, - array.raid_disks; if ((info->new_level > 1 || info->new_level == 0) && spares_needed > info->array.spare_disks +added_disks) { - fprintf(stderr, - Name ": Need %d spare%s to create working array," - " and only have %d.\n", - spares_needed, - spares_needed == 1 ? "" : "s", - info->array.spare_disks + added_disks); + pr_err("Need %d spare%s to create working array," + " and only have %d.\n", + spares_needed, + spares_needed == 1 ? "" : "s", + info->array.spare_disks + added_disks); goto release; } @@ -1921,22 +2200,25 @@ static int reshape_array(char *container, int fd, char *devname, err = sysfs_set_str(info, NULL, "level", c); if (err) { err = errno; - fprintf(stderr, Name ": %s: could not set level to %s\n", + pr_err("%s: could not set level to %s\n", devname, c); - if (err == EBUSY && + if (err == EBUSY && (info->array.state & (1< 0 && st->ss->external) { /* make sure mdmon is aware of the new level */ + if (mdmon_running(st->container_dev)) + flush_mdmon(container); + if (!mdmon_running(st->container_dev)) start_mdmon(st->container_dev); ping_monitor(container); @@ -2002,7 +2284,7 @@ static int reshape_array(char *container, int fd, char *devname, info->new_layout != array.layout) { array.layout = info->new_layout; if (ioctl(fd, SET_ARRAY_INFO, &array) != 0) { - fprintf(stderr, Name ": failed to set new layout\n"); + pr_err("failed to set new layout\n"); goto release; } else if (!quiet) printf("layout for %s set to %d\n", @@ -2013,7 +2295,7 @@ static int reshape_array(char *container, int fd, char *devname, array.raid_disks != (info->array.raid_disks + info->delta_disks)) { array.raid_disks += info->delta_disks; if (ioctl(fd, SET_ARRAY_INFO, &array) != 0) { - fprintf(stderr, Name ": failed to set raid disks\n"); + pr_err("failed to set raid disks\n"); goto release; } else if (!quiet) { printf("raid_disks for %s set to %d\n", @@ -2024,7 +2306,7 @@ static int reshape_array(char *container, int fd, char *devname, info->new_chunk != array.chunk_size) { if (sysfs_set_num(info, NULL, "chunk_size", info->new_chunk) != 0) { - fprintf(stderr, Name ": failed to set chunk size\n"); + pr_err("failed to set chunk size\n"); goto release; } else if (!quiet) printf("chunk size for %s set to %d\n", @@ -2039,7 +2321,7 @@ static int reshape_array(char *container, int fd, char *devname, * 1/ The array will shrink. * We need to ensure the reshape will pause before reaching * the 'critical section'. We also need to fork and wait for - * that to happen. When it does we + * that to happen. When it does we * suspend/backup/complete/unfreeze * * 2/ The array will not change size. @@ -2071,11 +2353,10 @@ started: /* Check that we can hold all the data */ get_dev_size(fd, NULL, &array_size); if (reshape.new_size < (array_size/512)) { - fprintf(stderr, - Name ": this change will reduce the size of the array.\n" - " use --grow --array-size first to truncate array.\n" - " e.g. mdadm --grow %s --array-size %llu\n", - devname, reshape.new_size/2); + pr_err("this change will reduce the size of the array.\n" + " use --grow --array-size first to truncate array.\n" + " e.g. mdadm --grow %s --array-size %llu\n", + devname, reshape.new_size/2); goto release; } @@ -2083,7 +2364,7 @@ started: GET_COMPONENT|GET_DEVS|GET_OFFSET|GET_STATE|GET_CHUNK| GET_CACHE); if (!sra) { - fprintf(stderr, Name ": %s: Cannot get array details from sysfs\n", + pr_err("%s: Cannot get array details from sysfs\n", devname); goto release; } @@ -2092,7 +2373,7 @@ started: * unit. The number we have so far is just a minimum */ blocks = reshape.backup_blocks; - if (reshape.before.data_disks == + if (reshape.before.data_disks == reshape.after.data_disks) { /* Make 'blocks' bigger for better throughput, but * not so big that we reject it below. @@ -2102,11 +2383,11 @@ started: blocks < 16*1024*2) blocks *= 2; } else - fprintf(stderr, Name ": Need to backup %luK of critical " + pr_err("Need to backup %luK of critical " "section..\n", blocks/2); if (blocks >= sra->component_size/2) { - fprintf(stderr, Name ": %s: Something wrong" + pr_err("%s: Something wrong" " - reshape aborted\n", devname); goto release; @@ -2117,12 +2398,8 @@ started: nrdisks = max(reshape.before.data_disks, reshape.after.data_disks) + reshape.parity + sra->array.spare_disks; - fdlist = malloc((1+nrdisks) * sizeof(int)); - offsets = malloc((1+nrdisks) * sizeof(offsets[0])); - if (!fdlist || !offsets) { - fprintf(stderr, Name ": malloc failed: grow aborted\n"); - goto release; - } + fdlist = xcalloc((1+nrdisks), sizeof(int)); + offsets = xcalloc((1+nrdisks), sizeof(offsets[0])); odisks = reshape.before.data_disks + reshape.parity; d = reshape_prepare_fdlist(devname, sra, odisks, @@ -2136,11 +2413,11 @@ started: if (backup_file == NULL) { if (reshape.after.data_disks <= reshape.before.data_disks) { - fprintf(stderr, Name ": %s: Cannot grow - " + pr_err("%s: Cannot grow - " "need backup-file\n", devname); goto release; } else if (sra->array.spare_disks == 0) { - fprintf(stderr, Name ": %s: Cannot grow - " + pr_err("%s: Cannot grow - " "need a spare or backup-file to backup " "critical section\n", devname); goto release; @@ -2182,9 +2459,16 @@ started: sra->new_chunk = info->new_chunk; - if (restart) + if (restart) { + /* for external metadata checkpoint saved by mdmon can be lost + * or missed /due to e.g. crash/. Check if md is not during + * restart farther than metadata points to. + * If so, this means metadata information is obsolete. + */ + if (st->ss->external) + verify_reshape_position(info, reshape.level); sra->reshape_progress = info->reshape_progress; - else { + } else { sra->reshape_progress = 0; if (reshape.after.data_disks < reshape.before.data_disks) /* start from the end of the new array */ @@ -2202,15 +2486,13 @@ started: ioctl(fd, SET_ARRAY_INFO, &array) != 0) { int err = errno; - fprintf(stderr, - Name ": Cannot set device shape for %s: %s\n", - devname, strerror(errno)); + pr_err("Cannot set device shape for %s: %s\n", + devname, strerror(errno)); - if (err == EBUSY && + if (err == EBUSY && (array.state & (1<new_chunk) < 0) err = errno; - if (!err && sysfs_set_num(sra, NULL, "layout", - reshape.after.layout) < 0) + if (!err && sysfs_set_num(sra, NULL, "layout", + reshape.after.layout) < 0) err = errno; if (!err && subarray_set_num(container, sra, "raid_disks", - reshape.after.data_disks + - reshape.parity) < 0) + reshape.after.data_disks + + reshape.parity) < 0) err = errno; if (err) { - fprintf(stderr, Name ": Cannot set device shape for %s\n", + pr_err("Cannot set device shape for %s\n", devname); - if (err == EBUSY && + if (err == EBUSY && (array.state & (1<reshape_progress); return 1; } @@ -2267,11 +2548,14 @@ started: */ switch(forked ? 0 : fork()) { case -1: - fprintf(stderr, Name ": Cannot run child to monitor reshape: %s\n", + pr_err("Cannot run child to monitor reshape: %s\n", strerror(errno)); abort_reshape(sra); goto release; default: + free(fdlist); + free(offsets); + sysfs_free(sra); return 0; case 0: map_fork(); @@ -2299,6 +2583,9 @@ started: d - odisks, fdlist+odisks, offsets+odisks); + free(fdlist); + free(offsets); + if (backup_file && done) unlink(backup_file); if (!done) { @@ -2314,6 +2601,7 @@ started: /* no need to wait for the reshape to finish as * there is nothing more to do. */ + sysfs_free(sra); exit(0); } wait_reshape(sra); @@ -2322,7 +2610,7 @@ started: /* Re-load the metadata as much could have changed */ int cfd = open_dev(st->container_dev); if (cfd >= 0) { - ping_monitor(container); + flush_mdmon(container); st->ss->free_super(st); st->ss->load_container(st, cfd, container); close(cfd); @@ -2334,35 +2622,8 @@ started: */ 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) { @@ -2370,32 +2631,38 @@ started: if (c) { err = sysfs_set_str(sra, NULL, "level", c); if (err) - fprintf(stderr, Name\ - ": %s: could not set level " - "to %s\n", devname, c); + pr_err("%s: could not set level " + "to %s\n", devname, c); } if (info->new_level == 0) st->update_tail = NULL; } out: + sysfs_free(sra); if (forked) return 0; unfreeze(st); exit(0); release: + free(fdlist); + free(offsets); if (orig_level != UnSet && sra) { c = map_num(pers, orig_level); if (c && sysfs_set_str(sra, NULL, "level", c) == 0) - fprintf(stderr, Name ": aborting level change\n"); + pr_err("aborting level change\n"); } + sysfs_free(sra); if (!forked) unfreeze(st); return 1; } +/* mdfd handle is passed to be closed in child process (after fork). + */ int reshape_container(char *container, char *devname, - struct supertype *st, + int mdfd, + struct supertype *st, struct mdinfo *info, int force, char *backup_file, @@ -2403,6 +2670,7 @@ int reshape_container(char *container, char *devname, { struct mdinfo *cc = NULL; int rv = restart; + int last_devnum = -1; /* component_size is not meaningful for a container, * so pass '-1' meaning 'no change' @@ -2411,7 +2679,8 @@ int reshape_container(char *container, char *devname, 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; } @@ -2437,6 +2706,11 @@ int reshape_container(char *container, char *devname, break; } + /* close unused handle in child process + */ + if (mdfd > -1) + close(mdfd); + while(1) { /* For each member array with reshape_active, * we need to perform the reshape. @@ -2468,22 +2742,54 @@ int reshape_container(char *container, char *devname, devname2devnum(container)); if (!mdstat) continue; + if (mdstat->active == 0) { + pr_err("Skipping inactive " + "array md%i.\n", mdstat->devnum); + free_mdstat(mdstat); + mdstat = NULL; + continue; + } break; } if (!content) break; - fd = open_dev(mdstat->devnum); - if (fd < 0) - break; adev = map_dev(dev2major(mdstat->devnum), dev2minor(mdstat->devnum), 0); if (!adev) adev = content->text_version; + fd = open_dev(mdstat->devnum); + if (fd < 0) { + printf(Name ": Device %s cannot be opened for reshape.", + adev); + break; + } + + if (last_devnum == mdstat->devnum) { + /* Do not allow for multiple reshape_array() calls for + * the same array. + * It can happen when reshape_array() returns without + * error, when reshape is not finished (wrong reshape + * starting/continuation conditions). Mdmon doesn't + * switch to next array in container and reentry + * conditions for the same array occur. + * This is possibly interim until the behaviour of + * reshape_array is resolved(). + */ + printf(Name ": Multiple reshape execution detected for " + "device %s.", adev); + close(fd); + break; + } + last_devnum = mdstat->devnum; + sysfs_init(content, fd, mdstat->devnum); + if (mdmon_running(devname2devnum(container))) + flush_mdmon(container); + rv = reshape_array(container, fd, adev, st, content, force, NULL, backup_file, quiet, 1, restart, @@ -2498,6 +2804,9 @@ int reshape_container(char *container, char *devname, restart = 0; if (rv) break; + + if (mdmon_running(devname2devnum(container))) + flush_mdmon(container); } if (!rv) unfreeze(st); @@ -2526,7 +2835,7 @@ int reshape_container(char *container, char *devname, * suspend/backup/allow always come together * wait/resume/discard do too. * For the same-size case we have two backups to improve flow. - * + * */ int progress_reshape(struct mdinfo *info, struct reshape *reshape, @@ -2671,7 +2980,7 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape, * this much. */ target = 64*1024*2 * min(reshape->before.data_disks, - reshape->after.data_disks); + reshape->after.data_disks); target /= reshape->backup_blocks; if (target < 2) target = 2; @@ -2808,7 +3117,7 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape, - completed; } *reshape_completed = completed; - + close(fd); /* We return the need_backup flag. Caller will decide @@ -2868,7 +3177,6 @@ check_progress: } } - /* FIXME return status is never checked */ static int grow_backup(struct mdinfo *sra, unsigned long long offset, /* per device */ @@ -2938,7 +3246,7 @@ static int grow_backup(struct mdinfo *sra, else lseek64(destfd[i], destoffsets[i], 0); - rv = save_stripes(sources, offsets, + rv = save_stripes(sources, offsets, disks, chunk, level, layout, dests, destfd, offset*512*odata, stripes * chunk * odata, @@ -2986,11 +3294,11 @@ static int grow_backup(struct mdinfo *sra, * every works. */ /* FIXME return value is often ignored */ -static int forget_backup( - int dests, int *destfd, unsigned long long *destoffsets, - int part) +static int forget_backup(int dests, int *destfd, + unsigned long long *destoffsets, + int part) { - /* + /* * Erase backup 'part' (which is 0 or 1) */ int i; @@ -3014,7 +3322,7 @@ static int forget_backup( if ((unsigned long long)lseek64(destfd[i], destoffsets[i]-4096, 0) != destoffsets[i]-4096) rv = -1; - if (rv == 0 && + if (rv == 0 && write(destfd[i], &bsb, 512) != 512) rv = -1; fsync(destfd[i]); @@ -3050,7 +3358,7 @@ static void validate(int afd, int bfd, unsigned long long offset) fail("magic is bad"); if (memcmp(bsb2.magic, "md_backup_data-2", 16) == 0 && bsb2.sb_csum2 != bsb_csum((char*)&bsb2, - ((char*)&bsb2.sb_csum2)-((char*)&bsb2))) + ((char*)&bsb2.sb_csum2)-((char*)&bsb2))) fail("second csum bad"); if (__le64_to_cpu(bsb2.devstart)*512 != offset) @@ -3080,7 +3388,7 @@ static void validate(int afd, int bfd, unsigned long long offset) if ((unsigned long long)read(afd, abuf, len) != len) fail("read first from array failed"); if (memcmp(bbuf, abuf, len) != 0) { - #if 0 +#if 0 int i; printf("offset=%llu len=%llu\n", (unsigned long long)__le64_to_cpu(bsb2.arraystart)*512, len); @@ -3089,7 +3397,7 @@ static void validate(int afd, int bfd, unsigned long long offset) printf("first diff byte %d\n", i); break; } - #endif +#endif fail("data1 compare failed"); } } @@ -3100,8 +3408,8 @@ static void validate(int afd, int bfd, unsigned long long offset) free(abuf); free(bbuf); abuflen = len; - abuf = malloc(abuflen); - bbuf = malloc(abuflen); + abuf = xmalloc(abuflen); + bbuf = xmalloc(abuflen); } lseek64(bfd, offset+__le64_to_cpu(bsb2.devstart2)*512, 0); @@ -3163,7 +3471,7 @@ int child_monitor(int afd, struct mdinfo *sra, struct reshape *reshape, break; } if (!sd) { - fprintf(stderr, Name ": Cannot find a superblock\n"); + pr_err("Cannot find a superblock\n"); return 0; } @@ -3358,7 +3666,7 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt if (i == old_disks-1) { fd = open(backup_file, O_RDONLY); if (fd<0) { - fprintf(stderr, Name ": backup file %s inaccessible: %s\n", + pr_err("backup file %s inaccessible: %s\n", backup_file, strerror(errno)); continue; } @@ -3376,7 +3684,7 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt if (lseek64(fd, (dinfo.data_offset + dinfo.component_size - 8) <<9, 0) < 0) { - fprintf(stderr, Name ": Cannot seek on device %d\n", i); + pr_err("Cannot seek on device %d\n", i); continue; /* Cannot seek */ } sprintf(namebuf, "device-%d", i); @@ -3384,29 +3692,29 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt } if (read(fd, &bsb, sizeof(bsb)) != sizeof(bsb)) { if (verbose) - fprintf(stderr, Name ": Cannot read from %s\n", devname); + pr_err("Cannot read from %s\n", devname); continue; /* Cannot read */ } if (memcmp(bsb.magic, "md_backup_data-1", 16) != 0 && memcmp(bsb.magic, "md_backup_data-2", 16) != 0) { if (verbose) - fprintf(stderr, Name ": No backup metadata on %s\n", devname); + pr_err("No backup metadata on %s\n", devname); continue; } if (bsb.sb_csum != bsb_csum((char*)&bsb, ((char*)&bsb.sb_csum)-((char*)&bsb))) { if (verbose) - fprintf(stderr, Name ": Bad backup-metadata checksum on %s\n", devname); + pr_err("Bad backup-metadata checksum on %s\n", devname); continue; /* bad checksum */ } if (memcmp(bsb.magic, "md_backup_data-2", 16) == 0 && bsb.sb_csum2 != bsb_csum((char*)&bsb, ((char*)&bsb.sb_csum2)-((char*)&bsb))) { if (verbose) - fprintf(stderr, Name ": Bad backup-metadata checksum2 on %s\n", devname); + pr_err("Bad backup-metadata checksum2 on %s\n", devname); continue; /* Bad second checksum */ } if (memcmp(bsb.set_uuid,info->uuid, 16) != 0) { if (verbose) - fprintf(stderr, Name ": Wrong uuid on backup-metadata on %s\n", devname); + pr_err("Wrong uuid on backup-metadata on %s\n", devname); continue; /* Wrong uuid */ } @@ -3417,13 +3725,13 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt if (info->array.utime > (int)__le64_to_cpu(bsb.mtime) + 2*60*60 || info->array.utime < (int)__le64_to_cpu(bsb.mtime) - 10*60) { if (check_env("MDADM_GROW_ALLOW_OLD")) { - fprintf(stderr, Name ": accepting backup with timestamp %lu " + pr_err("accepting backup with timestamp %lu " "for array with timestamp %lu\n", (unsigned long)__le64_to_cpu(bsb.mtime), (unsigned long)info->array.utime); } else { if (verbose) - fprintf(stderr, Name ": too-old timestamp on " + pr_err("too-old timestamp on " "backup-metadata on %s\n", devname); continue; /* time stamp is too bad */ } @@ -3439,8 +3747,7 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt < info->reshape_progress) { nonew: if (verbose) - fprintf(stderr, Name - ": backup-metadata found on %s but is not needed\n", devname); + pr_err("backup-metadata found on %s but is not needed\n", devname); continue; /* No new data here */ } } else { @@ -3474,9 +3781,8 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt if (lseek64(fd, __le64_to_cpu(bsb.devstart)*512, 0)< 0) { second_fail: if (verbose) - fprintf(stderr, Name - ": Failed to verify secondary backup-metadata block on %s\n", - devname); + pr_err("Failed to verify secondary backup-metadata block on %s\n", + devname); continue; /* Cannot seek */ } /* There should be a duplicate backup superblock 4k before here */ @@ -3491,7 +3797,7 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt goto second_fail; /* Cannot find leading superblock */ /* Now need the data offsets for all devices. */ - offsets = malloc(sizeof(*offsets)*info->array.raid_disks); + offsets = xmalloc(sizeof(*offsets)*info->array.raid_disks); for(j=0; jarray.raid_disks; j++) { if (fdlist[j] < 0) continue; @@ -3514,11 +3820,12 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt __le64_to_cpu(bsb.length)*512, NULL)) { /* didn't succeed, so giveup */ if (verbose) - fprintf(stderr, Name ": Error restoring backup from %s\n", + pr_err("Error restoring backup from %s\n", devname); + free(offsets); return 1; } - + if (bsb.magic[15] == '2' && restore_stripes(fdlist, offsets, info->array.raid_disks, @@ -3531,11 +3838,13 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt __le64_to_cpu(bsb.length2)*512, NULL)) { /* didn't succeed, so giveup */ if (verbose) - fprintf(stderr, Name ": Error restoring second backup from %s\n", + pr_err("Error restoring second backup from %s\n", devname); + free(offsets); return 1; } + free(offsets); /* Ok, so the data is restored. Let's update those superblocks. */ @@ -3628,7 +3937,7 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt } /* needed to recover critical section! */ if (verbose) - fprintf(stderr, Name ": Failed to find backup of critical section\n"); + pr_err("Failed to find backup of critical section\n"); return 1; } @@ -3645,24 +3954,21 @@ int Grow_continue_command(char *devname, int fd, char buf[40]; int cfd = -1; int fd2 = -1; - char *ep; - unsigned long long position; dprintf("Grow continue from command line called for %s\n", devname); st = super_by_fd(fd, &subarray); if (!st || !st->ss) { - fprintf(stderr, - Name ": Unable to determine metadata format for %s\n", - devname); + pr_err("Unable to determine metadata format for %s\n", + devname); return 1; } dprintf("Grow continue is run for "); if (st->ss->external == 0) { dprintf("native array (%s)\n", devname); if (ioctl(fd, GET_ARRAY_INFO, &array) < 0) { - fprintf(stderr, Name ": %s is not an active md array -" + pr_err("%s is not an active md array -" " aborting\n", devname); ret_val = 1; goto Grow_continue_command_exit; @@ -3684,7 +3990,7 @@ int Grow_continue_command(char *devname, int fd, fd = cfd; } if (cfd < 0) { - fprintf(stderr, Name ": Unable to open container " + pr_err("Unable to open container " "for %s\n", devname); ret_val = 1; goto Grow_continue_command_exit; @@ -3695,36 +4001,63 @@ int Grow_continue_command(char *devname, int fd, */ ret_val = st->ss->load_container(st, cfd, NULL); if (ret_val) { - fprintf(stderr, - Name ": Cannot read superblock for %s\n", - devname); + pr_err("Cannot read superblock for %s\n", + devname); ret_val = 1; goto Grow_continue_command_exit; } - cc = st->ss->container_content(st, NULL); + cc = st->ss->container_content(st, subarray); for (content = cc; content ; content = content->next) { char *array; + int allow_reshape = 1; if (content->reshape_active == 0) continue; + /* The decision about array or container wide + * reshape is taken in Grow_continue based + * content->reshape_active state, therefore we + * need to check_reshape based on + * reshape_active and subarray name + */ + if (content->array.state & (1<reshape_active == CONTAINER_RESHAPE && + (content->array.state + & (1<text_version+1, '/')+1; mdstat = mdstat_by_subdev(array, container_dev); if (!mdstat) continue; + if (mdstat->active == 0) { + pr_err("Skipping inactive " + "array md%i.\n", mdstat->devnum); + free_mdstat(mdstat); + mdstat = NULL; + continue; + } break; } if (!content) { - fprintf(stderr, - Name ": Unable to determine reshaped " - "array for %s\n", devname); + pr_err("Unable to determine reshaped " + "array for %s\n", devname); ret_val = 1; goto Grow_continue_command_exit; } fd2 = open_dev(mdstat->devnum); if (fd2 < 0) { - fprintf(stderr, Name ": cannot open (md%i)\n", + pr_err("cannot open (md%i)\n", mdstat->devnum); ret_val = 1; goto Grow_continue_command_exit; @@ -3741,7 +4074,7 @@ int Grow_continue_command(char *devname, int fd, if (mdmon_running(container_dev)) st->update_tail = &st->updates; else { - fprintf(stderr, Name ": No mdmon found. " + pr_err("No mdmon found. " "Grow cannot continue.\n"); ret_val = 1; goto Grow_continue_command_exit; @@ -3751,28 +4084,8 @@ int Grow_continue_command(char *devname, int fd, /* verify that array under reshape is started from * correct position */ - ret_val = sysfs_get_str(content, NULL, "sync_max", buf, 40); - if (ret_val <= 0) { - fprintf(stderr, Name - ": cannot open verify reshape progress for %s (%i)\n", - content->sys_name, ret_val); - ret_val = 1; - goto Grow_continue_command_exit; - } - dprintf(Name ": Read sync_max sysfs entry is: %s\n", buf); - position = strtoull(buf, &ep, 0); - if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' ')) { - fprintf(stderr, Name ": Fatal error: array reshape was" - " not properly frozen\n"); - ret_val = 1; - goto Grow_continue_command_exit; - } - position *= get_data_disks(map_name(pers, mdstat->level), - content->new_layout, - content->array.raid_disks); - if (position != content->reshape_progress) { - fprintf(stderr, Name ": Fatal error: array reshape was" - " not properly frozen.\n"); + if (verify_reshape_position(content, + map_name(pers, mdstat->level)) < 0) { ret_val = 1; goto Grow_continue_command_exit; } @@ -3797,34 +4110,28 @@ Grow_continue_command_exit: int Grow_continue(int mdfd, struct supertype *st, struct mdinfo *info, char *backup_file, int freeze_reshape) { - char buf[40]; - char *container = NULL; - int err; + int ret_val = 2; + + if (!info->reshape_active) + return ret_val; - err = sysfs_set_str(info, NULL, "array_state", "readonly"); - if (err) - return err; if (st->ss->external) { - fmt_devname(buf, st->container_dev); - container = buf; - freeze(st); + char container[40]; + int cfd = open_dev(st->container_dev); - if (!mdmon_running(st->container_dev)) - start_mdmon(st->container_dev); - ping_monitor_by_id(st->container_dev); + if (cfd < 0) + return 1; + fmt_devname(container, st->container_dev); + st->ss->load_container(st, cfd, container); + close(cfd); + ret_val = reshape_container(container, 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, + freeze_reshape); - if (info->reshape_active == 2) { - int cfd = open_dev(st->container_dev); - if (cfd < 0) - return 1; - st->ss->load_container(st, cfd, container); - close(cfd); - return reshape_container(container, NULL, - st, info, 0, backup_file, - 0, 1, freeze_reshape); - } - } - return reshape_array(container, mdfd, "array", st, info, 1, - NULL, backup_file, 0, 0, 1, freeze_reshape); + return ret_val; }