]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: FIX: Move reshape_progress forward
[thirdparty/mdadm.git] / super-intel.c
index 2d79fd821100f0faa52780e57cf29deb46a5fe92..b5adb88b834170819b7be1a348564c43dcae3949 100644 (file)
@@ -345,7 +345,8 @@ enum imsm_update_type {
        update_add_remove_disk,
        update_reshape_container_disks,
        update_reshape_migration,
-       update_takeover
+       update_takeover,
+       update_general_migration_checkpoint,
 };
 
 struct imsm_update_activate_spare {
@@ -398,6 +399,11 @@ struct imsm_update_reshape_migration {
        int new_disks[1]; /* new_raid_disks - old_raid_disks makedev number */
 };
 
+struct imsm_update_general_migration_checkpoint {
+       enum imsm_update_type type;
+       __u32 curr_migr_unit;
+};
+
 struct disk_info {
        __u8 serial[MAX_RAID_SERIAL_LEN];
 };
@@ -1816,7 +1822,7 @@ static __u64 blocks_per_migr_unit(struct intel_super *super,
                migr_chunk = migr_strip_blocks_resync(dev);
                disks = imsm_num_data_members(dev, 0);
                blocks_per_unit = stripes_per_unit * migr_chunk * disks;
-               stripe = __le32_to_cpu(map->blocks_per_strip) * disks;
+               stripe = __le16_to_cpu(map->blocks_per_strip) * disks;
                segment = blocks_per_unit / stripe;
                block_rel = blocks_per_unit - segment * stripe;
                parity_depth = parity_segment_depth(dev);
@@ -1938,6 +1944,51 @@ out:
        return retval;
 }
 
+/*******************************************************************************
+ * function: imsm_create_metadata_checkpoint_update
+ * Description: It creates update for checkpoint change.
+ * Parameters:
+ *     super   : imsm internal array info
+ *     u       : pointer to prepared update
+ * Returns:
+ *     Uptate length.
+ *     If length is equal to 0, input pointer u contains no update
+ ******************************************************************************/
+static int imsm_create_metadata_checkpoint_update(
+       struct intel_super *super,
+       struct imsm_update_general_migration_checkpoint **u)
+{
+
+       int update_memory_size = 0;
+
+       dprintf("imsm_create_metadata_checkpoint_update(enter)\n");
+
+       if (u == NULL)
+               return 0;
+       *u = NULL;
+
+       /* size of all update data without anchor */
+       update_memory_size =
+               sizeof(struct imsm_update_general_migration_checkpoint);
+
+       *u = calloc(1, update_memory_size);
+       if (*u == NULL) {
+               dprintf("error: cannot get memory for "
+                       "imsm_create_metadata_checkpoint_update update\n");
+               return 0;
+       }
+       (*u)->type = update_general_migration_checkpoint;
+       (*u)->curr_migr_unit = __le32_to_cpu(super->migr_rec->curr_migr_unit);
+       dprintf("imsm_create_metadata_checkpoint_update: prepared for %u\n",
+               (*u)->curr_migr_unit);
+
+       return update_memory_size;
+}
+
+
+static void imsm_update_metadata_locally(struct supertype *st,
+                                        void *buf, int len);
+
 /*******************************************************************************
  * Function:   write_imsm_migr_rec
  * Description:        Function writes imsm migration record
@@ -1956,6 +2007,8 @@ static int write_imsm_migr_rec(struct supertype *st)
        int fd = -1;
        int retval = -1;
        struct dl *sd;
+       int len;
+       struct imsm_update_general_migration_checkpoint *u;
 
        for (sd = super->disks ; sd ; sd = sd->next) {
                /* write to 2 first slots only */
@@ -1981,6 +2034,26 @@ static int write_imsm_migr_rec(struct supertype *st)
                close(fd);
                fd = -1;
        }
+       /* update checkpoint information in metadata */
+       len = imsm_create_metadata_checkpoint_update(super, &u);
+
+       if (len <= 0) {
+               dprintf("imsm: Cannot prepare update\n");
+               goto out;
+       }
+       /* update metadata locally */
+       imsm_update_metadata_locally(st, u, len);
+       /* and possibly remotely */
+       if (st->update_tail) {
+               append_metadata_update(st, u, len);
+               /* during reshape we do all work inside metadata handler
+                * manage_reshape(), so metadata update has to be triggered
+                * insida it
+                */
+               flush_metadata_updates(st);
+               st->update_tail = &st->updates;
+       } else
+               free(u);
 
        retval = 0;
  out:
