]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Grow.c
FIX: delta_disk can have UnSet value
[thirdparty/mdadm.git] / Grow.c
diff --git a/Grow.c b/Grow.c
index df736eda321004efed2d24266603bbb384717e0e..9ae2ecd281ab4a0fbac4fd72aba1bdfb1ef79381 100644 (file)
--- a/Grow.c
+++ b/Grow.c
@@ -565,7 +565,8 @@ 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,
-                        char *backup_file, char *dev, int verbose)
+                        int delta_disks, char *backup_file, char *dev,
+                        int verbose)
 {
        /* nothing extra to check in the native case */
        if (!st->ss->external)
@@ -578,7 +579,8 @@ static int reshape_super(struct supertype *st, long long size, int level,
        }
 
        return st->ss->reshape_super(st, size, level, layout, chunksize,
-                                    raid_disks, backup_file, dev, verbose);
+                                    raid_disks, delta_disks, backup_file, dev,
+                                    verbose);
 }
 
 static void sync_metadata(struct supertype *st)
@@ -627,18 +629,17 @@ static int subarray_set_num(char *container, struct mdinfo *sra, char *name, int
        return rc;
 }
 
-int start_reshape(struct mdinfo *sra)
+int start_reshape(struct mdinfo *sra, int already_running)
 {
        int err;
        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);
-       /* Setting sync_min can fail if the recovery is already 'running',
-        * which can happen when restarting an array which is reshaping.
-        * So don't worry about errors here */
-       sysfs_set_num(sra, NULL, "sync_min", 0);
+       if (!already_running)
+               sysfs_set_num(sra, NULL, "sync_min", 0);
        err = err ?: sysfs_set_num(sra, NULL, "sync_max", 0);
-       err = err ?: sysfs_set_str(sra, NULL, "sync_action", "reshape");
+       if (!already_running)
+               err = err ?: sysfs_set_str(sra, NULL, "sync_action", "reshape");
 
        return err;
 }
@@ -653,15 +654,20 @@ void abort_reshape(struct mdinfo *sra)
        sysfs_set_str(sra, NULL, "sync_max", "max");
 }
 
