]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: Disable checkpoint updating by mdmon for general migration
[thirdparty/mdadm.git] / super-intel.c
index 4b30024441da1f8c18e2dd085c3e0c553886b7da..7654a11721f14d71f7b98707daefdf466a2dd4fd 100644 (file)
@@ -915,9 +915,13 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
 }
 
 #ifndef MDASSEMBLE
-static __u64 blocks_per_migr_unit(struct imsm_dev *dev);
+static __u64 blocks_per_migr_unit(struct intel_super *super,
+                                 struct imsm_dev *dev);
 
-static void print_imsm_dev(struct imsm_dev *dev, char *uuid, int disk_idx)
+static void print_imsm_dev(struct intel_super *super,
+                          struct imsm_dev *dev,
+                          char *uuid,
+                          int disk_idx)
 {
        __u64 sz;
        int slot, i;
@@ -1008,7 +1012,7 @@ static void print_imsm_dev(struct imsm_dev *dev, char *uuid, int disk_idx)
                printf(" <-- %s", map_state_str[map->map_state]);
                printf("\n     Checkpoint : %u (%llu)",
                       __le32_to_cpu(dev->vol.curr_migr_unit),
-                      (unsigned long long)blocks_per_migr_unit(dev));
+                      (unsigned long long)blocks_per_migr_unit(super, dev));
        }
        printf("\n");
        printf("    Dirty State : %s\n", dev->vol.dirty ? "dirty" : "clean");
@@ -1035,6 +1039,57 @@ static void print_imsm_disk(struct imsm_super *mpb, int index, __u32 reserved)
               human_size(sz * 512));
 }
 
+static int is_gen_migration(struct imsm_dev *dev);
+
+void examine_migr_rec_imsm(struct intel_super *super)
+{
+       struct migr_record *migr_rec = super->migr_rec;
+       struct imsm_super *mpb = super->anchor;
+       int i;
+
+       for (i = 0; i < mpb->num_raid_devs; i++) {
+               struct imsm_dev *dev = __get_imsm_dev(mpb, i);
+               if (is_gen_migration(dev) == 0)
+                               continue;
+
+               printf("\nMigration Record Information:");
+               if (super->disks->index > 1) {
+                       printf(" Empty\n                              ");
+                       printf("Examine one of first two disks in array\n");
+                       break;
+               }
+               printf("\n                     Status : ");
+               if (__le32_to_cpu(migr_rec->rec_status) == UNIT_SRC_NORMAL)
+                       printf("Normal\n");
+               else
+                       printf("Contains Data\n");
+               printf("               Current Unit : %u\n",
+                      __le32_to_cpu(migr_rec->curr_migr_unit));
+               printf("                     Family : %u\n",
+                      __le32_to_cpu(migr_rec->family_num));
+               printf("                  Ascending : %u\n",
+                      __le32_to_cpu(migr_rec->ascending_migr));
+               printf("            Blocks Per Unit : %u\n",
+                      __le32_to_cpu(migr_rec->blocks_per_unit));
+               printf("       Dest. Depth Per Unit : %u\n",
+                      __le32_to_cpu(migr_rec->dest_depth_per_unit));
+               printf("        Checkpoint Area pba : %u\n",
+                      __le32_to_cpu(migr_rec->ckpt_area_pba));
+               printf("           First member lba : %u\n",
+                      __le32_to_cpu(migr_rec->dest_1st_member_lba));
+               printf("      Total Number of Units : %u\n",
+                      __le32_to_cpu(migr_rec->num_migr_units));
+               printf("             Size of volume : %u\n",
+                      __le32_to_cpu(migr_rec->post_migr_vol_cap));
+               printf("  Expansion space for LBA64 : %u\n",
+                      __le32_to_cpu(migr_rec->post_migr_vol_cap_hi));
+               printf("       Record was read from : %u\n",
+                      __le32_to_cpu(migr_rec->ckpt_read_disk_num));
+
+               break;
+       }
+}
+
 static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *map);
 
 static void examine_super_imsm(struct supertype *st, char *homehost)
