]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
Add raid10 -> raid0 takeover support
[thirdparty/mdadm.git] / super-intel.c
index 34e03c385768141b294a7ae9e6d77d07f49f1106..0178f289f51f942a980fa125ec688fba8f9c8f4e 100644 (file)
@@ -284,6 +284,13 @@ struct extent {
        unsigned long long start, size;
 };
 
+/* definitions of reshape process types */
+enum imsm_reshape_type {
+       CH_TAKEOVER,
+       CH_CHUNK_MIGR,
+       CH_LEVEL_MIGRATION
+};
+
 /* definition of messages passed to imsm_process_update */
 enum imsm_update_type {
        update_activate_spare,
@@ -292,6 +299,7 @@ enum imsm_update_type {
        update_rename_array,
        update_add_remove_disk,
        update_reshape_container_disks,
+       update_takeover
 };
 
 struct imsm_update_activate_spare {
@@ -312,6 +320,15 @@ struct geo_params {
        int raid_disks;
 };
 
+enum takeover_direction {
+       R10_TO_R0,
+       R0_TO_R10
+};
+struct imsm_update_takeover {
+       enum imsm_update_type type;
+       int subarray;
+       enum takeover_direction direction;
+};
 
 struct imsm_update_reshape {
        enum imsm_update_type type;
@@ -3736,7 +3753,6 @@ static int write_init_super_imsm(struct supertype *st)
        if (st->update_tail) {
                /* queue the recently created array / added disk
                 * as a metadata update */
-               struct dl *d;
                int rv;
 
                /* determine if we are creating a volume or adding a disk */
@@ -3748,11 +3764,6 @@ static int write_init_super_imsm(struct supertype *st)
                } else
                        rv = create_array(st, current_vol);
 
-               for (d = super->disks; d ; d = d->next) {
-                       close(d->fd);
-                       d->fd = -1;
-               }
-
                return rv;
        } else {
                struct dl *d;
@@ -5757,6 +5768,58 @@ update_reshape_exit:
        return ret_val;
 }
 
+static int apply_takeover_update(struct imsm_update_takeover *u,
+                                struct intel_super *super)
+{
+       struct imsm_dev *dev = NULL;
+       struct imsm_map *map;
+       struct dl *dm, *du;
+       struct intel_dev *dv;
+
+       for (dv = super->devlist; dv; dv = dv->next)
+               if (dv->index == (unsigned int)u->subarray) {
+                       dev = dv->dev;
+                       break;
+               }
+
+       if (dev == NULL)
+               return 0;
+
+       map = get_imsm_map(dev, 0);
+
+       if (u->direction == R10_TO_R0) {
+               /* iterate through devices to mark removed disks as spare */
+               for (dm = super->disks; dm; dm = dm->next) {
+                       if (dm->disk.status & FAILED_DISK) {
+                               int idx = dm->index;
+                               /* update indexes on the disk list */
+/* FIXME this loop-with-the-loop looks wrong,  I'm not convinced
+   the index values will end up being correct.... NB */
+                               for (du = super->disks; du; du = du->next)
+                                       if (du->index > idx)
+                                               du->index--;
+                               /* mark as spare disk */
+                               dm->disk.status = SPARE_DISK;
+                               dm->index = -1;
+                       }
+               }
+
+               /* update map */
+               map->num_members = map->num_members / 2;
+               map->map_state = IMSM_T_STATE_NORMAL;
+               map->num_domains = 1;
+               map->raid_level = 0;
+               map->failed_disk_num = -1;
+       }
+
+       /* update disk order table */
+       for (du = super->disks; du; du = du->next)
+               if (du->index >= 0)
+                       set_imsm_ord_tbl_ent(map, du->index, du->index);
+
+       return 1;
+}
+
 static void imsm_process_update(struct supertype *st,
                                struct metadata_update *update)
 {
@@ -5799,6 +5862,13 @@ static void imsm_process_update(struct supertype *st,
        mpb = super->anchor;
 
        switch (type) {
+       case update_takeover: {
+               struct imsm_update_takeover *u = (void *)update->buf;
+               if (apply_takeover_update(u, super))
+                       super->updates_pending++;
+               break;
+       }
+
        case update_reshape_container_disks: {
                struct imsm_update_reshape *u = (void *)update->buf;
                if (apply_reshape_container_disks_update(
@@ -6324,6 +6394,10 @@ static int imsm_reshape_is_allowed_on_container(struct supertype *st,
                                                struct geo_params *geo,
                                                int *old_raid_disks)
 {
+       /* 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 = 0;
        struct mdinfo *info, *member;
        int devices_that_can_grow = 0;
@@ -6536,15 +6610,148 @@ static void imsm_update_metadata_locally(struct supertype *st,
        }
 }
 
+/***************************************************************************
+* Function:    imsm_analyze_change
+* Description: Function analyze change for single volume
+*              and validate if transition is supported
+* Parameters:  Geometry parameters, supertype structure
+* Returns:     Operation type code on success, -1 if fail
+****************************************************************************/
+enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
+                                          struct geo_params *geo)
+{
+       struct mdinfo info;
+       int change = -1;
+       int check_devs = 0;
+
+       getinfo_super_imsm_volume(st, &info, NULL);
+
+       if ((geo->level != info.array.level) &&
+           (geo->level >= 0) &&
+           (geo->level != UnSet)) {
+               switch (info.array.level) {
+               case 0:
+                       if (geo->level == 5) {
+                               change = CH_LEVEL_MIGRATION;
+                               check_devs = 1;
+                       }
+                       if (geo->level == 10) {
+                               change = CH_TAKEOVER;
+                               check_devs = 1;
+                       }
+                       break;
+               case 5:
+                       if (geo->level != 0)
+                               change = CH_LEVEL_MIGRATION;
+                       break;
+               case 10:
+                       if (geo->level == 0) {
+                               change = CH_TAKEOVER;
+                               check_devs = 1;
+                       }
+                       break;
+               }
+               if (change == -1) {
+                       fprintf(stderr,
+                               Name " Error. Level Migration from %d to %d "
+                               "not supported!\n",
+                               info.array.level, geo->level);
+                       goto analyse_change_exit;
+               }
+       } else
+               geo->level = info.array.level;
+
+       if ((geo->layout != info.array.layout)
+           && ((geo->layout != UnSet) && (geo->layout != -1))) {
+               change = CH_LEVEL_MIGRATION;
+               if ((info.array.layout == 0)
+                   && (info.array.level == 5)
+                   && (geo->layout == 5)) {
+                       /* reshape 5 -> 4 */
+               } else if ((info.array.layout == 5)
+                          && (info.array.level == 5)
+                          && (geo->layout == 0)) {
+                       /* reshape 4 -> 5 */
+                       geo->layout = 0;
+                       geo->level = 5;
+               } else {
+                       fprintf(stderr,
+                               Name " Error. Layout Migration from %d to %d "
+                               "not supported!\n",
+                               info.array.layout, geo->layout);
+                       change = -1;
+                       goto analyse_change_exit;
+               }
+       } else
+               geo->layout = info.array.layout;
+
+       if ((geo->chunksize > 0) && (geo->chunksize != UnSet)
+           && (geo->chunksize != info.array.chunk_size))
+               change = CH_CHUNK_MIGR;
+       else
+               geo->chunksize = info.array.chunk_size;
+
+       if (!validate_geometry_imsm(st,
+                                   geo->level,
+                                   geo->layout,
+                                   geo->raid_disks,
+                                   (geo->chunksize / 1024),
+                                   geo->size,
+                                   0, 0, 1))
+               change = -1;
+
+       if (check_devs) {
+               struct intel_super *super = st->sb;
+               struct imsm_super *mpb = super->anchor;
+
+               if (mpb->num_raid_devs > 1) {
+                       fprintf(stderr,
+                               Name " Error. Cannot perform operation on %s"
+                               "- for this operation it MUST be single "
+                               "array in container\n",
+                               geo->dev_name);
+                       change = -1;
+               }
+       }
+
+analyse_change_exit:
+
+       return change;
+}
+
+int imsm_takeover(struct supertype *st, struct geo_params *geo)
+{
+       struct intel_super *super = st->sb;
+       struct imsm_update_takeover *u;
+
+       u = malloc(sizeof(struct imsm_update_takeover));
+       if (u == NULL)
+               return 1;
+
+       u->type = update_takeover;
+       u->subarray = super->current_vol;
+
+       /* 10->0 transition */
+       if (geo->level == 0)
+               u->direction = R10_TO_R0;
+
+       /* update metadata locally */
+       imsm_update_metadata_locally(st, u,
+                                       sizeof(struct imsm_update_takeover));
+       /* and possibly remotely */
+       if (st->update_tail)
+               append_metadata_update(st, u,
+                                       sizeof(struct imsm_update_takeover));
+       else
+               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 verbose)
 {
-       /* 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;
 
@@ -6553,6 +6760,7 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
        memset(&geo, sizeof(struct geo_params), 0);
 
        geo.dev_name = dev;
+       geo.dev_id = st->devnum;
        geo.size = size;
        geo.level = level;
        geo.layout = layout;
@@ -6565,13 +6773,9 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
        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
-                */
+               /* On container level we can only increase number of devices. */
+               dprintf("imsm: info: Container operation\n");
                int old_raid_disks = 0;
                if (imsm_reshape_is_allowed_on_container(
                            st, &geo, &old_raid_disks)) {
@@ -6595,11 +6799,48 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
                        else
                                free(u);
 
-               } else
+               } else {
                        fprintf(stderr, Name "imsm: Operation is not allowed "
                                "on this container\n");
-       } else
-               fprintf(stderr, Name "imsm: not a container operation\n");
+               }
+       } else {
+               /* On volume level we support following operations
+                * - takeover: raid10 -> raid0; raid0 -> raid10
+                * - chunk size migration
+                * - migration: raid5 -> raid0; raid0 -> raid5
+                */
+               struct intel_super *super = st->sb;
+               struct intel_dev *dev = super->devlist;
+               int change, devnum;
+               dprintf("imsm: info: Volume operation\n");
+               /* find requested device */
+               while (dev) {
+                       imsm_find_array_minor_by_subdev(dev->index, st->container_dev, &devnum);
+                       if (devnum == geo.dev_id)
+                               break;
+                       dev = dev->next;
+               }
+               if (dev == NULL) {
+                       fprintf(stderr, Name " Cannot find %s (%i) subarray\n",
+                               geo.dev_name, geo.dev_id);
+                       goto exit_imsm_reshape_super;
+               }
+               super->current_vol = dev->index;
+               change = imsm_analyze_change(st, &geo);
+               switch (change) {
+               case CH_TAKEOVER:
+                       ret_val = imsm_takeover(st, &geo);
+                       break;
+               case CH_CHUNK_MIGR:
+                       ret_val = 0;
+                       break;
+               case CH_LEVEL_MIGRATION:
+                       ret_val = 0;
+                       break;
+               default:
+                       ret_val = 1;
+               }
+       }
 
 exit_imsm_reshape_super:
        dprintf("imsm: reshape_super Exit code = %i\n", ret_val);