]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Grow.c
Grow: just pass delta_disks instead of all of 'info'.
[thirdparty/mdadm.git] / Grow.c
diff --git a/Grow.c b/Grow.c
index f6fe1683234f2393f23dabf4e74dd1dc31e0b9e9..2bf9d64983a79d0dec2c9a9b5805ce4413dc6400 100644 (file)
--- a/Grow.c
+++ b/Grow.c
@@ -382,7 +382,7 @@ int Grow_addbitmap(char *devname, int fd, struct context *c, struct shape *s)
                                "with %s metadata\n", st->ss->name);
                        return 1;
                }
-               mdi = sysfs_read(fd, -1, GET_BITMAP_LOCATION);
+               mdi = sysfs_read(fd, NULL, GET_BITMAP_LOCATION);
                if (mdi)
                        offset_setable = 1;
                for (d=0; d< st->max_devs; d++) {
@@ -421,7 +421,7 @@ int Grow_addbitmap(char *devname, int fd, struct context *c, struct shape *s)
                }
                if (offset_setable) {
                        st->ss->getinfo_super(st, mdi, NULL);
-                       sysfs_init(mdi, fd, -1);
+                       sysfs_init(mdi, fd, NULL);
                        rv = sysfs_set_num_signed(mdi, NULL, "bitmap/location",
                                                  mdi->bitmap_offset);
                } else {
@@ -533,13 +533,11 @@ static int check_idle(struct supertype *st)
        /* Check that all member arrays for this container, or the
         * container of this array, are idle
         */
-       int container_dev = (st->container_dev != NoMdDev
-                            ? st->container_dev : st->devnum);
-       char container[40];
+       char *container = (st->container_devnm[0]
+                          ? st->container_devnm : st->devnm);
        struct mdstat_ent *ent, *e;
        int is_idle = 1;
 
-       fmt_devname(container, container_dev);
        ent = mdstat_read(0, 0);
        for (e = ent ; e; e = e->next) {
                if (!is_container_member(e, container))
@@ -555,15 +553,12 @@ static int check_idle(struct supertype *st)
 
 static int freeze_container(struct supertype *st)
 {
-       int container_dev = (st->container_dev != NoMdDev
-                            ? st->container_dev : st->devnum);
-       char container[40];
+       char *container = (st->container_devnm[0]
+                          ? st->container_devnm : st->devnm);
 
        if (!check_idle(st))
                return -1;
 
-       fmt_devname(container, container_dev);
-
        if (block_monitor(container, 1)) {
                pr_err("failed to freeze container\n");
                return -2;
@@ -574,11 +569,8 @@ static int freeze_container(struct supertype *st)
 
 static void unfreeze_container(struct supertype *st)
 {
-       int container_dev = (st->container_dev != NoMdDev
-                            ? st->container_dev : st->devnum);
-       char container[40];
-
-       fmt_devname(container, container_dev);
+       char *container = (st->container_devnm[0]
+                          ? st->container_devnm : st->devnm);
 
        unblock_monitor(container, 1);
 }
@@ -594,7 +586,7 @@ static int freeze(struct supertype *st)
        if (st->ss->external)
                return freeze_container(st);
        else {
-               struct mdinfo *sra = sysfs_read(-1, st->devnum, GET_VERSION);
+               struct mdinfo *sra = sysfs_read(-1, st->devnm, GET_VERSION);
                int err;
                char buf[20];
 
@@ -616,7 +608,7 @@ static void unfreeze(struct supertype *st)
        if (st->ss->external)
                return unfreeze_container(st);
        else {
-               struct mdinfo *sra = sysfs_read(-1, st->devnum, GET_VERSION);
+               struct mdinfo *sra = sysfs_read(-1, st->devnm, GET_VERSION);
 
                if (sra)
                        sysfs_set_str(sra, NULL, "sync_action", "idle");
@@ -938,6 +930,17 @@ 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)
 {
@@ -950,12 +953,7 @@ unsigned long compute_backup_blocks(int nchunk, int ochunk,
        a = (ochunk/512) * odata;
        b = (nchunk/512) * ndata;
        /* Find GCD */
-       while (a != b) {
-               if (a < b)
-                       b -= a;
-               if (b < a)
-                       a -= b;
-       }
+       a = GCD(a, b);
        /* LCM == product / GCD */
        blocks = (ochunk/512) * (nchunk/512) * odata * ndata / a;
 
@@ -979,7 +977,9 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
         * This can be called as part of starting a reshape, or
         * when assembling an array that is undergoing reshape.
         */
+       int near, far, offset, copies;
        int new_disks;
+       int old_chunk, new_chunk;
        /* delta_parity records change in number of devices
         * caused by level change
         */
@@ -1066,38 +1066,96 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
                return "Impossibly level change request for RAID1";
 
        case 10:
-               /* RAID10 can only be converted from near mode to
-                * RAID0 by removing some devices
+               /* RAID10 can be converted from near mode to
+                * RAID0 by removing some devices.
+                * It can also be reshaped if the kernel supports
+                * new_data_offset.
                 */
-               if ((info->array.layout & ~0xff) != 0x100)
-                       return "Cannot Grow RAID10 with far/offset layout";
-               /* number of devices must be multiple of number of copies */
-               if (info->array.raid_disks % (info->array.layout & 0xff))
-                       return "RAID10 layout too complex for Grow operation";
+               switch (info->new_level) {
+               case 0:
+                       if ((info->array.layout & ~0xff) != 0x100)
+                               return "Cannot Grow RAID10 with far/offset layout";
+                       /* number of devices must be multiple of number of copies */
+                       if (info->array.raid_disks % (info->array.layout & 0xff))
+                               return "RAID10 layout too complex for Grow operation";
+
+                       new_disks = (info->array.raid_disks
+                                    / (info->array.layout & 0xff));
+                       if (info->delta_disks == UnSet)
+                               info->delta_disks = (new_disks
+                                                    - info->array.raid_disks);
 
-               if (info->new_level != 0)
-                       return "RAID10 can only be changed to RAID0";
-               new_disks = (info->array.raid_disks
-                            / (info->array.layout & 0xff));
-               if (info->delta_disks == UnSet)
-                       info->delta_disks = (new_disks
-                                            - info->array.raid_disks);
-
-               if (info->delta_disks != new_disks - info->array.raid_disks)
-                       return "New number of raid-devices impossible for RAID10";
-               if (info->new_chunk &&
-                   info->new_chunk != info->array.chunk_size)
-                       return "Cannot change chunk-size with RAID10 Grow";
-
-               /* looks good */
-               re->level = 0;
-               re->parity = 0;
-               re->before.data_disks = new_disks;
-               re->after.data_disks = re->before.data_disks;
-               re->before.layout = 0;
-               re->backup_blocks = 0;
-               return NULL;
+                       if (info->delta_disks != new_disks - info->array.raid_disks)
+                               return "New number of raid-devices impossible for RAID10";
+                       if (info->new_chunk &&
+                           info->new_chunk != info->array.chunk_size)
+                               return "Cannot change chunk-size with RAID10 Grow";
 
+                       /* looks good */
+                       re->level = 0;
+                       re->parity = 0;
+                       re->before.data_disks = new_disks;
+                       re->after.data_disks = re->before.data_disks;
+                       re->before.layout = 0;
+                       re->backup_blocks = 0;
+                       return NULL;
+
+               case 10:
+                       near = info->array.layout & 0xff;
+                       far = (info->array.layout >> 8) & 0xff;
+                       offset = info->array.layout & 0x10000;
+                       if (far > 1 && !offset)
+                               return "Cannot reshape RAID10 in far-mode";
+                       copies = near * far;
+
+                       old_chunk = info->array.chunk_size * far;
+
+                       if (info->new_layout == UnSet)
+                               info->new_layout = info->array.layout;
+                       else {
+                               near = info->new_layout & 0xff;
+                               far = (info->new_layout >> 8) & 0xff;
+                               offset = info->new_layout & 0x10000;
+                               if (far > 1 && !offset)
+                                       return "Cannot reshape RAID10 to far-mode";
+                               if (near * far != copies)
+                                       return "Cannot change number of copies"
+                                               " when reshaping RAID10";
+                       }
+                       if (info->delta_disks == UnSet)
+                               info->delta_disks = 0;
+                       new_disks = (info->array.raid_disks +
+                                    info->delta_disks);
+
+                       new_chunk = info->new_chunk * far;
+
+                       re->level = 10;
+                       re->parity = 0;
+                       re->before.layout = info->array.layout;
+                       re->before.data_disks = info->array.raid_disks;
+                       re->after.layout = info->new_layout;
+                       re->after.data_disks = new_disks;
+                       /* For RAID10 we don't do backup, and there is
+                        * no need to synchronise stripes on both
+                        * 'old' and  'new'.  So the important
+                        * number is the minimum data_offset difference
+                        * which is the larger of (offset copies * chunk).
+                        */
+
+                       re->backup_blocks = max(old_chunk, new_chunk) / 512;
+                       if (new_disks < re->before.data_disks &&
+                           info->space_after < re->backup_blocks)
+                               /* Reduce component size by one chunk */
+                               re->new_size = (info->component_size -
+                                               re->backup_blocks);
+                       else
+                               re->new_size = info->component_size;
+                       re->new_size = re->new_size * new_disks / copies;
+                       return NULL;
+
+               default:
+                       return "RAID10 can only be changed to RAID0";
+               }
        case 0:
                /* RAID0 can be converted to RAID10, or to RAID456 */
                if (info->new_level == 10) {
@@ -1215,7 +1273,9 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
                                return "Cannot set raid_disk when "
                                        "converting RAID5->RAID1";
                        re->level = 1;
-                       break;
+                       re->backup_blocks = 0;
+                       info->new_chunk = 0;
+                       return NULL;
                default:
                        return "Impossible level change requested";
                }
@@ -1253,6 +1313,7 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
 
                switch (re->level) {
                case 4:
+                       re->before.layout = 0;
                        re->after.layout = 0;
                        break;
                case 5:
@@ -1268,6 +1329,7 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
 
                switch (re->level) {
                case 4:
+                       re->before.layout = 0;
                        re->after.layout = 0;
                        break;
                case 5:
@@ -1360,12 +1422,14 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
        if (re->after.data_disks == re->before.data_disks &&
            re->after.layout == re->before.layout &&
            info->new_chunk == info->array.chunk_size) {
-               /* Nothing to change */
+               /* Nothing to change, can change level immediately. */
+               re->level = info->new_level;
                re->backup_blocks = 0;
                return NULL;
        }
        if (re->after.data_disks == 1 && re->before.data_disks == 1) {
                /* chunk and layout changes make no difference */
+               re->level = info->new_level;
                re->backup_blocks = 0;
                return NULL;
        }
@@ -1428,6 +1492,7 @@ static int set_array_size(struct supertype *st, struct mdinfo *sra,
 static int reshape_array(char *container, int fd, char *devname,
                         struct supertype *st, struct mdinfo *info,
                         int force, struct mddev_dev *devlist,
+                        unsigned long long data_offset,
                         char *backup_file, int verbose, int forked,
                         int restart, int freeze_reshape);
 static int reshape_container(char *container, char *devname,
@@ -1468,7 +1533,6 @@ int Grow_reshape(char *devname, int fd,
        int frozen;
        int changed = 0;
        char *container = NULL;
-       char container_buf[20];
        int cfd = -1;
 
        struct mddev_dev *dv;
@@ -1477,16 +1541,16 @@ int Grow_reshape(char *devname, int fd,
        struct mdinfo info;
        struct mdinfo *sra;
 
-       if (data_offset != INVALID_SECTORS) {
-               fprintf(stderr, Name ": --grow --data-offset not yet supported\n");
-               return 1;
-       }
 
        if (ioctl(fd, GET_ARRAY_INFO, &array) < 0) {
-               pr_err("%s is not an active md array - aborting\n",
+               fprintf(stderr, Name ": %s is not an active md array - aborting\n",
                        devname);
                return 1;
        }
+       if (data_offset != INVALID_SECTORS && array.level != 10) {
+               pr_err("--grow --data-offset not yet supported\n");
+               return 1;
+       }
 
        if (s->size > 0 &&
            (s->chunk || s->level!= UnSet || s->layout_str || s->raiddisks)) {
@@ -1521,16 +1585,15 @@ int Grow_reshape(char *devname, int fd,
         * pre-requisite spare devices (mdmon owns final validation)
         */
        if (st->ss->external) {
-               int container_dev;
                int rv;
 
                if (subarray) {
-                       container_dev = st->container_dev;
-                       cfd = open_dev_excl(st->container_dev);
+                       container = st->container_devnm;
+                       cfd = open_dev_excl(st->container_devnm);
                } else {
-                       container_dev = st->devnum;
+                       container = st->devnm;
                        close(fd);
-                       cfd = open_dev_excl(st->devnum);
+                       cfd = open_dev_excl(st->devnm);
                        fd = cfd;
                }
                if (cfd < 0) {
@@ -1540,9 +1603,6 @@ int Grow_reshape(char *devname, int fd,
                        return 1;
                }
 
-               fmt_devname(container_buf, container_dev);
-               container = container_buf;
-
                rv = st->ss->load_container(st, cfd, NULL);
 
                if (rv) {
@@ -1573,7 +1633,7 @@ int Grow_reshape(char *devname, int fd,
                                        pr_err("cannot reshape arrays in"
                                               " container with unsupported"
                                               " metadata: %s(%s)\n",
-                                              devname, container_buf);
+                                              devname, container);
                                        sysfs_free(cc);
                                        free(subarray);
                                        return 1;
@@ -1581,7 +1641,7 @@ int Grow_reshape(char *devname, int fd,
                        }
                        sysfs_free(cc);
                }
-               if (mdmon_running(container_dev))
+               if (mdmon_running(container))
                        st->update_tail = &st->updates;
        }
 
@@ -1600,7 +1660,7 @@ int Grow_reshape(char *devname, int fd,
                return 1;
        }
 
-       sra = sysfs_read(fd, 0, GET_LEVEL | GET_DISKS | GET_DEVS
+       sra = sysfs_read(fd, NULL, GET_LEVEL | GET_DISKS | GET_DEVS
                         | GET_STATE | GET_VERSION);
        if (sra) {
                if (st->ss->external && subarray == NULL) {
@@ -1634,6 +1694,12 @@ int Grow_reshape(char *devname, int fd,
                if (orig_size == 0)
                        orig_size = (unsigned) array.size;
 
+               if (orig_size == 0) {
+                       pr_err("Cannot set device size in this type of array.\n");
+                       rv = 1;
+                       goto release;
+               }
+
                if (reshape_super(st, s->size, UnSet, UnSet, 0, 0, UnSet, NULL,
                                  devname, APPLY_METADATA_CHANGES, c->verbose > 0)) {
                        rv = 1;
@@ -1724,10 +1790,10 @@ int Grow_reshape(char *devname, int fd,
                        }
                        /* make sure mdmon is
                         * aware of the new level */
-                       if (!mdmon_running(st->container_dev))
-                               start_mdmon(st->container_dev);
+                       if (!mdmon_running(st->container_devnm))
+                               start_mdmon(st->container_devnm);
                        ping_monitor(container);
-                       if (mdmon_running(st->container_dev) &&
+                       if (mdmon_running(st->container_devnm) &&
                                        st->update_tail == NULL)
                                st->update_tail = &st->updates;
                }
@@ -1783,12 +1849,12 @@ size_change_error:
                        goto release;
                }
                if (s->assume_clean) {
-                       /* This will fail on kernels newer than 3.0 unless
+                       /* This will fail on kernels older than 3.0 unless
                         * a backport has been arranged.
                         */
                        if (sra == NULL ||
                            sysfs_set_str(sra, NULL, "resync_start", "none") < 0)
-                               pr_err("--assume-clean not support with --grow on this kernel\n");
+                               pr_err("--assume-clean not supported with --grow on this kernel\n");
                }
                ioctl(fd, GET_ARRAY_INFO, &array);
                s->size = get_component_size(fd)/2;
@@ -1851,7 +1917,7 @@ size_change_error:
 
        memset(&info, 0, sizeof(info));
        info.array = array;
-       sysfs_init(&info, fd, NoMdDev);
+       sysfs_init(&info, fd, NULL);
        strcpy(info.text_version, sra->text_version);
        info.component_size = s->size*2;
        info.new_level = s->level;
@@ -2015,7 +2081,8 @@ size_change_error:
                }
                sync_metadata(st);
                rv = reshape_array(container, fd, devname, st, &info, c->force,
-                                  devlist, c->backup_file, c->verbose, 0, 0, 0);
+                                  devlist, data_offset, c->backup_file, c->verbose,
+                                  0, 0, 0);
                frozen = 0;
        }
 release:
@@ -2082,9 +2149,295 @@ static int verify_reshape_position(struct mdinfo *info, int level)
        return ret_val;
 }
 
+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)
+{
+       struct mdinfo *sd;
+       int dir = 0;
+       int err = 0;
+
+       for (sd = sra->devs; sd; sd = sd->next) {
+               char *dn;
+               int dfd;
+               int rv;
+               struct supertype *st2;
+               struct mdinfo info2;
+
+               if (sd->disk.state & (1<<MD_DISK_FAULTY))
+                       continue;
+               dn = map_dev(sd->disk.major, sd->disk.minor, 0);
+               dfd = dev_open(dn, O_RDONLY);
+               if (dfd < 0) {
+                       fprintf(stderr,
+                               Name ": %s: cannot open component %s\n",
+                               devname, dn ? dn : "-unknown-");
+                       rv = -1;
+                       goto release;
+               }
+               st2 = dup_super(st);
+               rv = st2->ss->load_super(st2,dfd, NULL);
+               close(dfd);
+               if (rv) {
+                       free(st2);
+                       fprintf(stderr, ": %s: cannot get superblock from %s\n",
+                               devname, dn);
+                       goto release;
+               }
+               st2->ss->getinfo_super(st2, &info2, NULL);
+               st2->ss->free_super(st2);
+               free(st2);
+               if (delta_disks < 0) {
+                       /* Don't need any space as array is shrinking
+                        * just move data_offset up by min
+                        */
+                       if (data_offset == INVALID_SECTORS)
+                               info2.new_data_offset = info2.data_offset + min;
+                       else {
+                               if ((unsigned long long)data_offset
+                                   < info2.data_offset + min) {
+                                       fprintf(stderr, Name ": --data-offset too small for %s\n",
+                                               dn);
+                                       goto release;
+                               }
+                               info2.new_data_offset = data_offset;
+                       }
+               } else if (delta_disks > 0) {
+                       /* need space before */
+                       if (info2.space_before < min) {
+                               fprintf(stderr, Name ": Insufficient head-space for reshape on %s\n",
+                                       dn);
+                               goto release;
+                       }
+                       if (data_offset == INVALID_SECTORS)
+                               info2.new_data_offset = info2.data_offset - min;
+                       else {
+                               if ((unsigned long long)data_offset
+                                   > info2.data_offset - min) {
+                                       fprintf(stderr, Name ": --data-offset too large for %s\n",
+                                               dn);
+                                       goto release;
+                               }
+                               info2.new_data_offset = data_offset;
+                       }
+               } else {
+                       if (dir == 0) {
+                               /* can move up or down. 'data_offset'
+                                * might guide us, otherwise choose
+                                * direction with most space
+                                */
+                               if (data_offset == INVALID_SECTORS) {
+                                       if (info2.space_before > info2.space_after)
+                                               dir = -1;
+                                       else
+                                               dir = 1;
+                               } else if (data_offset < info2.data_offset)
+                                       dir = -1;
+                               else
+                                       dir = 1;
+                               sysfs_set_str(sra, NULL, "reshape_direction",
+                                             dir == 1 ? "backwards" : "forwards");
+                       }
+                       switch (dir) {
+                       case 1: /* Increase data offset */
+                               if (info2.space_after < min) {
+                                       fprintf(stderr, Name ": Insufficient tail-space for reshape on %s\n",
+                                               dn);
+                                       goto release;
+                               }
+                               if (data_offset != INVALID_SECTORS &&
+                                   data_offset < info2.data_offset + min) {
+                                       fprintf(stderr, Name ": --data-offset too small on %s\n",
+                                               dn);
+                                       goto release;
+                               }
+                               if (data_offset != INVALID_SECTORS)
+                                       info2.new_data_offset = data_offset;
+                               else {
+                                       unsigned long long off =
+                                               info2.space_after / 2;
+                                       off &= ~7ULL;
+                                       if (off < min)
+                                               off = min;
+                                       info2.new_data_offset =
+                                               info2.data_offset + off;
+                               }
+                               break;
+                       case -1: /* Decrease data offset */
+                               if (info2.space_before < min) {
+                                       fprintf(stderr, Name ": insufficient head-room on %s\n",
+                                               dn);
+                                       goto release;
+                               }
+                               if (data_offset != INVALID_SECTORS &&
+                                   data_offset < info2.data_offset - min) {
+                                       fprintf(stderr, Name ": --data-offset too small on %s\n",
+                                               dn);
+                                       goto release;
+                               }
+                               if (data_offset != INVALID_SECTORS)
+                                       info2.new_data_offset = data_offset;
+                               else {
+                                       unsigned long long off =
+                                               info2.space_before / 2;
+                                       off &= ~7ULL;
+                                       if (off < min)
+                                               off = min;
+                                       info2.new_data_offset =
+                                               info2.data_offset - off;
+                               }
+                               break;
+                       }
+               }
+               if (sysfs_set_num(sra, sd, "new_offset",
+                                 info2.new_data_offset) < 0) {
+                       err = errno;
+                       fprintf(stderr, Name ": Cannot set new_offset for %s\n",
+                               dn);
+                       break;
+               }
+       }
+       return err;
+release:
+       return -1;
+}
+
+static int raid10_reshape(char *container, int fd, char *devname,
+                         struct supertype *st, struct mdinfo *info,
+                         struct reshape *reshape,
+                         unsigned long long data_offset,
+                         int force, int verbose)
+{
+       /* Changing raid_disks, layout, chunksize or possibly
+        * just data_offset for a RAID10.
+        * We must always change data_offset.  We change by at least
+        * ->backup_blocks which is the largest of the old and new
+        * chunk sizes.
+        * If raid_disks is increasing, then data_offset must decrease
+        * by at least this copy size.
+        * If raid_disks is unchanged, data_offset must increase or
+        * decrease by at least backup_blocks but preferably by much more.
+        * We choose half of the available space.
+        * If raid_disks is decreasing, data_offset must increase by
+        * at least backup_blocks.  To allow of this, component_size
+        * must be decreased by the same amount.
+        *
+        * So we calculate the required minimum and direction, possibly
+        * reduce the component_size, then iterate through the devices
+        * and set the new_data_offset.
+        * If that all works, we set chunk_size, layout, raid_disks, and start
+        * 'reshape'
+        */
+       struct mdinfo *sra;
+       unsigned long long min;
+       int err = 0;
+
+       sra = sysfs_read(fd, NULL,
+                        GET_COMPONENT|GET_DEVS|GET_OFFSET|GET_STATE|GET_CHUNK
+               );
+       if (!sra) {
+               fprintf(stderr, Name ": %s: Cannot get array details from sysfs\n",
+                       devname);
+               goto release;
+       }
+       min = reshape->backup_blocks;
+
+       if (info->delta_disks)
+               sysfs_set_str(sra, NULL, "reshape_direction",
+                             info->delta_disks < 0 ? "backwards" : "forwards");
+       if (info->delta_disks < 0 &&
+           info->space_after < reshape->backup_blocks) {
+               int rv = sysfs_set_num(sra, NULL, "component_size",
+                                      (sra->component_size -
+                                       reshape->backup_blocks)/2);
+               if (rv) {
+                       fprintf(stderr, Name ": cannot reduce component size\n");
+                       goto release;
+               }
+       }
+       err = set_new_data_offset(sra, st, devname, info->delta_disks, data_offset,
+                                 min);
+       if (err < 0)
+               goto release;
+
+       if (!err && sysfs_set_num(sra, NULL, "chunk_size", info->new_chunk) < 0)
+               err = errno;
+       if (!err && sysfs_set_num(sra, NULL, "layout", reshape->after.layout) < 0)
+               err = errno;
+       if (!err && sysfs_set_num(sra, NULL, "raid_disks",
+                                 info->array.raid_disks + info->delta_disks) < 0)
+               err = errno;
+       if (!err && sysfs_set_str(sra, NULL, "sync_action", "reshape") < 0)
+               err = errno;
+       if (err) {
+               fprintf(stderr, Name ": Cannot set array shape for %s\n",
+                       devname);
+                       if (err == EBUSY &&
+                           (info->array.state & (1<<MD_SB_BITMAP_PRESENT)))
+                               fprintf(stderr,
+                                       "       Bitmap must be removed before"
+                                       " shape can be changed\n");
+                       goto release;
+       }
+       sysfs_free(sra);
+       return 0;
+release:
+       sysfs_free(sra);
+       return 1;
+}
+
+static void get_space_after(int fd, struct supertype *st, struct mdinfo *info)
+{
+       struct mdinfo *sra, *sd;
+       /* Initialisation to silence compiler warning */
+       unsigned long long min_space_before = 0, min_space_after = 0;
+       int first = 1;
+
+       sra = sysfs_read(fd, NULL, GET_DEVS);
+       if (!sra)
+               return;
+       for (sd = sra->devs; sd; sd = sd->next) {
+               char *dn;
+               int dfd;
+               struct supertype *st2;
+               struct mdinfo info2;
+
+               if (sd->disk.state & (1<<MD_DISK_FAULTY))
+                       continue;
+               dn = map_dev(sd->disk.major, sd->disk.minor, 0);
+               dfd = dev_open(dn, O_RDONLY);
+               if (dfd < 0)
+                       break;
+               st2 = dup_super(st);
+               if (st2->ss->load_super(st2,dfd, NULL)) {
+                       close(dfd);
+                       free(st2);
+                       break;
+               }
+               close(dfd);
+               st2->ss->getinfo_super(st2, &info2, NULL);
+               st2->ss->free_super(st2);
+               free(st2);
+               if (first ||
+                   min_space_before > info2.space_before)
+                       min_space_before = info2.space_before;
+               if (first ||
+                   min_space_after > info2.space_after)
+                       min_space_after = info2.space_after;
+               first = 0;
+       }
+       if (sd == NULL && !first) {
+               info->space_after = min_space_after;
+               info->space_before = min_space_before;
+       }
+       sysfs_free(sra);
+}
+
 static int reshape_array(char *container, int fd, char *devname,
                         struct supertype *st, struct mdinfo *info,
                         int force, struct mddev_dev *devlist,
+                        unsigned long long data_offset,
                         char *backup_file, int verbose, int forked,
                         int restart, int freeze_reshape)
 {
@@ -2124,6 +2477,10 @@ static int reshape_array(char *container, int fd, char *devname,
                info->component_size = array_size / array.raid_disks;
        }
 
+       if (array.level == 10)
+               /* Need space_after info */
+               get_space_after(fd, st, info);
+
        if (info->reshape_active) {
                int new_level = info->new_level;
                info->new_level = UnSet;
@@ -2234,13 +2591,13 @@ static int reshape_array(char *container, int fd, char *devname,
 
                if (reshape.level > 0 && st->ss->external) {
                        /* make sure mdmon is aware of the new level */
-                       if (mdmon_running(st->container_dev))
+                       if (mdmon_running(container))
                                flush_mdmon(container);
 
-                       if (!mdmon_running(st->container_dev))
-                               start_mdmon(st->container_dev);
+                       if (!mdmon_running(container))
+                               start_mdmon(container);
                        ping_monitor(container);
-                       if (mdmon_running(st->container_dev) &&
+                       if (mdmon_running(container) &&
                            st->update_tail == NULL)
                                st->update_tail = &st->updates;
                }
@@ -2257,7 +2614,7 @@ static int reshape_array(char *container, int fd, char *devname,
                struct mdinfo *d;
 
                if (info2) {
-                       sysfs_init(info2, fd, st->devnum);
+                       sysfs_init(info2, fd, st->devnm);
                        /* When increasing number of devices, we need to set
                         * new raid_disks before adding these, or they might
                         * be rejected.
@@ -2367,7 +2724,6 @@ static int reshape_array(char *container, int fd, char *devname,
         *   -  request the shape change.
         *   -  fork to handle backup etc.
         */
-started:
        /* Check that we can hold all the data */
        get_dev_size(fd, NULL, &array_size);
        if (reshape.new_size < (array_size/512)) {
@@ -2378,7 +2734,22 @@ started:
                goto release;
        }
 
-       sra = sysfs_read(fd, 0,
+started:
+
+       if (array.level == 10) {
+               /* Reshaping RAID10 does not require and data backup by
+                * user-space.  Instead it requires that the data_offset
+                * is changed to avoid the need for backup.
+                * So this is handled very separately
+                */
+               if (restart)
+                       /* Nothing to do. */
+                       return 0;
+               return raid10_reshape(container, fd, devname, st, info,
+                                     &reshape, data_offset,
+                                     force, verbose);
+       }
+       sra = sysfs_read(fd, NULL,
                         GET_COMPONENT|GET_DEVS|GET_OFFSET|GET_STATE|GET_CHUNK|
                         GET_CACHE);
        if (!sra) {
@@ -2591,8 +2962,8 @@ started:
                struct mdstat_ent *mds, *m;
                delayed = 0;
                mds = mdstat_read(0, 0);
-               for (m = mds; m; m = mds->next)
-                       if (m->devnum == devname2devnum(sra->sys_name)) {
+               for (m = mds; m; m = m->next)
+                       if (strcmp(m->devnm, sra->sys_name) == 0) {
                                if (m->resync &&
                                    m->percent == RESYNC_DELAYED)
                                        delayed = 1;
@@ -2659,7 +3030,7 @@ started:
 
        if (st->ss->external) {
                /* Re-load the metadata as much could have changed */
-               int cfd = open_dev(st->container_dev);
+               int cfd = open_dev(st->container_devnm);
                if (cfd >= 0) {
                        flush_mdmon(container);
                        st->ss->free_super(st);
@@ -2721,7 +3092,7 @@ int reshape_container(char *container, char *devname,
 {
        struct mdinfo *cc = NULL;
        int rv = restart;
-       int last_devnum = -1;
+       char last_devnm[32] = "";
 
        /* component_size is not meaningful for a container,
         * so pass '0' meaning 'no change'
@@ -2778,6 +3149,7 @@ int reshape_container(char *container, char *devname,
                int fd;
                struct mdstat_ent *mdstat;
                char *adev;
+               int devid;
 
                sysfs_free(cc);
 
@@ -2789,13 +3161,12 @@ int reshape_container(char *container, char *devname,
                                continue;
 
                        subarray = strchr(content->text_version+1, '/')+1;
-                       mdstat = mdstat_by_subdev(subarray,
-                                                 devname2devnum(container));
+                       mdstat = mdstat_by_subdev(subarray, container);
                        if (!mdstat)
                                continue;
                        if (mdstat->active == 0) {
-                               pr_err("Skipping inactive "
-                                       "array md%i.\n", mdstat->devnum);
+                               pr_err("Skipping inactive array %s.\n",
+                                      mdstat->devnm);
                                free_mdstat(mdstat);
                                mdstat = NULL;
                                continue;
@@ -2805,20 +3176,19 @@ int reshape_container(char *container, char *devname,
                if (!content)
                        break;
 
-               adev = map_dev(dev2major(mdstat->devnum),
-                              dev2minor(mdstat->devnum),
-                              0);
+               devid = devnm2devid(mdstat->devnm);
+               adev = map_dev(major(devid), minor(devid), 0);
                if (!adev)
                        adev = content->text_version;
 
-               fd = open_dev(mdstat->devnum);
+               fd = open_dev(mdstat->devnm);
                if (fd < 0) {
                        printf(Name ": Device %s cannot be opened for reshape.",
                               adev);
                        break;
                }
 
-               if (last_devnum == mdstat->devnum) {
+               if (strcmp(last_devnm, mdstat->devnm) == 0) {
                        /* Do not allow for multiple reshape_array() calls for
                         * the same array.
                         * It can happen when reshape_array() returns without
@@ -2834,15 +3204,15 @@ int reshape_container(char *container, char *devname,
                        close(fd);
                        break;
                }
-               last_devnum = mdstat->devnum;
+               strcpy(last_devnm, mdstat->devnm);
 
-               sysfs_init(content, fd, mdstat->devnum);
+               sysfs_init(content, fd, mdstat->devnm);
 
-               if (mdmon_running(devname2devnum(container)))
+               if (mdmon_running(container))
                        flush_mdmon(container);
 
                rv = reshape_array(container, fd, adev, st,
-                                  content, force, NULL,
+                                  content, force, NULL, 0ULL,
                                   backup_file, verbose, 1, restart,
                                   freeze_reshape);
                close(fd);
@@ -2856,7 +3226,7 @@ int reshape_container(char *container, char *devname,
                if (rv)
                        break;
 
-               if (mdmon_running(devname2devnum(container)))
+               if (mdmon_running(container))
                        flush_mdmon(container);
        }
        if (!rv)
@@ -4002,7 +4372,6 @@ int Grow_continue_command(char *devname, int fd,
        char *subarray = NULL;
        struct mdinfo *cc = NULL;
        struct mdstat_ent *mdstat = NULL;
-       char buf[40];
        int cfd = -1;
        int fd2 = -1;
 
@@ -4017,27 +4386,61 @@ int Grow_continue_command(char *devname, int fd,
        }
        dprintf("Grow continue is run for ");
        if (st->ss->external == 0) {
+               int d;
                dprintf("native array (%s)\n", devname);
-               if (ioctl(fd, GET_ARRAY_INFO, &array) < 0) {
+               if (ioctl(fd, GET_ARRAY_INFO, &array.array) < 0) {
                        pr_err("%s is not an active md array -"
                                " aborting\n", devname);
                        ret_val = 1;
                        goto Grow_continue_command_exit;
                }
                content = &array;
-               sysfs_init(content, fd, st->devnum);
+               /* Need to load a superblock.
+                * FIXME we should really get what we need from
+                * sysfs
+                */
+               for (d = 0; d < MAX_DISKS; d++) {
+                       mdu_disk_info_t disk;
+                       char *dv;
+                       int err;
+                       disk.number = d;
+                       if (ioctl(fd, GET_DISK_INFO, &disk) < 0)
+                               continue;
+                       if (disk.major == 0 && disk.minor == 0)
+                               continue;
+                       if ((disk.state & (1 << MD_DISK_ACTIVE)) == 0)
+                               continue;
+                       dv = map_dev(disk.major, disk.minor, 1);
+                       if (!dv)
+                               continue;
+                       fd2 = dev_open(dv, O_RDONLY);
+                       if (fd2 < 0)
+                               continue;
+                       err = st->ss->load_super(st, fd2, NULL);
+                       close(fd2);
+                       if (err)
+                               continue;
+                       break;
+               }
+               if (d == MAX_DISKS) {
+                       pr_err("Unable to load metadata for %s\n",
+                              devname);
+                       ret_val = 1;
+                       goto Grow_continue_command_exit;
+               }
+               st->ss->getinfo_super(st, content, NULL);
        } else {
-               int container_dev;
+               char *container;
 
                if (subarray) {
                        dprintf("subarray (%s)\n", subarray);
-                       container_dev = st->container_dev;
-                       cfd = open_dev_excl(st->container_dev);
+                       container = st->container_devnm;
+                       cfd = open_dev_excl(st->container_devnm);
                } else {
-                       container_dev = st->devnum;
+                       container = st->devnm;
                        close(fd);
-                       cfd = open_dev_excl(st->devnum);
-                       dprintf("container (%i)\n", container_dev);
+                       cfd = open_dev_excl(st->devnm);
+                       dprintf("container (%s)\n", container);
                        fd = cfd;
                }
                if (cfd < 0) {
@@ -4046,7 +4449,6 @@ int Grow_continue_command(char *devname, int fd,
                        ret_val = 1;
                        goto Grow_continue_command_exit;
                }
-               fmt_devname(buf, container_dev);
 
                /* find in container array under reshape
                 */
@@ -4082,18 +4484,18 @@ int Grow_continue_command(char *devname, int fd,
                                pr_err("cannot continue reshape of an array"
                                       " in container with unsupported"
                                       " metadata: %s(%s)\n",
-                                      devname, buf);
+                                      devname, container);
                                ret_val = 1;
                                goto Grow_continue_command_exit;
                        }
 
                        array = strchr(content->text_version+1, '/')+1;
-                       mdstat = mdstat_by_subdev(array, container_dev);
+                       mdstat = mdstat_by_subdev(array, container);
                        if (!mdstat)
                                continue;
                        if (mdstat->active == 0) {
-                               pr_err("Skipping inactive "
-                                       "array md%i.\n", mdstat->devnum);
+                               pr_err("Skipping inactive array %s.\n",
+                                      mdstat->devnm);
                                free_mdstat(mdstat);
                                mdstat = NULL;
                                continue;
@@ -4106,23 +4508,22 @@ int Grow_continue_command(char *devname, int fd,
                        ret_val = 1;
                        goto Grow_continue_command_exit;
                }
-               fd2 = open_dev(mdstat->devnum);
+               fd2 = open_dev(mdstat->devnm);
                if (fd2 < 0) {
-                       pr_err("cannot open (md%i)\n",
-                               mdstat->devnum);
+                       pr_err("cannot open (%s)\n", mdstat->devnm);
                        ret_val = 1;
                        goto Grow_continue_command_exit;
                }
 
-               sysfs_init(content, fd2, mdstat->devnum);
+               sysfs_init(content, fd2, mdstat->devnm);
 
                /* start mdmon in case it is not running
                 */
-               if (!mdmon_running(container_dev))
-                       start_mdmon(container_dev);
-               ping_monitor(buf);
+               if (!mdmon_running(container))
+                       start_mdmon(container);
+               ping_monitor(container);
 
-               if (mdmon_running(container_dev))
+               if (mdmon_running(container))
                        st->update_tail = &st->updates;
                else {
                        pr_err("No mdmon found. "
@@ -4135,8 +4536,7 @@ int Grow_continue_command(char *devname, int fd,
        /* verify that array under reshape is started from
         * correct position
         */
-       if (verify_reshape_position(content,
-                                   map_name(pers, mdstat->level)) < 0) {
+       if (verify_reshape_position(content, content->array.level) < 0) {
                ret_val = 1;
                goto Grow_continue_command_exit;
        }
@@ -4167,21 +4567,19 @@ int Grow_continue(int mdfd, struct supertype *st, struct mdinfo *info,
                return ret_val;
 
        if (st->ss->external) {
-               char container[40];
-               int cfd = open_dev(st->container_dev);
+               int cfd = open_dev(st->container_devnm);
 
                if (cfd < 0)
                        return 1;
 
-               fmt_devname(container, st->container_dev);
-               st->ss->load_container(st, cfd, container);
+               st->ss->load_container(st, cfd, st->container_devnm);
                close(cfd);
-               ret_val = reshape_container(container, NULL, mdfd,
+               ret_val = reshape_container(st->container_devnm, NULL, mdfd,
                                            st, info, 0, backup_file,
                                            0, 1, freeze_reshape);
        } else
                ret_val = reshape_array(NULL, mdfd, "array", st, info, 1,
-                                       NULL, backup_file, 0, 0, 1,
+                                       NULL, 0ULL, backup_file, 0, 0, 1,
                                        freeze_reshape);
 
        return ret_val;