@@ -1085,7 +1140,7 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
                super->current_vol = i;
                getinfo_super_imsm(st, &info, NULL);
                fname_from_uuid(st, &info, nbuf, ':');
-               print_imsm_dev(dev, nbuf + 5, super->disks->index);
+               print_imsm_dev(super, dev, nbuf + 5, super->disks->index);
        }
        for (i = 0; i < mpb->num_disks; i++) {
                if (i == super->disks->index)
@@ -1112,6 +1167,8 @@ static void examine_super_imsm(struct supertype *st, char *homehost)
                printf("    Usable Size : %llu%s\n", (unsigned long long)sz,
                       human_size(sz * 512));
        }
+
+       examine_migr_rec_imsm(super);
 }
 
 static void brief_examine_super_imsm(struct supertype *st, int verbose)
@@ -1723,7 +1780,8 @@ static __u32 map_migr_block(struct imsm_dev *dev, __u32 block)
        }
 }
 
-static __u64 blocks_per_migr_unit(struct imsm_dev *dev)
+static __u64 blocks_per_migr_unit(struct intel_super *super,
+                                 struct imsm_dev *dev)
 {
        /* calculate the conversion factor between per member 'blocks'
         * (md/{resync,rebuild}_start) and imsm migration units, return
@@ -1733,7 +1791,10 @@ static __u64 blocks_per_migr_unit(struct imsm_dev *dev)
                return 0;
 
        switch (migr_type(dev)) {
-       case MIGR_GEN_MIGR:
+       case MIGR_GEN_MIGR: {
+               struct migr_record *migr_rec = super->migr_rec;
+               return __le32_to_cpu(migr_rec->blocks_per_unit);
+       }
        case MIGR_VERIFY:
        case MIGR_REPAIR:
        case MIGR_INIT: {
@@ -1931,6 +1992,7 @@ static int write_imsm_migr_rec(struct supertype *st)
 static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, char *dmap)
 {
        struct intel_super *super = st->sb;
+       struct migr_record *migr_rec = super->migr_rec;
        struct imsm_dev *dev = get_imsm_dev(super, super->current_vol);
        struct imsm_map *map = get_imsm_map(dev, 0);
        struct imsm_map *prev_map = get_imsm_map(dev, 1);
@@ -2046,15 +2108,17 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
                switch (migr_type(dev)) {
                case MIGR_REPAIR:
                case MIGR_INIT: {
-                       __u64 blocks_per_unit = blocks_per_migr_unit(dev);
+                       __u64 blocks_per_unit = blocks_per_migr_unit(super,
+                                                                    dev);
                        __u64 units = __le32_to_cpu(dev->vol.curr_migr_unit);
 
                        info->resync_start = blocks_per_unit * units;
                        break;
                }
                case MIGR_GEN_MIGR: {
-                       __u64 blocks_per_unit = blocks_per_migr_unit(dev);
-                       __u64 units = __le32_to_cpu(dev->vol.curr_migr_unit);
+                       __u64 blocks_per_unit = blocks_per_migr_unit(super,
+                                                                    dev);
+                       __u64 units = __le32_to_cpu(migr_rec->curr_migr_unit);
                        unsigned long long array_blocks;
                        int used_disks;
 
@@ -4248,6 +4312,8 @@ static int write_super_imsm_spares(struct intel_super *super, int doclose)
        return 0;
 }
 
+static int is_gen_migration(struct imsm_dev *dev);
+
 static int write_super_imsm(struct supertype *st, int doclose)
 {
        struct intel_super *super = st->sb;
@@ -4259,6 +4325,7 @@ static int write_super_imsm(struct supertype *st, int doclose)
        int i;
        __u32 mpb_size = sizeof(struct imsm_super) - sizeof(struct imsm_disk);
        int num_disks = 0;
+       int clear_migration_record = 1;
 
        /* 'generation' is incremented everytime the metadata is written */
        generation = __le32_to_cpu(mpb->generation_num);
@@ -4293,6 +4360,8 @@ static int write_super_imsm(struct supertype *st, int doclose)
                        imsm_copy_dev(dev, dev2);
                        mpb_size += sizeof_imsm_dev(dev, 0);
                }
