]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: Do not mark resync during reshape
[thirdparty/mdadm.git] / super-intel.c
index 193e0d04804d83f663a0a6ddbf00028bdf131870..8e9e977450115ccceae777ae126c6b8f5b520529 100644 (file)
@@ -88,6 +88,7 @@
 
 #define MPB_SECTOR_CNT 2210
 #define IMSM_RESERVED_SECTORS 4096
+#define NUM_BLOCKS_DIRTY_STRIPE_REGION 2056
 #define SECT_PER_MB_SHIFT 11
 
 /* Disk configuration info. */
@@ -827,6 +828,8 @@ static int count_memberships(struct dl *dl, struct intel_super *super)
        return memberships;
 }
 
+static __u32 imsm_min_reserved_sectors(struct intel_super *super);
+
 static struct extent *get_extents(struct intel_super *super, struct dl *dl)
 {
        /* find a list of used extents on the given physical device */
@@ -840,7 +843,7 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl)
         * IMSM_RESERVED_SECTORS region
         */
        if (dl->index == -1)
-               reservation = MPB_SECTOR_CNT;
+               reservation = imsm_min_reserved_sectors(super);
        else
                reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
 
@@ -933,6 +936,51 @@ static int is_failed(struct imsm_disk *disk)
        return (disk->status & FAILED_DISK) == FAILED_DISK;
 }
 
+/* try to determine how much space is reserved for metadata from
+ * the last get_extents() entry on the smallest active disk,
+ * otherwise fallback to the default
+ */
+static __u32 imsm_min_reserved_sectors(struct intel_super *super)
+{
+       struct extent *e;
+       int i;
+       __u32 min_active, remainder;
+       __u32 rv = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
+       struct dl *dl, *dl_min = NULL;
+
+       if (!super)
+               return rv;
+
+       min_active = 0;
+       for (dl = super->disks; dl; dl = dl->next) {
+               if (dl->index < 0)
+                       continue;
+               if (dl->disk.total_blocks < min_active || min_active == 0) {
+                       dl_min = dl;
+                       min_active = dl->disk.total_blocks;
+               }
+       }
+       if (!dl_min)
+               return rv;
+
+       /* find last lba used by subarrays on the smallest active disk */
+       e = get_extents(super, dl_min);
+       if (!e)
+               return rv;
+       for (i = 0; e[i].size; i++)
+               continue;
+
+       remainder = min_active - e[i].start;
+       free(e);
+
+       /* to give priority to recovery we should not require full
+          IMSM_RESERVED_SECTORS from the spare */
+       rv = MPB_SECTOR_CNT + NUM_BLOCKS_DIRTY_STRIPE_REGION;
+
+       /* if real reservation is smaller use that value */
+       return  (remainder < rv) ? remainder : rv;
+}
+
 /* Return minimum size of a spare that can be used in this array*/
 static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
 {
@@ -941,6 +989,7 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
        struct extent *e;
        int i;
        unsigned long long rv = 0;
+       __u32 reservation;
 
        if (!super)
                return rv;
@@ -958,9 +1007,12 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st)
                continue;
        if (i > 0)
                rv = e[i-1].start + e[i-1].size;
+       reservation = __le32_to_cpu(dl->disk.total_blocks) - e[i].start;
        free(e);
+
        /* add the amount of space needed for metadata */
-       rv = rv + MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
+       rv = rv + imsm_min_reserved_sectors(super);
+
        return rv * 512;
 }
 
@@ -2185,6 +2237,31 @@ static int write_imsm_migr_rec(struct supertype *st)
 }
 #endif /* MDASSEMBLE */
 
