]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Grow.c
DDF: ddf_activate_spare: only activate good drives
[thirdparty/mdadm.git] / Grow.c
diff --git a/Grow.c b/Grow.c
index 299583c87a51c79fd5328d3e7477a081a680d01e..c1ae6e839312d31a8213fc12ea2e7dde35a5cc0d 100644 (file)
--- a/Grow.c
+++ b/Grow.c
@@ -626,13 +626,9 @@ static void wait_reshape(struct mdinfo *sra)
        if (fd < 0)
                return;
 
-       while  (sysfs_fd_get_str(fd, action, 20) > 0 &&
-               strncmp(action, "reshape", 7) == 0) {
-               fd_set rfds;
-               FD_ZERO(&rfds);
-               FD_SET(fd, &rfds);
-               select(fd+1, NULL, NULL, &rfds, NULL);
-       }
+       while (sysfs_fd_get_str(fd, action, 20) > 0 &&
+              strncmp(action, "reshape", 7) == 0)
+               sysfs_wait(fd, NULL);
        close(fd);
 }
 
@@ -933,17 +929,6 @@ int reshape_open_backup_file(char *backup_file,
        return 1;
 }
 
-unsigned long GCD(unsigned long a, unsigned long b)
-{
-       while (a != b) {
-               if (a < b)
-                       b -= a;
-               if (b < a)
-                       a -= b;
-       }
-       return a;
-}
-
 unsigned long compute_backup_blocks(int nchunk, int ochunk,
                                    unsigned int ndata, unsigned int odata)
 {
@@ -963,7 +948,7 @@ unsigned long compute_backup_blocks(int nchunk, int ochunk,
        return blocks;
 }
 
-char *analyse_change(struct mdinfo *info, struct reshape *re)
+char *analyse_change(char *devname, struct mdinfo *info, struct reshape *re)
 {
        /* Based on the current array state in info->array and
         * the changes in info->new_* etc, determine:
@@ -1004,9 +989,16 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
                        /* chunk size is meaningful, must divide component_size
                         * evenly
                         */
-                       if (info->component_size % (info->new_chunk/512))
-                               return "New chunk size does not"
-                                       " divide component size";
+                       if (info->component_size % (info->new_chunk/512)) {
+                               unsigned long long shrink = info->component_size;
+                               shrink &= ~(unsigned long long)(info->new_chunk/512-1);
+                               pr_err("New chunk size (%dK) does not evenly divide device size (%lluk)\n",
+                                      info->new_chunk/1024, info->component_size/2);
+                               pr_err("After shrinking any filesystem, \"mdadm --grow %s --size %llu\"\n",
+                                      devname, shrink/2);
+                               pr_err("will shrink the array so the given chunk size would work.\n");
+                               return "";
+                       }
                        break;
                default:
                        return "chunk size not meaningful for this level";
@@ -1205,11 +1197,15 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
                        delta_parity = 1;
                        re->level = 5;
                        re->before.layout = ALGORITHM_PARITY_N;
+                       if (info->new_layout == UnSet)
+                               info->new_layout = map_name(r5layout, "default");
                        break;
                case 6:
                        delta_parity = 2;
                        re->level = 6;
                        re->before.layout = ALGORITHM_PARITY_N;
+                       if (info->new_layout == UnSet)
+                               info->new_layout = map_name(r6layout, "default");
                        break;
                default:
                        return "Impossible level change requested";
@@ -2191,7 +2187,8 @@ static unsigned long long choose_offset(unsigned long long lo,
 static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
                               char *devname, int delta_disks,
                               unsigned long long data_offset,
-                              unsigned long long min)
+                              unsigned long long min,
+                              int can_fallback)
 {
        struct mdinfo *sd;
        int dir = 0;
@@ -2287,6 +2284,8 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
                } else if (delta_disks > 0) {
                        /* need space before */
                        if (before < min) {
+                               if (can_fallback)
+                                       goto fallback;
                                pr_err("Insufficient head-space for reshape on %s\n",
                                        dn);
                                goto release;
@@ -2317,6 +2316,8 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
                        if (dir > 0) {
                                /* Increase data offset */
                                if (after < min) {
+                                       if (can_fallback)
+                                               goto fallback;
                                        pr_err("Insufficient tail-space for reshape on %s\n",
                                                dn);
                                        goto release;
@@ -2337,6 +2338,8 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
                        } else {
                                /* Decrease data offset */
                                if (before < min) {
+                                       if (can_fallback)
+                                               goto fallback;
                                        pr_err("insufficient head-room on %s\n",
                                                dn);
                                        goto release;
@@ -2391,6 +2394,9 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
        return err;
 release:
        return -1;
+fallback:
+       /* Just use a backup file */
+       return 1;
 }
 
 static int raid10_reshape(char *container, int fd, char *devname,
@@ -2447,7 +2453,7 @@ static int raid10_reshape(char *container, int fd, char *devname,
                }
        }
        err = set_new_data_offset(sra, st, devname, info->delta_disks, data_offset,
-                                 min);
+                                 min, 0);
        if (err == 1) {
                pr_err("Cannot set new_data_offset: RAID10 reshape not\n");
                cont_err("supported on this kernel\n");
@@ -2771,7 +2777,7 @@ static int reshape_array(char *container, int fd, char *devname,
                info->new_level = UnSet;
                if (info->delta_disks > 0)
                        info->array.raid_disks -= info->delta_disks;
-               msg = analyse_change(info, &reshape);
+               msg = analyse_change(devname, info, &reshape);
                info->new_level = new_level;
                if (info->delta_disks > 0)
                        info->array.raid_disks += info->delta_disks;
@@ -2779,9 +2785,11 @@ static int reshape_array(char *container, int fd, char *devname,
                        /* Make sure the array isn't read-only */
                        ioctl(fd, RESTART_ARRAY_RW, 0);
        } else
-               msg = analyse_change(info, &reshape);
+               msg = analyse_change(devname, info, &reshape);
        if (msg) {
-               pr_err("%s\n", msg);
+               /* if msg == "", error has already been printed */
+               if (msg[0])
+                       pr_err("%s\n", msg);
                goto release;
        }
        if (restart &&
@@ -2812,6 +2820,16 @@ static int reshape_array(char *container, int fd, char *devname,
                        return 0;
                if (restart & RESHAPE_NO_BACKUP)
                        return 0;
+
+               /* Need 'sra' down at 'started:' */
+               sra = sysfs_read(fd, NULL,
+                                GET_COMPONENT|GET_DEVS|GET_OFFSET|GET_STATE|GET_CHUNK|
+                                GET_CACHE);
+               if (!sra) {
+                       pr_err("%s: Cannot get array details from sysfs\n",
+                              devname);
+                       goto release;
+               }
                goto started;
        }
        /* The container is frozen but the array may not be.
@@ -3035,7 +3053,7 @@ static int reshape_array(char *container, int fd, char *devname,
                switch(set_new_data_offset(sra, st, devname,
                                           reshape.after.data_disks - reshape.before.data_disks,
                                           data_offset,
-                                          reshape.min_offset_change)) {
+                                          reshape.min_offset_change, 1)) {
        case -1:
                goto release;
        case 0:
@@ -3068,9 +3086,11 @@ static int reshape_array(char *container, int fd, char *devname,
                        map_fork();
                        break;
                }
+               close(fd);
                wait_reshape(sra);
-               impose_level(fd, info->new_level, devname, verbose);
-
+               fd = open_dev(sra->sys_name);
+               if (fd >= 0)
+                       impose_level(fd, info->new_level, devname, verbose);
                return 0;
        case 1: /* Couldn't set data_offset, try the old way */
                if (data_offset != INVALID_SECTORS) {
@@ -3125,8 +3145,9 @@ started:
                if (backup_file == NULL) {
                        if (reshape.after.data_disks <=
                            reshape.before.data_disks) {
-                               pr_err("%s: Cannot grow - "
-                                       "need backup-file\n", devname);
+                               pr_err("%s: Cannot grow - need backup-file\n",
+                                      devname);
+                               pr_err(" Please provide one with \"--backup=...\"\n");
                                goto release;
                        } else if (sra->array.spare_disks == 0) {
                                pr_err("%s: Cannot grow - "
@@ -3209,7 +3230,7 @@ started:
        do {
                struct mdstat_ent *mds, *m;
                delayed = 0;
-               mds = mdstat_read(0, 0);
+               mds = mdstat_read(1, 0);
                for (m = mds; m; m = m->next)
                        if (strcmp(m->devnm, sra->sys_name) == 0) {
                                if (m->resync &&
@@ -3229,9 +3250,9 @@ started:
                        delayed = 0;
                }
                if (delayed)
-                       sleep(30 - (delayed-1) * 25);
+                       mdstat_wait(30 - (delayed-1) * 25);
        } while (delayed);
-
+       mdstat_close();
        close(fd);
        if (check_env("MDADM_GROW_VERIFY"))
                fd = open(devname, O_RDONLY | O_DIRECT);
@@ -3456,7 +3477,7 @@ int reshape_container(char *container, char *devname,
                        flush_mdmon(container);
 
                rv = reshape_array(container, fd, adev, st,
-                                  content, force, NULL, 0ULL,
+                                  content, force, NULL, INVALID_SECTORS,
                                   backup_file, verbose, 1, restart,
                                   freeze_reshape);
                close(fd);
@@ -3507,7 +3528,7 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
                     unsigned long long backup_point,
                     unsigned long long wait_point,
                     unsigned long long *suspend_point,
-                    unsigned long long *reshape_completed)
+                    unsigned long long *reshape_completed, int *frozen)
 {
        /* This function is called repeatedly by the reshape manager.
         * It determines how much progress can safely be made and allows
@@ -3724,7 +3745,8 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
                wait_point = info->component_size - wait_point;
        }
 
-       sysfs_set_num(info, NULL, "sync_max", max_progress);
+       if (!*frozen)
+               sysfs_set_num(info, NULL, "sync_max", max_progress);
 
        /* Now wait.  If we have already reached the point that we were
         * asked to wait to, don't wait at all, else wait for any change.
@@ -3744,7 +3766,6 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
                 * waiting forever on a dead array
                 */
                char action[20];
-               fd_set rfds;
                if (sysfs_get_str(info, NULL, "sync_action",
                                  action, 20) <= 0 ||
                    strncmp(action, "reshape", 7) != 0)
@@ -3760,9 +3781,7 @@ int progress_reshape(struct mdinfo *info, struct reshape *reshape,
                    && 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);
+               sysfs_wait(fd, NULL);
                if (sysfs_fd_get_ll(fd, &completed) < 0)
                        goto check_progress;
        }
@@ -3807,23 +3826,24 @@ check_progress:
                /* The abort might only be temporary.  Wait up to 10
                 * seconds for fd to contain a valid number again.
                 */
-               struct timeval tv;
+               int wait = 10000;
                int rv = -2;
-               tv.tv_sec = 10;
-               tv.tv_usec = 0;
-               while (fd >= 0 && rv < 0 && tv.tv_sec > 0) {
-                       fd_set rfds;
-                       FD_ZERO(&rfds);
-                       FD_SET(fd, &rfds);
-                       if (select(fd+1, NULL, NULL, &rfds, &tv) != 1)
+               unsigned long long new_sync_max;
+               while (fd >= 0 && rv < 0 && wait > 0) {
+                       if (sysfs_wait(fd, &wait) != 1)
                                break;
                        switch (sysfs_fd_get_ll(fd, &completed)) {
                        case 0:
                                /* all good again */
                                rv = 1;
+                               /* If "sync_max" is no longer max_progress
+                                * we need to freeze things
+                                */
+                               sysfs_get_ll(info, NULL, "sync_max", &new_sync_max);
+                               *frozen = (new_sync_max != max_progress);
                                break;
                        case -2: /* read error - abort */
-                               tv.tv_sec = 0;
+                               wait = 0;
                                break;
                        }
                }
@@ -4115,6 +4135,7 @@ int child_monitor(int afd, struct mdinfo *sra, struct reshape *reshape,
        struct mdinfo *sd;
        unsigned long stripes;
        int uuid[4];
+       int frozen = 0;
 
        /* set up the backup-super-block.  This requires the
         * uuid from the array.
@@ -4192,9 +4213,11 @@ int child_monitor(int afd, struct mdinfo *sra, struct reshape *reshape,
                                wait_point = __le64_to_cpu(bsb.arraystart2);
                }
 
+               reshape_completed = sra->reshape_progress;
                rv = progress_reshape(sra, reshape,
                                      backup_point, wait_point,
-                                     &suspend_point, &reshape_completed);
+                                     &suspend_point, &reshape_completed,
+                                     &frozen);
                /* external metadata would need to ping_monitor here */
                sra->reshape_progress = reshape_completed;
 
@@ -4825,7 +4848,8 @@ int Grow_continue(int mdfd, struct supertype *st, struct mdinfo *info,
                                            freeze_reshape);
        } else
                ret_val = reshape_array(NULL, mdfd, "array", st, info, 1,
-                                       NULL, 0ULL, backup_file, 0, 0,
+                                       NULL, INVALID_SECTORS,
+                                       backup_file, 0, 0,
                                        1 | info->reshape_active,
                                        freeze_reshape);