+               if (is_gen_migration(dev2))
+                       clear_migration_record = 0;
        }
        mpb_size += __le32_to_cpu(mpb->bbm_log_size);
        mpb->mpb_size = __cpu_to_le32(mpb_size);
@@ -4301,6 +4370,9 @@ static int write_super_imsm(struct supertype *st, int doclose)
        sum = __gen_imsm_checksum(mpb);
        mpb->check_sum = __cpu_to_le32(sum);
 
+       if (clear_migration_record)
+               memset(super->migr_rec_buf, 0, 512);
+
        /* write the mpb for disks that compose raid devices */
        for (d = super->disks; d ; d = d->next) {
                if (d->index < 0)
@@ -4308,6 +4380,14 @@ static int write_super_imsm(struct supertype *st, int doclose)
                if (store_imsm_mpb(d->fd, mpb))
                        fprintf(stderr, "%s: failed for device %d:%d %s\n",
                                __func__, d->major, d->minor, strerror(errno));
+               if (clear_migration_record) {
+                       unsigned long long dsize;
+
+                       get_dev_size(d->fd, NULL, &dsize);
+                       if (lseek64(d->fd, dsize - 512, SEEK_SET) >= 0) {
+                               write(d->fd, super->migr_rec_buf, 512);
+                       }
+               }
                if (doclose) {
                        close(d->fd);
                        d->fd = -1;
@@ -5181,7 +5261,9 @@ static int is_rebuilding(struct imsm_dev *dev)
                return 0;
 }
 
-static void update_recovery_start(struct imsm_dev *dev, struct mdinfo *array)
+static void update_recovery_start(struct intel_super *super,
+                                       struct imsm_dev *dev,
+                                       struct mdinfo *array)
 {
        struct mdinfo *rebuild = NULL;
        struct mdinfo *d;
@@ -5208,9 +5290,10 @@ static void update_recovery_start(struct imsm_dev *dev, struct mdinfo *array)
        }
 
        units = __le32_to_cpu(dev->vol.curr_migr_unit);
-       rebuild->recovery_start = units * blocks_per_migr_unit(dev);
+       rebuild->recovery_start = units * blocks_per_migr_unit(super, dev);
 }
 
+static int recover_backup_imsm(struct supertype *st, struct mdinfo *info);
 
 static struct mdinfo *container_content_imsm(struct supertype *st, char *subarray)
 {
@@ -5372,8 +5455,13 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra
                        info_d->component_size = __le32_to_cpu(map->blocks_per_member);
                }
                /* now that the disk list is up-to-date fixup recovery_start */
-               update_recovery_start(dev, this);
+               update_recovery_start(super, dev, this);
                this->array.spare_disks += spare_disks;
+
+               /* check for reshape */
+               if (this->reshape_active == 1)
+                       recover_backup_imsm(st, this);
+
                rest = this;
        }
 
@@ -5763,8 +5851,14 @@ 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(dev);
+       blocks_per_unit = blocks_per_migr_unit(super, dev);
        if (blocks_per_unit) {
                __u32 units32;
                __u64 units;
@@ -5784,6 +5878,7 @@ mark_checkpoint:
                }
        }
 
+skip_mark_checkpoint:
        /* mark dirty / clean */
        if (dev->vol.dirty != !consistent) {
                dprintf("imsm: mark '%s'\n", consistent ? "clean" : "dirty");
@@ -7596,6 +7691,126 @@ int save_checkpoint_imsm(struct supertype *st, struct mdinfo *info, int state)
        return 0;
 }
 