+/* spare/missing disks activations are not allowe when
+ * array/container performs reshape operation, because
+ * all arrays in container works on the same disks set
+ */
+int imsm_reshape_blocks_arrays_changes(struct intel_super *super)
+{
+       int rv = 0;
+       struct intel_dev *i_dev;
+       struct imsm_dev *dev;
+
+       /* check whole container
+        */
+       for (i_dev = super->devlist; i_dev; i_dev = i_dev->next) {
+               dev = i_dev->dev;
+               if (dev->vol.migr_state &&
+                   dev->vol.migr_type == MIGR_GEN_MIGR) {
+                       /* No repair during any migration in container
+                        */
+                       rv = 1;
+                       break;
+               }
+       }
+       return rv;
+}
+
 static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, char *dmap)
 {
        struct intel_super *super = st->sb;
@@ -2296,8 +2373,9 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
 
        info->reshape_progress = 0;
        info->resync_start = MaxSector;
-       if (map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
-           dev->vol.dirty) {
+       if ((map_to_analyse->map_state == IMSM_T_STATE_UNINITIALIZED ||
+           dev->vol.dirty) &&
+           imsm_reshape_blocks_arrays_changes(super) == 0) {
                info->resync_start = 0;
        }
        if (dev->vol.migr_state) {
@@ -2875,7 +2953,6 @@ static void serialcpy(__u8 *dest, __u8 *src)
        strncpy((char *) dest, (char *) src, MAX_RAID_SERIAL_LEN);
 }
 
-#ifndef MDASSEMBLE
 static struct dl *serial_to_dl(__u8 *serial, struct intel_super *super)
 {
        struct dl *dl;
@@ -2886,7 +2963,6 @@ static struct dl *serial_to_dl(__u8 *serial, struct intel_super *super)
 
        return dl;
 }
-#endif
 
 static struct imsm_disk *
 __serial_to_disk(__u8 *serial, struct imsm_super *mpb, int *idx)
@@ -3444,7 +3520,6 @@ static int find_intel_hba_capability(int fd, struct intel_super *super, char *de
        return 0;
 }
 
-#ifndef MDASSEMBLE
 /* find_missing - helper routine for load_super_imsm_all that identifies
  * disks that have disappeared from the system.  This routine relies on
  * the mpb being uptodate, which it is at load time.
@@ -3480,6 +3555,7 @@ static int find_missing(struct intel_super *super)
        return 0;
 }
 
+#ifndef MDASSEMBLE
 static struct intel_disk *disk_list_get(__u8 *serial, struct intel_disk *disk_list)
 {
        struct intel_disk *idisk = disk_list;
@@ -4133,12 +4209,40 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
                memset(mpb_new + size_old, 0, size_round - size_old);
        }
        super->current_vol = idx;
-       /* when creating the first raid device in this container set num_disks
-        * to zero, i.e. delete this spare and add raid member devices in
-        * add_to_super_imsm_volume()
+
+       /* handle 'failed_disks' by either:
+        * a) create dummy disk entries in the table if this the first
+        *    volume in the array.  We add them here as this is the only
+        *    opportunity to add them. add_to_super_imsm_volume()
+        *    handles the non-failed disks and continues incrementing
+        *    mpb->num_disks.
+        * b) validate that 'failed_disks' matches the current number
+        *    of missing disks if the container is populated
         */
-       if (super->current_vol == 0)
+       if (super->current_vol == 0) {
                mpb->num_disks = 0;
+               for (i = 0; i < info->failed_disks; i++) {
+                       struct imsm_disk *disk;
+
+                       mpb->num_disks++;
+                       disk = __get_imsm_disk(mpb, i);
+                       disk->status = CONFIGURED_DISK | FAILED_DISK;
+                       disk->scsi_id = __cpu_to_le32(~(__u32)0);
+                       snprintf((char *) disk->serial, MAX_RAID_SERIAL_LEN,
+                                "missing:%d", i);
+               }
+               find_missing(super);
+       } else {
+               int missing = 0;
+               struct dl *d;
+
+               for (d = super->missing; d; d = d->next)
+                       missing++;
+               if (info->failed_disks > missing) {
+                       fprintf(stderr, Name": unable to add 'missing' disk to container\n");
+                       return 0;
+               }
+       }
 
        if (!check_name(super, name, 0))
                return 0;
@@ -4170,15 +4274,14 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        vol = &dev->vol;
        vol->migr_state = 0;
        set_migr_type(dev, MIGR_INIT);
-       vol->dirty = 0;
+       vol->dirty = !info->state;
        vol->curr_migr_unit = 0;
        map = get_imsm_map(dev, 0);
        map->pba_of_lba0 = __cpu_to_le32(super->create_offset);
        map->blocks_per_member = __cpu_to_le32(info_to_blocks_per_member(info));
        map->blocks_per_strip = __cpu_to_le16(info_to_blocks_per_strip(info));
        map->failed_disk_num = ~0;
-       map->map_state = info->level ? IMSM_T_STATE_UNINITIALIZED :
-                                      IMSM_T_STATE_NORMAL;
+       map->map_state = info->failed_disks ? IMSM_T_STATE_DEGRADED : IMSM_T_STATE_NORMAL;
        map->ddf = 1;
 
        if (info->level == 1 && info->raid_disks > 2) {
@@ -4286,9 +4389,10 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
 {
        struct intel_super *super = st->sb;
        struct imsm_super *mpb = super->anchor;
-       struct dl *dl;
+       struct imsm_disk *_disk;
        struct imsm_dev *dev;
        struct imsm_map *map;
+       struct dl *dl, *df;
        int slot;
 
        dev = get_imsm_dev(super, super->current_vol);
@@ -4335,12 +4439,37 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
        set_imsm_ord_tbl_ent(map, dk->raid_disk, dl->index);
        dl->disk.status = CONFIGURED_DISK;
 
+       /* update size of 'missing' disks to be at least as large as the
+        * largest acitve member (we only have dummy missing disks when
+        * creating the first volume)
+        */
+       if (super->current_vol == 0) {
+               for (df = super->missing; df; df = df->next) {
+                       if (dl->disk.total_blocks > df->disk.total_blocks)
+                               df->disk.total_blocks = dl->disk.total_blocks;
+                       _disk = __get_imsm_disk(mpb, df->index);
+                       *_disk = df->disk;
+               }
+       }
+
+       /* refresh unset/failed slots to point to valid 'missing' entries */
+       for (df = super->missing; df; df = df->next)
+               for (slot = 0; slot < mpb->num_disks; slot++) {
+                       __u32 ord = get_imsm_ord_tbl_ent(dev, slot, -1);
+
+                       if ((ord & IMSM_ORD_REBUILD) == 0)
+                               continue;
+                       set_imsm_ord_tbl_ent(map, slot, df->index | IMSM_ORD_REBUILD);
+                       dprintf("set slot:%d to missing disk:%d\n", slot, df->index);
+                       break;
+               }
+
        /* if we are creating the first raid device update the family number */
        if (super->current_vol == 0) {
                __u32 sum;
                struct imsm_dev *_dev = __get_imsm_dev(mpb, 0);
-               struct imsm_disk *_disk = __get_imsm_disk(mpb, dl->index);
 
+               _disk = __get_imsm_disk(mpb, dl->index);
                if (!_dev || !_disk) {
                        fprintf(stderr, Name ": BUG mpb setup error\n");
                        return 1;
@@ -4356,6 +4485,37 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk,
        return 0;
 }
 
+/* mark_spare()
+ *   Function marks disk as spare and restores disk serial
+ *   in case it was previously marked as failed by takeover operation
+ * reruns:
+ *   -1 : critical error
+ *    0 : disk is marked as spare but serial is not set
+ *    1 : success
+ */
+int mark_spare(struct dl *disk)
+{
+       __u8 serial[MAX_RAID_SERIAL_LEN];
+       int ret_val = -1;
+
+       if (!disk)
+               return ret_val;
+
+       ret_val = 0;
+       if (!imsm_read_serial(disk->fd, NULL, serial)) {
+               /* Restore disk serial number, because takeover marks disk
+                * as failed and adds to serial ':0' before it becomes
+                * a spare disk.
+                */
+               serialcpy(disk->serial, serial);
+               serialcpy(disk->disk.serial, serial);
+               ret_val = 1;
+       }
+       disk->disk.status = SPARE_DISK;
+       disk->index = -1;
+
+       return ret_val;
+}
 
 static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
                             int fd, char *devname)
@@ -4393,7 +4553,6 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
        memset(dd, 0, sizeof(*dd));
        dd->major = major(stb.st_rdev);
        dd->minor = minor(stb.st_rdev);
-       dd->index = -1;
        dd->devname = devname ? strdup(devname) : NULL;
        dd->fd = fd;
        dd->e = NULL;
@@ -4410,7 +4569,7 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk,
        size /= 512;
        serialcpy(dd->disk.serial, dd->serial);
        dd->disk.total_blocks = __cpu_to_le32(size);
-       dd->disk.status = SPARE_DISK;
+       mark_spare(dd);
        if (sysfs_disk_to_scsi_id(fd, &id) == 0)
                dd->disk.scsi_id = __cpu_to_le32(id);
        else
@@ -4453,9 +4612,8 @@ static int remove_from_super_imsm(struct supertype *st, mdu_disk_info_t *dk)
        memset(dd, 0, sizeof(*dd));
        dd->major = dk->major;
        dd->minor = dk->minor;
-       dd->index = -1;
        dd->fd = -1;
-       dd->disk.status = SPARE_DISK;
+       mark_spare(dd);
        dd->action = DISK_REMOVE;
 
        dd->next = super->disk_mgmt_list;
@@ -4990,6 +5148,12 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
        if (!super)
                return 0;
 
+       if (mpb->num_raid_devs > 0 && mpb->num_disks != raiddisks) {
+               fprintf(stderr, Name ": the option-rom requires all "
+                       "member disks to be a member of all volumes.\n");
+               return 0;
+       }
+
        if (!validate_geometry_imsm_orom(super, level, layout, raiddisks, chunk, verbose)) {
                fprintf(stderr, Name ": RAID gemetry validation failed. "
                        "Cannot proceed with the action(s).\n");
@@ -5283,7 +5447,8 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
                        return validate_geometry_imsm_volume(st, level, layout,
                                                             raiddisks, chunk,
                                                             size, dev,
-                                                            freesize, verbose);
+                                                            freesize, 1)
+                               ? 1 : -1;
                }
        }
 
@@ -5373,10 +5538,8 @@ static int kill_subarray_imsm(struct supertype *st)
                struct dl *d;
 
                for (d = super->disks; d; d = d->next)
-                       if (d->index > -2) {
-                               d->index = -1;
-                               d->disk.status = SPARE_DISK;
-                       }
+                       if (d->index > -2)
+                               mark_spare(d);
        }
 
        super->updates_pending++;
@@ -6068,7 +6231,8 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
                        super->updates_pending++;
                        a->last_checkpoint = 0;
                }
-       } else if (!is_resyncing(dev) && !failed) {
+       } else if ((!is_resyncing(dev) && !failed) &&
+                  (imsm_reshape_blocks_arrays_changes(super) == 0)) {
                /* mark the start of the init process if nothing is failed */
                dprintf("imsm: mark resync start\n");
                if (map->map_state == IMSM_T_STATE_UNINITIALIZED)
@@ -6455,10 +6619,8 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
        dprintf("imsm: activate spare: inst=%d failed=%d (%d) level=%d\n",
                inst, failed, a->info.array.raid_disks, a->info.array.level);
 
-       if (dev->vol.migr_state &&
-           dev->vol.migr_type == MIGR_GEN_MIGR)
-               /* No repair during migration */
-               return NULL;
+       if (imsm_reshape_blocks_arrays_changes(super))
+                       return NULL;
 
        if (a->info.array.level == 4)
                /* No repair for takeovered array
@@ -6960,8 +7122,7 @@ static int apply_takeover_update(struct imsm_update_takeover *u,
                                        if (du->index > idx)
                                                du->index--;
                                /* mark as spare disk */
-                               dm->disk.status = SPARE_DISK;
-                               dm->index = -1;
+                               mark_spare(dm);
                        }
                }
                /* update map */