]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
imsm: Prepare reshape_update in mdadm
authorNeilBrown <neilb@suse.de>
Thu, 16 Dec 2010 00:45:21 +0000 (11:45 +1100)
committerNeilBrown <neilb@suse.de>
Thu, 16 Dec 2010 00:45:21 +0000 (11:45 +1100)
During Online Capacity Expansion metadata has to be updated to show
array changes and allow for future assembly of array.  To do this
mdadm prepares and sends reshape_update metadata update to mdmon.
The update contains the old and new number of raid disks, and the
indices of the spare disks that will be used to fill the spaces.

This works as follows:
1. reshape_super() prepares metadata update.
2. mdadm discovers the spares and adds them to the array
3. mdadm sends the update to mdmon
4. managemon in prepare_update() allocates required memory for bigger
   device object
5. monitor in process_update() updates the metadata to record the
   new sizes and the newly assigned devices.
6. mdadm initiates the reshape

Based on code From: Adam Kwolek <adam.kwolek@intel.com>

Signed-off-by: Krzysztof Wojcik <krzysztof.wojcik@intel.com>
Signed-off-by: Adam Kwolek <adam.kwolek@intel.com>
Signed-off-by: NeilBrown <neilb@suse.de>
mdadm.h
mdstat.c
super-intel.c

diff --git a/mdadm.h b/mdadm.h
index 73db7ecb3d08216490cb3ad05f33becd9d217ebe..2062825405353a7ed19e634686d2b6847d600ca6 100644 (file)
--- a/mdadm.h
+++ b/mdadm.h
@@ -401,6 +401,7 @@ extern void mdstat_wait(int seconds);
 extern void mdstat_wait_fd(int fd, const sigset_t *sigmask);
 extern int mddev_busy(int devnum);
 extern struct mdstat_ent *mdstat_by_component(char *name);
+extern struct mdstat_ent *mdstat_by_subdev(char *subdev, int container);
 
 struct map_ent {
        struct map_ent *next;
index c5a07b5ba9d651f8dec74d9acdf4d2168528541a..bac374212e96444ceac9654b360fb9d99fb6a8fa 100644 (file)
--- a/mdstat.c
+++ b/mdstat.c
@@ -383,3 +383,36 @@ struct mdstat_ent *mdstat_by_component(char *name)
        }
        return NULL;
 }