-int remove_disks_on_raid10_to_raid0_takeover(struct supertype *st,
-                                            struct mdinfo *sra,
-                                            int layout)
+int remove_disks_for_takeover(struct supertype *st,
+                             struct mdinfo *sra,
+                             int layout)
 {
        int nr_of_copies;
        struct mdinfo *remaining;
        int slot;
 
-       nr_of_copies = layout & 0xff;
+       if (sra->array.level == 10)
+               nr_of_copies = layout & 0xff;
+       else if (sra->array.level == 1)
+               nr_of_copies = sra->array.raid_disks;
+       else
+               return 1;
 
        remaining = sra->devs;
        sra->devs = NULL;
@@ -714,7 +720,9 @@ int remove_disks_on_raid10_to_raid0_takeover(struct supertype *st,
 
                sysfs_set_str(sra, sd, "state", "faulty");
                sysfs_set_str(sra, sd, "slot", "none");
-               sysfs_set_str(sra, sd, "state", "remove");
+               /* for external metadata disks should be removed in mdmon */
+               if (!st->ss->external)
+                       sysfs_set_str(sra, sd, "state", "remove");
                sd->disk.state |= (1<<MD_DISK_REMOVED);
                sd->disk.state &= ~(1<<MD_DISK_SYNC);
                sd->next = sra->devs;
@@ -826,7 +834,7 @@ int reshape_open_backup_file(char *backup_file,
        }
 
        memset(buf, 0, 512);
-       for (i=0; i < blocks + 1 ; i++) {
+       for (i=0; i < blocks + 8 ; i++) {
                if (write(*fdlist, buf, 512) != 512) {
                        fprintf(stderr, Name ": %s: cannot create"
                                " backup file %s: %s\n",
@@ -913,29 +921,35 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
        switch (info->array.level) {
        case 1:
                /* RAID1 can convert to RAID1 with different disks, or
-                * raid5 with 2 disks
+                * raid5 with 2 disks, or
+                * raid0 with 1 disk
                 */
+               if (info->new_level == 0) {
+                       re->level = 0;
+                       re->before.data_disks = 1;
+                       re->after.data_disks = 1;
+                       re->before.layout = 0;
+                       re->backup_blocks = 0;
+                       re->parity = 0;
+                       return NULL;
+               }
                if (info->new_level == 1) {
                        if (info->delta_disks == UnSet)
                                /* Don't know what to do */
                                return "no change requested for Growing RAID1";
                        re->level = 1;
-                       re->before.data_disks = (info->array.raid_disks +
-                                                info->delta_disks);
-                       re->before.layout = 0;
                        re->backup_blocks = 0;
                        re->parity = 0;
                        return NULL;
                }
                if (info->array.raid_disks == 2 &&
-                   info->array.raid_disks == 5) {
-                       /* simple in-place conversion */
+                   info->new_level == 5) {
                        re->level = 5;
-                       re->parity = 1;
                        re->before.data_disks = 1;
+                       re->after.data_disks = 1;
                        re->before.layout = ALGORITHM_LEFT_SYMMETRIC;
-                       re->backup_blocks = 0;
-                       return NULL;
+                       info->array.chunk_size = 65536;
+                       break;
                }
                /* Could do some multi-stage conversions, but leave that to
                 * later.
@@ -1082,8 +1096,6 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
                        if (info->array.raid_disks != 2)
                                return "Can only convert a 2-device array to RAID1";
                        re->level = 1;
-                       re->before.data_disks = 2;
-                       re->before.layout = 0;
                        break;
                default:
                        return "Impossible level change requested";
@@ -1224,7 +1236,7 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
                return NULL;
        }
        if (re->after.data_disks == 1 && re->before.data_disks == 1) {
-               /* chunks can layout changes make no difference */
+               /* chunk and layout changes make no difference */
                re->backup_blocks = 0;
                return NULL;
        }
@@ -1406,7 +1418,8 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
        if (size >= 0 && (size == 0 || size != array.size)) {
                long long orig_size = array.size;
 
-               if (reshape_super(st, size, UnSet, UnSet, 0, 0, NULL, devname, !quiet)) {
+               if (reshape_super(st, size, UnSet, UnSet, 0, 0, UnSet, NULL,
+                                 devname, !quiet)) {
                        rv = 1;
                        goto release;
                }
@@ -1428,7 +1441,7 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
 
                        /* restore metadata */
                        if (reshape_super(st, orig_size, UnSet, UnSet, 0, 0,
-                                         NULL, devname, !quiet) == 0)
+                                         UnSet, NULL, devname, !quiet) == 0)
                                sync_metadata(st);
                        fprintf(stderr, Name ": Cannot set device size for %s: %s\n",
                                devname, strerror(err));
@@ -1452,15 +1465,17 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                        size = array.size;
        }
 
-       /* ========= check for Raid10 -> Raid0 conversion ===============
+       /* ========= check for Raid10/Raid1 -> Raid0 conversion ===============
         * current implementation assumes that following conditions must be met:
-        * - far_copies == 1
-        * - near_copies == 2
+        * - RAID10:
+        *      - far_copies == 1
+        *      - near_copies == 2
         */
-       if (level == 0 && array.level == 10 && sra &&
-           array.layout == ((1 << 8) + 2) && !(array.raid_disks & 1)) {
+       if ((level == 0 && array.level == 10 && sra &&
+           array.layout == ((1 << 8) + 2) && !(array.raid_disks & 1)) ||
+           (level == 0 && array.level == 1 && sra)) {
                int err;
-               err = remove_disks_on_raid10_to_raid0_takeover(st, sra, array.layout);
+               err = remove_disks_for_takeover(st, sra, array.layout);
                if (err) {
                        dprintf(Name": Array cannot be reshaped\n");
                        if (cfd > -1)
@@ -1468,6 +1483,8 @@ 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);
        }
 
        info.array = array;
@@ -1561,7 +1578,7 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
 
                if (reshape_super(st, info.component_size, info.new_level,
                                  info.new_layout, info.new_chunk,
-                                 info.array.raid_disks + info.delta_disks,
+                                 info.array.raid_disks, info.delta_disks,
                                  backup_file, devname, quiet)) {
                        rv = 1;
                        goto release;
@@ -1619,12 +1636,18 @@ static int reshape_array(char *container, int fd, char *devname,
                        return 0;
                goto started;
        }
+       /* The container is frozen but the array may not be.
+        * So freeze the array so spares don't get put to the wrong use
+        * FIXME there should probably be a cleaner separation between
+        * freeze_array and freeze_container.
+        */
+       sysfs_freeze_array(info);
        spares_needed = max(reshape.before.data_disks,
                            reshape.after.data_disks)
                + reshape.parity - array.raid_disks;
 
        if (!force &&
-           info->new_level > 0 &&
+           info->new_level > 1 &&
            spares_needed > info->array.spare_disks) {
                fprintf(stderr,
                        Name ": Need %d spare%s to avoid degraded array,"
@@ -1657,14 +1680,15 @@ static int reshape_array(char *container, int fd, char *devname,
                        fprintf(stderr, Name ": level of %s changed to %s\n",
                                devname, c);    
                orig_level = info->array.level;
-       }
+               sysfs_freeze_array(info);
 
-       if (reshape.level > 0 && st->ss->external &&
-           !mdmon_running(st->container_dev)) {
-               start_mdmon(st->container_dev);
-               ping_monitor(container);
+               if (reshape.level > 0 && st->ss->external) {
+                       /* make sure mdmon is aware of the new level */
+                       if (!mdmon_running(st->container_dev))
+                               start_mdmon(st->container_dev);
+                       ping_monitor(container);
+               }
        }
-
        /* ->reshape_super might have chosen some spares from the
         * container that it wants to be part of the new array.
         * We can collect them with ->container_content and give
@@ -1695,35 +1719,44 @@ static int reshape_array(char *container, int fd, char *devname,
                /* No restriping needed, but we might need to impose
                 * some more changes: layout, raid_disks, chunk_size
                 */
+               /* read current array info */
+               if (ioctl(fd, GET_ARRAY_INFO, &array) != 0) {
+                       dprintf("Canot get array information.\n");
+                       goto release;
+               }
+               /* compare current array info with new values and if
+                * it is different update them to new */
                if (info->new_layout != UnSet &&
-                   info->new_layout != info->array.layout) {
-                       info->array.layout = info->new_layout;
-                       if (ioctl(fd, SET_ARRAY_INFO, &info->array) != 0) {
+                   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");
                                goto release;
                        } else if (!quiet)
                                printf("layout for %s set to %d\n",
-                                      devname, info->array.layout);
+                                      devname, array.layout);
                }
                if (info->delta_disks != UnSet &&
-                   info->delta_disks != 0) {
-                       info->array.raid_disks += info->delta_disks;
-                       if (ioctl(fd, SET_ARRAY_INFO, &info->array) != 0) {
+                   info->delta_disks != 0 &&
+                   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");
                                goto release;
-                       } else if (!quiet)
+                       } else if (!quiet) {
                                printf("raid_disks for %s set to %d\n",
-                                      devname, info->array.raid_disks);
+                                      devname, array.raid_disks);
+                       }
                }
                if (info->new_chunk != 0 &&
-                   info->new_chunk != info->array.chunk_size) {
+                   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");
                                goto release;
                        } else if (!quiet)
                                printf("chunk size for %s set to %d\n",
-                                      devname, info->array.chunk_size);
+                                      devname, array.chunk_size);
                }
                unfreeze(st);
                return 0;
@@ -1809,7 +1842,9 @@ started:
 
        /* Now we need to open all these devices so we can read/write.
         */
-       nrdisks = array.raid_disks + sra->array.spare_disks;
+       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) {
@@ -1817,7 +1852,8 @@ started:
                goto release;
        }
 
-       d = reshape_prepare_fdlist(devname, sra, array.raid_disks,
+       odisks = reshape.before.data_disks + reshape.parity;
+       d = reshape_prepare_fdlist(devname, sra, odisks,
                                   nrdisks, blocks, backup_file,
                                   fdlist, offsets);
        if (d < 0) {
@@ -1869,14 +1905,25 @@ started:
        sync_metadata(st);
 
        sra->new_chunk = info->new_chunk;
-       
+
        if (info->reshape_active)
-               /* nothing needed here */;
-       else if (info->array.chunk_size == info->new_chunk &&
+               sra->reshape_progress = info->reshape_progress;
+       else {
+               sra->reshape_progress = 0;
+               if (reshape.after.data_disks < reshape.before.data_disks)
+                       /* start from the end of the new array */
+                       sra->reshape_progress = (sra->component_size
+                                                * reshape.after.data_disks);
+       }
+
+       if (info->array.chunk_size == info->new_chunk &&
            reshape.before.layout == reshape.after.layout &&
            st->ss->external == 0) {
+               /* use SET_ARRAY_INFO but only if reshape hasn't started */
+               ioctl(fd, GET_ARRAY_INFO, &array);
                array.raid_disks = reshape.after.data_disks + reshape.parity;
-               if (ioctl(fd, SET_ARRAY_INFO, &array) != 0) {
+               if (!info->reshape_active &&
+                   ioctl(fd, SET_ARRAY_INFO, &array) != 0) {
                        int err = errno;
 
                        fprintf(stderr,
@@ -1891,9 +1938,17 @@ started:
 
                        goto release;
                }
+       } else if (info->reshape_active && !st->ss->external) {
+               /* We don't need to set anything here for internal
+                * metadata, and for kernels before 2.6.38 we can
+                * fail if we try.
+                */
        } else {
                /* set them all just in case some old 'new_*' value
-                * persists from some earlier problem
+                * persists from some earlier problem.
+                * We even set them when restarting in the middle.  They will
+                * already be set in that case so this will be a no-op,
+                * but it is hard to tell the difference.
                 */
                int err = 0;
                if (sysfs_set_num(sra, NULL, "chunk_size", info->new_chunk) < 0)
@@ -1918,7 +1973,12 @@ started:
                }
        }
 
-       start_reshape(sra);
+       err = start_reshape(sra, (info->reshape_active && !st->ss->external));
+       if (err) {
+               fprintf(stderr, Name ": Cannot start reshape for %s\n",
+                       devname);
+               goto release;
+       }
        if (restart)
                sysfs_set_str(sra, NULL, "array_state", "active");
 
@@ -1945,8 +2005,6 @@ started:
                fd = -1;
        mlockall(MCL_FUTURE);
 
-       odisks = reshape.before.data_disks + reshape.parity;
-
        if (st->ss->external) {
                /* metadata handler takes it from here */
                done = st->ss->manage_reshape(
@@ -2040,6 +2098,7 @@ started:
 out:
        if (forked)
                return 0;
+       unfreeze(st);
        exit(0);
 
 release:
@@ -2067,9 +2126,11 @@ int reshape_container(char *container, int cfd, char *devname,
         */
        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))
+                         info->array.raid_disks, info->delta_disks,
+                         backup_file, devname, quiet)) {
+               unfreeze(st);
                return 1;
+       }
 
        sync_metadata(st);
 
@@ -2080,6 +2141,7 @@ int reshape_container(char *container, int cfd, char *devname,
        switch (fork()) {
        case -1: /* error */
                perror("Cannot fork to complete reshape\n");
+               unfreeze(st);
                return 1;
        default: /* parent */
                printf(Name ": multi-array reshape continues in background\n");
@@ -2121,7 +2183,7 @@ int reshape_container(char *container, int cfd, char *devname,
                if (!content)
                        break;
 
-               fd = open_dev_excl(mdstat->devnum);
+               fd = open_dev(mdstat->devnum);
                if (fd < 0)
                        break;
                adev = map_dev(dev2major(mdstat->devnum),
@@ -2223,8 +2285,9 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
 
        int advancing = (reshape->after.data_disks
                         >= reshape->before.data_disks);
-       unsigned long long need_backup; /* need to eventually backup all the way
-                                        * to here
+       unsigned long long need_backup; /* All data between start of array and
+                                        * here will at some point need to
+                                        * be backed up.
                                         */
        unsigned long long read_offset, write_offset;
        unsigned long long write_range;
@@ -2266,37 +2329,37 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
        read_offset = info->reshape_progress / reshape->before.data_disks;
        write_offset = info->reshape_progress / reshape->after.data_disks;
        write_range = info->new_chunk/512;
+       if (reshape->before.data_disks == reshape->after.data_disks)
+               need_backup = array_size;
+       else
+               need_backup = reshape->backup_blocks;
        if (advancing) {
-               need_backup = 0;
-               if (read_offset < write_offset + write_range) {
+               if (read_offset < write_offset + write_range)
                        max_progress = backup_point;
-                       if (reshape->before.data_disks == reshape->after.data_disks)
-                               need_backup = array_size;
-                       else
-                               need_backup = reshape->backup_blocks;
-               } else {
+               else
                        max_progress =
                                read_offset *
                                reshape->after.data_disks;
-               }
        } else {
-               need_backup = array_size;
-               if (read_offset > write_offset - write_range) {
+               if (read_offset > write_offset - write_range)
+                       /* Can only progress as far as has been backed up,
+                        * which must be suspended */
                        max_progress = backup_point;
-                       if (max_progress >= info->reshape_progress)
-                               need_backup = 0;
-               } else {
-                       max_progress =
-                               read_offset *
-                               reshape->after.data_disks;
-                       /* If we are using internal metadata, then we can
-                        * progress all the way to the suspend_point without
-                        * worrying about backing-up/suspending along the
-                        * way.
-                        */
-                       if (max_progress < *suspend_point &&
-                               info->array.major_version >= 0)
-                               max_progress = *suspend_point;
+               else if (info->reshape_progress <= need_backup)
+                       max_progress = backup_point;
+               else {
+                       if (info->array.major_version >= 0)
+                               /* Can progress until backup is needed */
+                               max_progress = need_backup;
+                       else {
+                               /* Can progress until metadata update is required */
+                               max_progress =
+                                       read_offset *
+                                       reshape->after.data_disks;
+                               /* but data must be suspended */
+                               if (max_progress < *suspend_point)
+                                       max_progress = *suspend_point;
+                       }
                }
        }
 
@@ -2335,16 +2398,28 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
                                max_progress = *suspend_point;
                }
        } else {
-               if ((need_backup < info->reshape_progress
-                    || info->array.major_version < 0) &&
-                   *suspend_point > info->reshape_progress - target) {
-                       if (need_backup > *suspend_point - 2 * target)
-                               *suspend_point = need_backup;
-                       else if (*suspend_point >= 2 * target)
-                               *suspend_point -= 2 * target;
-                       else
+               if (info->array.major_version >= 0) {
+                       /* Only need to suspend when about to backup */
+                       if (info->reshape_progress < need_backup * 2 &&
+                           *suspend_point > 0) {
                                *suspend_point = 0;
-                       sysfs_set_num(info, NULL, "suspend_lo", *suspend_point);
+                               sysfs_set_num(info, NULL, "suspend_lo", 0);
+                               sysfs_set_num(info, NULL, "suspend_hi", need_backup);
+                       }
+               } else {
+                       /* Need to suspend continually */
+                       if (info->reshape_progress < *suspend_point)
+                               *suspend_point = info->reshape_progress;
+                       if (*suspend_point + target < info->reshape_progress)
+                               /* No need to move suspend region yet */;
+                       else {
+                               if (*suspend_point >= 2 * target)
+                                       *suspend_point -= 2 * target;
+                               else
+                                       *suspend_point = 0;
+                               sysfs_set_num(info, NULL, "suspend_lo",
+                                             *suspend_point);
+                       }
                        if (max_progress < *suspend_point)
                                max_progress = *suspend_point;
                }
@@ -2363,6 +2438,9 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
        /* Round to chunk size as some kernels give an erroneously high number */
        max_progress /= info->new_chunk/512;
        max_progress *= info->new_chunk/512;
+       /* And round to old chunk size as the kernel wants that */
+       max_progress /= info->array.chunk_size/512;
+       max_progress *= info->array.chunk_size/512;
        /* Limit progress to the whole device */
        if (max_progress > info->component_size)
                max_progress = info->component_size;
@@ -2399,6 +2477,17 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
                                  action, 20) <= 0 ||
                    strncmp(action, "reshape", 7) != 0)
                        break;
+               /* Some kernels reset 'sync_completed' to zero
+                * before setting 'sync_action' to 'idle'.
+                * So we need these extra tests.
+                */
+               if (completed == 0 && advancing
+                   && info->reshape_progress > 0)
+                       break;
+               if (completed == 0 && !advancing
+                   && info->reshape_progress < (info->component_size
+                                                * reshape->after.data_disks))
+                       break;
                FD_ZERO(&rfds);
                FD_SET(fd, &rfds);
                select(fd+1, NULL, NULL, &rfds, NULL);
@@ -2407,6 +2496,12 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
                        goto check_progress;
                }
        }
+       /* Some kernels reset 'sync_completed' to zero,
+        * we need to have real point we are in md
+        */
+       if (completed == 0)
+               completed = max_progress;
+
        /* some kernels can give an incorrectly high 'completed' number */
        completed /= (info->new_chunk/512);
        completed *= (info->new_chunk/512);
@@ -2423,9 +2518,10 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
        /* We return the need_backup flag.  Caller will decide
         * how much - a multiple of ->backup_blocks up to *suspend_point
         */
-       return advancing
-               ? (need_backup > info->reshape_progress)
-               : (need_backup < info->reshape_progress);
+       if (advancing)
+               return need_backup > info->reshape_progress;
+       else
+               return need_backup >= info->reshape_progress;
 
 check_progress:
        /* if we couldn't read a number from sync_completed, then
@@ -2475,7 +2571,8 @@ static int grow_backup(struct mdinfo *sra,
                odata--;
 
        /* Check that array hasn't become degraded, else we might backup the wrong data */
-       sysfs_get_ll(sra, NULL, "degraded", &ll);
+       if (sysfs_get_ll(sra, NULL, "degraded", &ll) < 0)
+               return -1; /* FIXME this error is ignored */
        new_degraded = (int)ll;
        if (new_degraded != *degraded) {
                /* check each device to ensure it is still working */
@@ -2766,7 +2863,7 @@ int child_monitor(int afd, struct mdinfo *sra, struct reshape *reshape,
                suspend_point = 0;
        } else {
                array_size = sra->component_size * reshape->before.data_disks;
-               backup_point = array_size;
+               backup_point = reshape->backup_blocks;
                suspend_point = array_size;
        }
 
@@ -2856,6 +2953,8 @@ int child_monitor(int afd, struct mdinfo *sra, struct reshape *reshape,
                                if (offset < suspend_point/data)
                                        break;
                        }
+                       if (actual_stripes == 0)
+                               break;
                        grow_backup(sra, offset, actual_stripes,
                                    fds, offsets,
                                    disks, chunk, level, layout,
@@ -2913,6 +3012,7 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt
                int fd;
                int bsbsize;
                char *devname, namebuf[20];
+               unsigned long long lo, hi;
 
                /* This was a spare and may have some saved data on it.
                 * Load the superblock, find and load the
@@ -2996,42 +3096,52 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt
                }
 
                if (bsb.magic[15] == '1') {
-               if (info->delta_disks >= 0) {
-                       /* reshape_progress is increasing */
-                       if (__le64_to_cpu(bsb.arraystart) + __le64_to_cpu(bsb.length) <
-                           info->reshape_progress) {
-                       nonew:
-                               if (verbose)
-                                       fprintf(stderr, Name ": backup-metadata found on %s but is not needed\n", devname);
-                               continue; /* No new data here */
+                       if (bsb.length == 0)
+                               continue;
+                       if (info->delta_disks >= 0) {
+                               /* reshape_progress is increasing */
+                               if (__le64_to_cpu(bsb.arraystart)
+                                   + __le64_to_cpu(bsb.length)
+                                   < info->reshape_progress) {
+                               nonew:
+                                       if (verbose)
+                                               fprintf(stderr, Name
+                  ": backup-metadata found on %s but is not needed\n", devname);
+                                       continue; /* No new data here */
+                               }
+                       } else {
+                               /* reshape_progress is decreasing */
+                               if (__le64_to_cpu(bsb.arraystart) >=
+                                   info->reshape_progress)
+                                       goto nonew; /* No new data here */
                        }
                } else {
-                       /* reshape_progress is decreasing */
-                       if (__le64_to_cpu(bsb.arraystart) >=
-                           info->reshape_progress)
-                               goto nonew; /* No new data here */
-               }
-               } else {
-               if (info->delta_disks >= 0) {
-                       /* reshape_progress is increasing */
-                       if (__le64_to_cpu(bsb.arraystart) + __le64_to_cpu(bsb.length) <
-                           info->reshape_progress &&
-                           __le64_to_cpu(bsb.arraystart2) + __le64_to_cpu(bsb.length2) <
-                           info->reshape_progress)
-                               goto nonew; /* No new data here */
-               } else {
-                       /* reshape_progress is decreasing */
-                       if (__le64_to_cpu(bsb.arraystart) >=
-                           info->reshape_progress &&
-                           __le64_to_cpu(bsb.arraystart2) >=
-                           info->reshape_progress)
-                               goto nonew; /* No new data here */
-               }
+                       if (bsb.length == 0 && bsb.length2 == 0)
+                               continue;
+                       if (info->delta_disks >= 0) {
+                               /* reshape_progress is increasing */
+                               if ((__le64_to_cpu(bsb.arraystart)
+                                    + __le64_to_cpu(bsb.length)
+                                    < info->reshape_progress)
+                                   &&
+                                   (__le64_to_cpu(bsb.arraystart2)
+                                    + __le64_to_cpu(bsb.length2)
+                                    < info->reshape_progress))
+                                       goto nonew; /* No new data here */
+                       } else {
+                               /* reshape_progress is decreasing */
+                               if (__le64_to_cpu(bsb.arraystart) >=
+                                   info->reshape_progress &&
+                                   __le64_to_cpu(bsb.arraystart2) >=
+                                   info->reshape_progress)
+                                       goto nonew; /* No new data here */
+                       }
                }
                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",
+                               fprintf(stderr, Name
+                    ": Failed to verify secondary backup-metadata block on %s\n",
                                        devname);
                        continue; /* Cannot seek */
                }
@@ -3095,7 +3205,28 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt
 
                /* Ok, so the data is restored. Let's update those superblocks. */
 
-               if (info->delta_disks >= 0) {
+               lo = hi = 0;
+               if (bsb.length) {
+                       lo = __le64_to_cpu(bsb.arraystart);
+                       hi = lo + __le64_to_cpu(bsb.length);
+               }
+               if (bsb.magic[15] == '2' && bsb.length2) {
+                       unsigned long long lo1, hi1;
+                       lo1 = __le64_to_cpu(bsb.arraystart2);
+                       hi1 = lo1 + __le64_to_cpu(bsb.length2);
+                       if (lo == hi) {
+                               lo = lo1;
+                               hi = hi1;
+                       } else if (lo < lo1)
+                               hi = hi1;
+                       else
+                               lo = lo1;
+               }
+               if (lo < hi &&
+                   (info->reshape_progress < lo ||
+                    info->reshape_progress > hi))
+                       /* backup does not affect reshape_progress*/ ;
+               else if (info->delta_disks >= 0) {
                        info->reshape_progress = __le64_to_cpu(bsb.arraystart) +
                                __le64_to_cpu(bsb.length);
                        if (bsb.magic[15] == '2') {