+static __u64 blocks_per_migr_unit(struct intel_super *super,
+                                 struct imsm_dev *dev);
+
+/*******************************************************************************
+ * Function:   recover_backup_imsm
+ * Description:        Function recovers critical data from the Migration Copy Area
+ *             while assembling an array.
+ * Parameters:
+ *     super   : imsm internal array info
+ *     info    : general array info
+ * Returns:
+ *     0 : success (or there is no data to recover)
+ *     1 : fail
+ ******************************************************************************/
+int recover_backup_imsm(struct supertype *st, struct mdinfo *info)
+{
+       struct intel_super *super = st->sb;
+       struct migr_record *migr_rec = super->migr_rec;
+       struct imsm_map *map_dest = NULL;
+       struct intel_dev *id = NULL;
+       unsigned long long read_offset;
+       unsigned long long write_offset;
+       unsigned unit_len;
+       int *targets = NULL;
+       int new_disks, i, err;
+       char *buf = NULL;
+       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];
+
+       err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, 20);
+       if (err < 1)
+               return 1;
+
+       /* recover data only during assemblation */
+       if (strncmp(buffer, "inactive", 8) != 0)
+               return 0;
+       /* no data to recover */
+       if (__le32_to_cpu(migr_rec->rec_status) == UNIT_SRC_NORMAL)
+               return 0;
+       if (curr_migr_unit >= num_migr_units)
+               return 1;
+
+       /* find device during reshape */
+       for (id = super->devlist; id; id = id->next)
+               if (is_gen_migration(id->dev))
+                       break;
+       if (id == NULL)
+               return 1;
+
+       map_dest = get_imsm_map(id->dev, 0);
+       new_disks = map_dest->num_members;
+
+       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;
+
+       unit_len = __le32_to_cpu(migr_rec->dest_depth_per_unit) * 512;
+       if (posix_memalign((void **)&buf, 512, unit_len) != 0)
+               goto abort;
+       targets = malloc(new_disks * sizeof(int));
+       if (!targets)
+               goto abort;
+
+       open_backup_targets(info, new_disks, targets);
+
+       for (i = 0; i < new_disks; i++) {
+               if (lseek64(targets[i], read_offset, SEEK_SET) < 0) {
+                       fprintf(stderr,
+                               Name ": Cannot seek to block: %s\n",
+                               strerror(errno));
+                       goto abort;
+               }
+               if (read(targets[i], buf, unit_len) != unit_len) {
+                       fprintf(stderr,
+                               Name ": Cannot read copy area block: %s\n",
+                               strerror(errno));
+                       goto abort;
+               }
+               if (lseek64(targets[i], write_offset, SEEK_SET) < 0) {
+                       fprintf(stderr,
+                               Name ": Cannot seek to block: %s\n",
+                               strerror(errno));
+                       goto abort;
+               }
+               if (write(targets[i], buf, unit_len) != unit_len) {
+                       fprintf(stderr,
+                               Name ": Cannot restore block: %s\n",
+                               strerror(errno));
+                       goto abort;
+               }
+       }
+
+       if (ascending && curr_migr_unit < (num_migr_units-1))
+               curr_migr_unit++;
+
+       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;
+               retval = 0;
+       }
+
+abort:
+       if (targets) {
+               for (i = 0; i < new_disks; i++)
+                       if (targets[i])
+                               close(targets[i]);
+               free(targets);
+       }
+       free(buf);
+       return retval;
+}
+
 static char disk_by_path[] = "/dev/disk/by-path/";
 
 static const char *imsm_get_disk_controller_domain(const char *path)
@@ -8549,9 +8764,10 @@ static int imsm_manage_reshape(
                sysfs_set_num(sra, NULL, "suspend_hi", next_step);
 
                /* wait until reshape finish */
-               if (wait_for_reshape_imsm(sra, next_step, ndata) < 0)
-                       dprintf("wait_for_reshape_imsm returned error,"
-                               " but we ignore it!\n");
+               if (wait_for_reshape_imsm(sra, next_step, ndata) < 0) {
+                       dprintf("wait_for_reshape_imsm returned error!\n");
+                       goto abort;
+               }
 
                sra->reshape_progress = next_step;
 
@@ -8612,6 +8828,8 @@ struct superswitch super_imsm = {
        .match_metadata_desc = match_metadata_desc_imsm,
        .container_content = container_content_imsm,
 
+       .recover_backup = recover_backup_imsm,
+
        .external       = 1,
        .name = "imsm",