+
+struct mdstat_ent *mdstat_by_subdev(char *subdev, int container)
+{
+       struct mdstat_ent *mdstat = mdstat_read(0, 0);
+
+       while (mdstat) {
+               struct mdstat_ent *ent;
+               char *pos;
+               /* metadata version must match:
+                *   external:[/-]md%d/%s
+                * where %d is 'container' and %s is 'subdev'
+                */
+               if (mdstat->metadata_version &&
+                   strncmp(mdstat->metadata_version, "external:", 9) == 0 &&
+                   strchr("/-", mdstat->metadata_version[9]) != NULL &&
+                   strncmp(mdstat->metadata_version+10, "md", 2) == 0 &&
+                   strtoul(mdstat->metadata_version+11, &pos, 10)
+                   == (unsigned)container &&
+                   pos > mdstat->metadata_version+11 &&
+                   *pos == '/' &&
+                   strcmp(pos+1, subdev) == 0
+                       ) {
+                       free_mdstat(mdstat->next);
+                       mdstat->next = NULL;
+                       return mdstat;
+               }
+               ent = mdstat;
+               mdstat = mdstat->next;
+               ent->next = NULL;
+               free_mdstat(ent);
+       }
+       return NULL;
+}
index 74cfa6cfc4a627f6283bc678e103ffb975bec02a..68382513a910c89766f253dbdb43795d2a6388db 100644 (file)
@@ -291,6 +291,7 @@ enum imsm_update_type {
        update_kill_array,
        update_rename_array,
        update_add_remove_disk,
+       update_reshape_container_disks,
 };
 
 struct imsm_update_activate_spare {
@@ -301,6 +302,24 @@ struct imsm_update_activate_spare {
        struct imsm_update_activate_spare *next;
 };
 
+struct geo_params {
+       int dev_id;
+       char *dev_name;
+       long long size;
+       int level;
+       int layout;
+       int chunksize;
+       int raid_disks;
+};
+
+
+struct imsm_update_reshape {
+       enum imsm_update_type type;
+       int old_raid_disks;
+       int new_raid_disks;
+       int new_disks[1]; /* new_raid_disk - old_raid_disks makedev number */
+};
+
 struct disk_info {
        __u8 serial[MAX_RAID_SERIAL_LEN];
 };
@@ -5489,6 +5508,9 @@ static void imsm_process_update(struct supertype *st,
        mpb = super->anchor;
 
        switch (type) {
+       case update_reshape_container_disks: {
+               break;
+       }
        case update_activate_spare: {
                struct imsm_update_activate_spare *u = (void *) update->buf; 
                struct imsm_dev *dev = get_imsm_dev(super, u->array);
@@ -5801,6 +5823,9 @@ static void imsm_prepare_update(struct supertype *st,
        size_t len = 0;
 
        switch (type) {
+       case update_reshape_container_disks: {
+               break;
+       }
        case update_create_array: {
                struct imsm_update_create_array *u = (void *) update->buf;
                struct intel_dev *dv;
@@ -5954,6 +5979,315 @@ static const char *imsm_get_disk_controller_domain(const char *path)
                return NULL;
 }
 
+static int imsm_find_array_minor_by_subdev(int subdev, int container, int *minor)
+{
+       char subdev_name[20];
+       struct mdstat_ent *mdstat;
+
+       sprintf(subdev_name, "%d", subdev);
+       mdstat = mdstat_by_subdev(subdev_name, container);
+       if (!mdstat)
+               return -1;
+
+       *minor = mdstat->devnum;
+       free_mdstat(mdstat);
+       return 0;
+}
+
+static int imsm_reshape_is_allowed_on_container(struct supertype *st,
+                                               struct geo_params *geo,
+                                               int *old_raid_disks)
+{
+       int ret_val = 0;
+       struct mdinfo *info, *member;
+       int devices_that_can_grow = 0;
+
+       dprintf("imsm: imsm_reshape_is_allowed_on_container(ENTER): "
+               "st->devnum = (%i)\n",
+               st->devnum);
+
+       if (geo->size != -1 ||
+           geo->level != UnSet ||
+           geo->layout != UnSet ||
+           geo->chunksize != 0 ||
+           geo->raid_disks == UnSet) {
+               dprintf("imsm: Container operation is allowed for "
+                       "raid disks number change only.\n");
+               return ret_val;
+       }
+
+       info = container_content_imsm(st, NULL);
+       for (member = info; member; member = member->next) {
+               int result;
+               int minor;
+
+               dprintf("imsm: checking device_num: %i\n",
+                       member->container_member);
+
+               if (geo->raid_disks < member->array.raid_disks) {
+                       /* we work on container for Online Capacity Expansion
+                        * only so raid_disks has to grow
+                        */
+                       dprintf("imsm: for container operation raid disks "
+                               "increase is required\n");
+                       break;
+               }
+
+               if ((info->array.level != 0) &&
+                   (info->array.level != 5)) {
+                       /* we cannot use this container with other raid level
+                        */
+                       dprintf("imsm: for container operation wrong"\
+                               " raid level (%i) detected\n",
+                               info->array.level);
+                       break;
+               } else {
+                       /* check for platform support
+                        * for this raid level configuration
+                        */
+                       struct intel_super *super = st->sb;
+                       if (!is_raid_level_supported(super->orom,
+                                                    member->array.level,
+                                                    geo->raid_disks)) {
+                               dprintf("platform does not support raid%d with"\
+                                       " %d disk%s\n",
+                                        info->array.level,
+                                        geo->raid_disks,
+                                        geo->raid_disks > 1 ? "s" : "");
+                               break;
+                       }
+               }
+
+               if (*old_raid_disks &&
+                   info->array.raid_disks != *old_raid_disks)
+                       break;
+               *old_raid_disks = info->array.raid_disks;
+
+               /* All raid5 and raid0 volumes in container
+                * have to be ready for Online Capacity Expansion
+                * so they need to be assembled.  We have already
+                * checked that no recovery etc is happening.
+                */
+               result = imsm_find_array_minor_by_subdev(member->container_member,
+                                                        st->container_dev,
+                                                        &minor);
+               if (result < 0) {
+                       dprintf("imsm: cannot find array\n");
+                       break;
+               }
+               devices_that_can_grow++;
+       }
+       sysfs_free(info);
+       if (!member && devices_that_can_grow)
+               ret_val = 1;
+
+       if (ret_val)
+               dprintf("\tContainer operation allowed\n");
+       else
+               dprintf("\tError: %i\n", ret_val);
+
+       return ret_val;
+}
+
+/* Function: get_spares_for_grow
+ * Description: Allocates memory and creates list of spare devices
+ *             avaliable in container. Checks if spare drive size is acceptable.
+ * Parameters: Pointer to the supertype structure
+ * Returns: Pointer to the list of spare devices (mdinfo structure) on success,
+ *             NULL if fail
+ */
+static struct mdinfo *get_spares_for_grow(struct supertype *st)
+{
+       dev_t dev = 0;
+       struct mdinfo *disks, *d, **dp;
+       unsigned long long min_size = min_acceptable_spare_size_imsm(st);
+
+       /* get list of alldisks in container */
+       disks = getinfo_super_disks_imsm(st);
+
+       if (!disks)
+               return NULL;
+       /* find spare devices on the list */
+       dp = &disks->devs;
+       disks->array.spare_disks = 0;
+       while (*dp) {
+               int found = 0;
+               d = *dp;
+               if (d->disk.state == 0) {
+                       /* check if size is acceptable */
+                       unsigned long long dev_size;
+                       dev = makedev(d->disk.major,d->disk.minor);
+                       if (min_size &&
+                           dev_size_from_id(dev,  &dev_size) &&
+                           dev_size >= min_size) {
+                               dev = 0;
+                               found = 1;
+                       }
+               }
+               if (found) {
+                       dp = &d->next;
+                       disks->array.spare_disks++;
+               } else {
+                       *dp = d->next;
+                       d->next = NULL;
+                       sysfs_free(d);
+               }
+       }
+       return disks;
+}
+
+/******************************************************************************
+ * function: imsm_create_metadata_update_for_reshape
+ * Function creates update for whole IMSM container.
+ *
+ ******************************************************************************/
+static int imsm_create_metadata_update_for_reshape(
+       struct supertype *st,
+       struct geo_params *geo,
+       int old_raid_disks,
+       struct imsm_update_reshape **updatep)
+{
+       struct intel_super *super = st->sb;
+       struct imsm_super *mpb = super->anchor;
+       int update_memory_size = 0;
+       struct imsm_update_reshape *u = NULL;
+       struct mdinfo *spares = NULL;
+       int i;
+       int delta_disks = 0;
+
+       dprintf("imsm_update_metadata_for_reshape(enter) raid_disks = %i\n",
+               geo->raid_disks);
+
+       delta_disks = geo->raid_disks - old_raid_disks;
+
+       /* size of all update data without anchor */
+       update_memory_size = sizeof(struct imsm_update_reshape);
+
+       /* now add space for spare disks that we need to add. */
+       update_memory_size += sizeof(u->new_disks[0]) * (delta_disks - 1);
+
+       u = calloc(1, update_memory_size);
+       if (u == NULL) {
+               dprintf("error: "
+                       "cannot get memory for imsm_update_reshape update\n");
+               return 0;
+       }
+       u->type = update_reshape_container_disks;
+       u->old_raid_disks = old_raid_disks;
+       u->new_raid_disks = geo->raid_disks;
+
+       /* now get spare disks list
+        */
+       spares = get_spares_for_grow(st);
+
+       if (spares == NULL
+           || delta_disks > spares->array.spare_disks) {
+               dprintf("imsm: ERROR: Cannot get spare devices.\n");
+               goto abort;
+       }
+
+       /* we have got spares
+        * update disk list in imsm_disk list table in anchor
+        */
+       dprintf("imsm: %i spares are available.\n\n",
+               spares->array.spare_disks);
+
+       for (i = 0; i < delta_disks; i++) {
+               struct mdinfo *dev = spares->devs;
+               struct dl *dl;
+
+               u->new_disks[i] = makedev(dev->disk.major,
+                                         dev->disk.minor);
+               dl = get_disk_super(super, dev->disk.major, dev->disk.minor);
+               dl->index = mpb->num_disks++;
+       }
+       /* Now update the metadata so that container_content will find
+        * the new devices
+        */
+       for (i = 0; i < mpb->num_raid_devs; i++) {
+               int d;
+               struct imsm_dev *dev = get_imsm_dev(super, i);
+               struct imsm_map *map = get_imsm_map(dev, 0);
+               map->num_members = geo->raid_disks;
+               for (d = 0; d < delta_disks; d++) {
+                       set_imsm_ord_tbl_ent(map, old_raid_disks + d,
+                                            mpb->num_disks - delta_disks + d);
+               }
+       }
+
+abort:
+       /* free spares
+        */
+       sysfs_free(spares);
+
+       if (i == delta_disks) {
+               *updatep = u;
+               return update_memory_size;
+       }
+       free(u);
+
+       return 0;
+}
+
+
+static int imsm_reshape_super(struct supertype *st, long long size, int level,
+                             int layout, int chunksize, int raid_disks,
+                             char *backup, char *dev, int verbouse)
+{
+       /* currently we only support increasing the number of devices
+        * for a container.  This increases the number of device for each
+        * member array.  They must all be RAID0 or RAID5.
+        */
+
+       int ret_val = 1;
+       struct geo_params geo;
+
+       dprintf("imsm: reshape_super called.\n");
+
+       memset(&geo, sizeof(struct geo_params), 0);
+
+       geo.dev_name = dev;
+       geo.size = size;
+       geo.level = level;
+       geo.layout = layout;
+       geo.chunksize = chunksize;
+       geo.raid_disks = raid_disks;
+
+       dprintf("\tfor level      : %i\n", geo.level);
+       dprintf("\tfor raid_disks : %i\n", geo.raid_disks);
+
+       if (experimental() == 0)
+               return ret_val;
+
+       /* verify reshape conditions
+        * on container level we can only increase number of devices. */
+       if (st->container_dev == st->devnum) {
+               /* check for delta_disks > 0
+                *and supported raid levels 0 and 5 only in container */
+               int old_raid_disks = 0;
+               if (imsm_reshape_is_allowed_on_container(
+                           st, &geo, &old_raid_disks)) {
+                       struct imsm_update_reshape *u = NULL;
+                       int len;
+
+                       len = imsm_create_metadata_update_for_reshape(
+                               st, &geo, old_raid_disks, &u);
+
+                       if (len) {
+                               ret_val = 0;
+                               append_metadata_update(st, u, len);
+                       } else
+                               dprintf("imsm: Cannot prepare "\
+                                       "update\n");
+               } else
+                       dprintf("imsm: Operation is not allowed "\
+                               "on this container\n");
+       } else
+               dprintf("imsm: not a container operation\n");
+
+       dprintf("imsm: reshape_super Exit code = %i\n", ret_val);
+       return ret_val;
+}
 
 struct superswitch super_imsm = {
 #ifndef        MDASSEMBLE
@@ -5991,6 +6325,7 @@ struct superswitch super_imsm = {
        .container_content = container_content_imsm,
        .default_geometry = default_geometry_imsm,
        .get_disk_controller_domain = imsm_get_disk_controller_domain,
+       .reshape_super  = imsm_reshape_super,
 
        .external       = 1,
        .name = "imsm",