@@ -2006,9 +2079,8 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        if (prev_map)
                map_to_analyse = prev_map;
 
-       for (dl = super->disks; dl; dl = dl->next)
-               if (dl->raiddisk == info->disk.raid_disk)
-                       break;
+       dl = super->disks;
+
        info->container_member    = super->current_vol;
        info->array.raid_disks    = map->num_members;
        info->array.level         = get_imsm_raid_level(map_to_analyse);
@@ -2122,6 +2194,13 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                        unsigned long long array_blocks;
                        int used_disks;
 
+                       if (__le32_to_cpu(migr_rec->ascending_migr) &&
+                           (units <
+                               (__le32_to_cpu(migr_rec->num_migr_units)-1)) &&
+                           (super->migr_rec->rec_status ==
+                                       __cpu_to_le32(UNIT_SRC_IN_CP_AREA)))
+                               units++;
+
                        info->reshape_progress = blocks_per_unit * units;
 
                        dprintf("IMSM: General Migration checkpoint : %llu "
@@ -2966,8 +3045,8 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
 
        if (lseek64(fd, dsize - (512 * 2), SEEK_SET) < 0) {
                if (devname)
-                       fprintf(stderr,
-                               Name ": Cannot seek to anchor block on %s: %s\n",
+                       fprintf(stderr, Name
+                               ": Cannot seek to anchor block on %s: %s\n",
                                devname, strerror(errno));
                return 1;
        }
@@ -3764,16 +3843,17 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
        }
 
        /* load migration record */
-       load_imsm_migr_rec(super, NULL);
-
-       /* Check for unsupported migration features */
-       if (check_mpb_migr_compatibility(super) != 0) {
-               fprintf(stderr, Name ": Unsupported migration detected");
-               if (devname)
-                       fprintf(stderr, " on %s\n", devname);
-               else
-                       fprintf(stderr, " (IMSM).\n");
-               return 3;
+       if (load_imsm_migr_rec(super, NULL) == 0) {
+               /* Check for unsupported migration features */
+               if (check_mpb_migr_compatibility(super) != 0) {
+                       fprintf(stderr,
+                               Name ": Unsupported migration detected");
+                       if (devname)
+                               fprintf(stderr, " on %s\n", devname);
+                       else
+                               fprintf(stderr, " (IMSM).\n");
+                       return 3;
+               }
        }
 
        return 0;
@@ -5373,11 +5453,10 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                                sizeof(*this));
                        break;
                }
-               memset(this, 0, sizeof(*this));
-               this->next = rest;
 
                super->current_vol = i;
                getinfo_super_imsm_volume(st, this, NULL);
+               this->next = rest;
                for (slot = 0 ; slot <  map->num_members; slot++) {
                        unsigned long long recovery_start;
                        struct mdinfo *info_d;
@@ -5851,6 +5930,12 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
        }
 
 mark_checkpoint:
+       /* skip checkpointing for general migration,
+        * it is controlled in mdadm
+        */
+       if (is_gen_migration(dev))
+               goto skip_mark_checkpoint;
+
        /* check if we can update curr_migr_unit from resync_start, recovery_start */
        blocks_per_unit = blocks_per_migr_unit(super, dev);
        if (blocks_per_unit) {
@@ -5872,6 +5957,7 @@ mark_checkpoint:
                }
        }
 
+skip_mark_checkpoint:
        /* mark dirty / clean */
        if (dev->vol.dirty != !consistent) {
                dprintf("imsm: mark '%s'\n", consistent ? "clean" : "dirty");
@@ -6834,6 +6920,24 @@ static void imsm_process_update(struct supertype *st,
        mpb = super->anchor;
 
        switch (type) {
+       case update_general_migration_checkpoint: {
+               struct intel_dev *id;
+               struct imsm_update_general_migration_checkpoint *u =
+                                                       (void *)update->buf;
+
+               dprintf("imsm: process_update() "
+                       "for update_general_migration_checkpoint called\n");
+
+               /* find device under general migration */
+               for (id = super->devlist ; id; id = id->next) {
+                       if (is_gen_migration(id->dev)) {
+                               id->dev->vol.curr_migr_unit =
+                                       __cpu_to_le32(u->curr_migr_unit);
+                               super->updates_pending++;
+                       }
+               }
+               break;
+       }
        case update_takeover: {
                struct imsm_update_takeover *u = (void *)update->buf;
                if (apply_takeover_update(u, super, &update->space_list)) {
@@ -7171,6 +7275,10 @@ static void imsm_prepare_update(struct supertype *st,
        size_t len = 0;
 
        switch (type) {
+       case update_general_migration_checkpoint:
+               dprintf("imsm: prepare_update() "
+                       "for update_general_migration_checkpoint called\n");
+               break;
        case update_takeover: {
                struct imsm_update_takeover *u = (void *)update->buf;
                if (u->direction == R0_TO_R10) {
@@ -7519,10 +7627,7 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev,
        struct imsm_map *map_dest = get_imsm_map(dev, 0);
        struct imsm_map *map_src = get_imsm_map(dev, 1);
        unsigned long long num_migr_units;
-
-       unsigned long long array_blocks =
-               (((unsigned long long)__le32_to_cpu(dev->size_high)) << 32) +
-               __le32_to_cpu(dev->size_low);
+       unsigned long long array_blocks;
 
        memset(migr_rec, 0, sizeof(struct migr_record));
        migr_rec->family_num = __cpu_to_le32(super->anchor->family_num);
@@ -7538,7 +7643,7 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev,
                __cpu_to_le32(migr_rec->dest_depth_per_unit * new_data_disks);
        migr_rec->dest_depth_per_unit =
                __cpu_to_le32(migr_rec->dest_depth_per_unit);
-
+       array_blocks = info->component_size * new_data_disks;
        num_migr_units =
                array_blocks / __le32_to_cpu(migr_rec->blocks_per_unit);
 
@@ -7578,9 +7683,9 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev,
  *             and to write it to the Copy Area.
  * Parameters:
  *     st              : supertype information
+ *     dev             : imsm device that backup is saved for
  *     info            : general array info
  *     buf             : input buffer
- *     write_offset    : address of data to backup
  *     length          : length of data to backup (blocks_per_unit)
  * Returns:
  *      0 : success
@@ -7590,7 +7695,6 @@ int save_backup_imsm(struct supertype *st,
                     struct imsm_dev *dev,
                     struct mdinfo *info,
                     void *buf,
-                    int new_data,
                     int length)
 {
        int rv = -1;
@@ -7600,6 +7704,8 @@ int save_backup_imsm(struct supertype *st,
        int i;
        struct imsm_map *map_dest = get_imsm_map(dev, 0);
        int new_disks = map_dest->num_members;
+       int dest_layout = 0;
+       int dest_chunk;
 
        targets = malloc(new_disks * sizeof(int));
        if (!targets)
@@ -7618,15 +7724,19 @@ int save_backup_imsm(struct supertype *st,
        if (open_backup_targets(info, new_disks, targets))
                goto abort;
 
+       if (map_dest->raid_level != 0)
+               dest_layout = ALGORITHM_LEFT_ASYMMETRIC;
+       dest_chunk = __le16_to_cpu(map_dest->blocks_per_strip) * 512;
+
        if (restore_stripes(targets, /* list of dest devices */
                            target_offsets, /* migration record offsets */
                            new_disks,
-                           info->new_chunk,
-                           info->new_level,
-                           info->new_layout,
-                           -1,         /* source backup file descriptor */
-                           0,          /* input buf offset
-                                        * always 0 buf is already offset */
+                           dest_chunk,
+                           map_dest->raid_level,
+                           dest_layout,
+                           -1,    /* source backup file descriptor */
+                           0,     /* input buf offset
+                                   * always 0 buf is already offseted */
                            0,
                            length,
                            buf) != 0) {
@@ -7658,16 +7768,23 @@ abort:
  * Returns:
  *     0: success
  *     1: failure
+ *     2: failure, means no valid migration record
+ *                / no general migration in progress /
  ******************************************************************************/
 int save_checkpoint_imsm(struct supertype *st, struct mdinfo *info, int state)
 {
        struct intel_super *super = st->sb;
-       load_imsm_migr_rec(super, info);
-       if (__le32_to_cpu(super->migr_rec->blocks_per_unit) == 0) {
-               dprintf("ERROR: blocks_per_unit = 0!!!\n");
+       if (load_imsm_migr_rec(super, info) != 0) {
+               dprintf("imsm: ERROR: Cannot read migration record "
+                       "for checkpoint save.\n");
                return 1;
        }
 
+       if (__le32_to_cpu(super->migr_rec->blocks_per_unit) == 0) {
+               dprintf("imsm: no migration in progress.\n");
+               return 2;
+       }
+
        super->migr_rec->curr_migr_unit =
          __cpu_to_le32(info->reshape_progress /
                        __le32_to_cpu(super->migr_rec->blocks_per_unit));
@@ -7713,8 +7830,9 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
        int retval = 1;
        unsigned long curr_migr_unit = __le32_to_cpu(migr_rec->curr_migr_unit);
        unsigned long num_migr_units = __le32_to_cpu(migr_rec->num_migr_units);
-       int ascending = __le32_to_cpu(migr_rec->ascending_migr);
        char buffer[20];
+       int skipped_disks = 0;
+       int max_degradation;
 
        err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, 20);
        if (err < 1)
@@ -7738,13 +7856,14 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
 
        map_dest = get_imsm_map(id->dev, 0);
        new_disks = map_dest->num_members;
+       max_degradation = new_disks - imsm_num_data_members(id->dev, 0);
 
        read_offset = (unsigned long long)
                        __le32_to_cpu(migr_rec->ckpt_area_pba) * 512;
 
        write_offset = ((unsigned long long)
                        __le32_to_cpu(migr_rec->dest_1st_member_lba) +
-                       info->data_offset) * 512;
+                       __le32_to_cpu(map_dest->pba_of_lba0)) * 512;
 
        unit_len = __le32_to_cpu(migr_rec->dest_depth_per_unit) * 512;
        if (posix_memalign((void **)&buf, 512, unit_len) != 0)
@@ -7756,6 +7875,10 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
        open_backup_targets(info, new_disks, targets);
 
        for (i = 0; i < new_disks; i++) {
+               if (targets[i] < 0) {
+                       skipped_disks++;
+                       continue;
+               }
                if (lseek64(targets[i], read_offset, SEEK_SET) < 0) {
                        fprintf(stderr,
                                Name ": Cannot seek to block: %s\n",
@@ -7782,16 +7905,20 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
                }
        }
 
-       if (ascending && curr_migr_unit < (num_migr_units-1))
-               curr_migr_unit++;
+       if (skipped_disks > max_degradation) {
+               fprintf(stderr,
+                       Name ": Cannot restore data from backup."
+                       " Too many failed disks\n");
+               goto abort;
+       }
 
-       migr_rec->curr_migr_unit = __le32_to_cpu(curr_migr_unit);
-       super->migr_rec->rec_status = __cpu_to_le32(UNIT_SRC_NORMAL);
-       if (write_imsm_migr_rec(st) == 0) {
-               __u64 blocks_per_unit = blocks_per_migr_unit(super, id->dev);
-               info->reshape_progress = curr_migr_unit * blocks_per_unit;
+       if (save_checkpoint_imsm(st, info, UNIT_SRC_NORMAL)) {
+               /* ignore error == 2, this can mean end of reshape here
+                */
+               dprintf("imsm: Cannot write checkpoint to "
+                       "migration record (UNIT_SRC_NORMAL) during restart\n");
+       } else
                retval = 0;
-       }
 
 abort:
        if (targets) {
@@ -8172,7 +8299,6 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        int chunk;
 
        getinfo_super_imsm_volume(st, &info, NULL);
-
        if ((geo->level != info.array.level) &&
            (geo->level >= 0) &&
            (geo->level != UnSet)) {
@@ -8180,6 +8306,14 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                case 0:
                        if (geo->level == 5) {
                                change = CH_MIGRATION;
+                               if (geo->layout != ALGORITHM_LEFT_ASYMMETRIC) {
+                                       fprintf(stderr,
+                                       Name " Error. Requested Layout "
+                                       "not supported (left-asymmetric layout "
+                                       "is supported only)!\n");
+                                       change = -1;
+                                       goto analyse_change_exit;
+                               }
                                check_devs = 1;
                        }
                        if (geo->level == 10) {
@@ -8302,30 +8436,6 @@ int imsm_takeover(struct supertype *st, struct geo_params *geo)
        return 0;
 }
 
-static int warn_user_about_risk(void)
-{
-       int rv = 0;
-
-       fprintf(stderr,
-               "\nThis is an experimental feature. Data on the RAID volume(s) "
-               "can be lost!!!\n\n"
-               "To continue command execution please make sure that\n"
-               "the grow process will not be interrupted. Use safe power\n"
-               "supply to avoid unexpected system reboot. Make sure that\n"
-               "reshaped container is not assembled automatically during\n"
-               "system boot.\n"
-               "If reshape is interrupted, assemble array manually\n"
-               "using e.g. '-Ac' option and up to date mdadm.conf file.\n"
-               "Assembly in scan mode is not possible in such case.\n"
-               "Growing container with boot array is not possible.\n"
-               "If boot array reshape is interrupted, whole file system\n"
-               "can be lost.\n\n");
-       rv = ask("Do you want to continue? ");
-       fprintf(stderr, "\n");
-
-       return rv;
-}
-
 static int imsm_reshape_super(struct supertype *st, long long size, int level,
                              int layout, int chunksize, int raid_disks,
                              int delta_disks, char *backup, char *dev,
@@ -8359,13 +8469,6 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
                dprintf("imsm: info: Container operation\n");
                int old_raid_disks = 0;
 
-               /* this warning will be removed when imsm checkpointing
-                * will be implemented, and restoring from check-point
-                * operation will be transparent for reboot process
-                */
-               if (warn_user_about_risk() == 0)
-                       return ret_val;
-
                if (imsm_reshape_is_allowed_on_container(
                            st, &geo, &old_raid_disks)) {
                        struct imsm_update_reshape *u = NULL;
@@ -8480,8 +8583,10 @@ int wait_for_reshape_imsm(struct mdinfo *sra, unsigned long long to_complete,
                sysfs_set_str(sra, NULL, "sync_max", "max");
                to_complete = MaxSector;
        } else {
-               if (completed > to_complete)
+               if (completed > to_complete) {
+                       close(fd);
                        return -1;
+               }
                if (sysfs_set_num(sra, NULL, "sync_max",
                                  to_complete / ndata) != 0) {
                        close(fd);
@@ -8604,8 +8709,9 @@ static int imsm_manage_reshape(
        unsigned long long start; /* [bytes] */
        unsigned long long start_buf_shift; /* [bytes] */
        int degraded = 0;
+       int source_layout = 0;
 
-       if (!fds || !offsets || !destfd || !destoffsets || !sra)
+       if (!fds || !offsets || !sra)
                goto abort;
 
        /* Find volume during the reshape */
@@ -8632,7 +8738,7 @@ static int imsm_manage_reshape(
        ndata = imsm_num_data_members(dev, 0);
        odata = imsm_num_data_members(dev, 1);
 
-       chunk = map_src->blocks_per_strip * 512;
+       chunk = __le16_to_cpu(map_src->blocks_per_strip) * 512;
        old_data_stripe_length = odata * chunk;
 
        migr_rec = super->migr_rec;
@@ -8657,10 +8763,9 @@ static int imsm_manage_reshape(
                goto abort;
        }
 
-       max_position =
-               __le32_to_cpu(migr_rec->post_migr_vol_cap) +
-               ((unsigned long long)__le32_to_cpu(
-                        migr_rec->post_migr_vol_cap_hi) << 32);
+       max_position = sra->component_size * ndata;
+       if (map_src->raid_level != 0)
+               source_layout = ALGORITHM_LEFT_ASYMMETRIC;
 
        while (__le32_to_cpu(migr_rec->curr_migr_unit) <
               __le32_to_cpu(migr_rec->num_migr_units)) {
@@ -8684,8 +8789,7 @@ static int imsm_manage_reshape(
                if ((current_position + next_step) > max_position)
                        next_step = max_position - current_position;
 
-               start = (map_src->pba_of_lba0 + dev->reserved_blocks +
-                        current_position) * 512;
+               start = current_position * 512;
 
                /* allign reading start to old geometry */
                start_buf_shift = start % old_data_stripe_length;
@@ -8718,8 +8822,8 @@ static int imsm_manage_reshape(
                                start_buf_shift, next_step_filler);
 
                        if (save_stripes(fds, offsets, map_src->num_members,
-                                        chunk, sra->array.level,
-                                        sra->array.layout, 0, NULL, start_src,
+                                        chunk, map_src->raid_level,
+                                        source_layout, 0, NULL, start_src,
                                         copy_length +
                                         next_step_filler + start_buf_shift,
                                         buf)) {
@@ -8731,8 +8835,7 @@ static int imsm_manage_reshape(
                         * in backup general migration area
                         */
                        if (save_backup_imsm(st, dev, sra,
-                                            buf + start_buf_shift,
-                                            ndata, copy_length)) {
+                               buf + start_buf_shift, copy_length)) {
                                dprintf("imsm: Cannot save stripes to "
                                        "target devices\n");
                                goto abort;
@@ -8764,7 +8867,9 @@ static int imsm_manage_reshape(
 
                sra->reshape_progress = next_step;
 
-               if (save_checkpoint_imsm(st, sra, UNIT_SRC_NORMAL)) {
+               if (save_checkpoint_imsm(st, sra, UNIT_SRC_NORMAL) == 1) {
+                       /* ignore error == 2, this can mean end of reshape here
+                        */
                        dprintf("imsm: Cannot write checkpoint to "
                                "migration record (UNIT_SRC_NORMAL)\n");
                        goto abort;