]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
Release 3.2.6 - stability release
[thirdparty/mdadm.git] / super-intel.c
index 32a53d1da0c590a10d0b07bf511c7cc4a2a612b0..544cb05967dc8a7f8a33457e853798211b1952fe 100644 (file)
@@ -918,6 +918,7 @@ static unsigned long long blocks_per_member(struct imsm_map *map)
        return join_u32(map->blocks_per_member_lo, map->blocks_per_member_hi);
 }
 
+#ifndef MDASSEMBLE
 static unsigned long long num_data_stripes(struct imsm_map *map)
 {
        if (map == NULL)
@@ -929,6 +930,7 @@ static void set_total_blocks(struct imsm_disk *disk, unsigned long long n)
 {
        split_ull(n, &disk->total_blocks_lo, &disk->total_blocks_hi);
 }
+#endif
 
 static void set_pba_of_lba0(struct imsm_map *map, unsigned long long n)
 {
@@ -2454,6 +2456,32 @@ int imsm_reshape_blocks_arrays_changes(struct intel_super *super)
        }
        return rv;
 }
+static unsigned long long imsm_component_size_aligment_check(int level,
+                                             int chunk_size,
+                                             unsigned long long component_size)
+{
+       unsigned int component_size_alligment;
+
+       /* check component size aligment
+       */
+       component_size_alligment = component_size % (chunk_size/512);
+
+       dprintf("imsm_component_size_aligment_check(Level: %i, "
+               "chunk_size = %i, component_size = %llu), "
+               "component_size_alligment = %u\n",
+               level, chunk_size, component_size,
+               component_size_alligment);
+
+       if (component_size_alligment && (level != 1) && (level != UnSet)) {
+               dprintf("imsm: reported component size alligned from %llu ",
+                       component_size);
+               component_size -= component_size_alligment;
+               dprintf("to %llu (%i).\n",
+                       component_size, component_size_alligment);
+       }
+
+       return component_size;
+}
 
 static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, char *dmap)
 {
@@ -2465,7 +2493,6 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        struct imsm_map *map_to_analyse = map;
        struct dl *dl;
        char *devname;
-       unsigned int component_size_alligment;
        int map_disks = info->array.raid_disks;
 
        memset(info, 0, sizeof(*info));
@@ -2548,19 +2575,10 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info,
        info->data_offset         = pba_of_lba0(map_to_analyse);
        info->component_size      = blocks_per_member(map_to_analyse);
 
-       /* check component size aligment
-        */
-       component_size_alligment =
-               info->component_size % (info->array.chunk_size/512);
-
-       if (component_size_alligment &&
-           (info->array.level != 1) && (info->array.level != UnSet)) {
-               dprintf("imsm: reported component size alligned from %llu ",
-                       info->component_size);
-               info->component_size -= component_size_alligment;
-               dprintf("to %llu (%i).\n",
-                       info->component_size, component_size_alligment);
-       }
+       info->component_size = imsm_component_size_aligment_check(
+                                                       info->array.level,
+                                                       info->array.chunk_size,
+                                                       info->component_size);
 
        memset(info->uuid, 0, sizeof(info->uuid));
        info->recovery_start = MaxSector;
@@ -4090,6 +4108,8 @@ imsm_thunderdome(struct intel_super **super_list, int len)
                if (s == champion)
                        continue;
 
+               mpb->attributes |= s->anchor->attributes & MPB_ATTRIB_2TB_DISK;
+
                for (i = 0; i < mpb->num_disks; i++) {
                        struct imsm_disk *disk;
 
@@ -4333,7 +4353,7 @@ static int get_super_block(struct intel_super **super_list, int devnum, char *de
        } else {
                if (s)
                        free(s);
-               if (dfd)
+               if (dfd >= 0)
                        close(dfd);
        }
        if ((dfd >= 0) && (!keep_fd))
@@ -4658,7 +4678,8 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
        map->blocks_per_strip = __cpu_to_le16(info_to_blocks_per_strip(info));
        map->failed_disk_num = ~0;
        if (info->level > 0)
-               map->map_state = IMSM_T_STATE_UNINITIALIZED;
+               map->map_state = (info->state ? IMSM_T_STATE_NORMAL
+                                 : IMSM_T_STATE_UNINITIALIZED);
        else
                map->map_state = info->failed_disks ? IMSM_T_STATE_FAILED :
                                                      IMSM_T_STATE_NORMAL;
@@ -5666,6 +5687,7 @@ get_devices(const char *hba_path)
                        free(dv);
                }
        }
+       closedir(dir);
        return devlist;
 }
 
@@ -6108,7 +6130,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
        return 1;
 }
 
-static int reserve_space(struct supertype *st, int raiddisks,
+static int imsm_get_free_size(struct supertype *st, int raiddisks,
                         unsigned long long size, int chunk,
                         unsigned long long *freesize)
 {
@@ -6186,9 +6208,32 @@ static int reserve_space(struct supertype *st, int raiddisks,
 
        *freesize = size;
 
+       dprintf("imsm: imsm_get_free_size() returns : %llu\n", size);
+
        return 1;
 }
 
+static int reserve_space(struct supertype *st, int raiddisks,
+                        unsigned long long size, int chunk,
+                        unsigned long long *freesize)
+{
+       struct intel_super *super = st->sb;
+       struct dl *dl;
+       int cnt;
+       int rv = 0;
+
+       rv = imsm_get_free_size(st, raiddisks, size, chunk, freesize);
+       if (rv) {
+               cnt = 0;
+               for (dl = super->disks; dl; dl = dl->next)
+                       if (dl->e)
+                               dl->raiddisk = cnt++;
+               rv = 1;
+       }
+
+       return rv;
+}
+
 static int validate_geometry_imsm(struct supertype *st, int level, int layout,
                                  int raiddisks, int *chunk, unsigned long long size,
                                  char *dev, unsigned long long *freesize,
@@ -7232,6 +7277,8 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
        struct imsm_dev *dev = get_imsm_dev(super, inst);
        struct imsm_map *map = get_imsm_map(dev, MAP_0);
        struct imsm_disk *disk;
+       struct mdinfo *mdi;
+       int recovery_not_finished = 0;
        int failed;
        __u32 ord;
        __u8 map_state;
@@ -7272,6 +7319,21 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
                dprintf("normal: ");
                if (is_rebuilding(dev)) {
                        dprintf("while rebuilding");
+                       /* check if recovery is really finished */
+                       for (mdi = a->info.devs; mdi ; mdi = mdi->next)
+                               if (mdi->recovery_start != MaxSector) {
+                                       recovery_not_finished = 1;
+                                       break;
+                               }
+                       if (recovery_not_finished) {
+                               dprintf("\nimsm: Rebuild has not finished yet, "
+                                               "state not changed");
+                               if (a->last_checkpoint < mdi->recovery_start) {
+                                       a->last_checkpoint = mdi->recovery_start;
+                                       super->updates_pending++;
+                               }
+                               break;
+                       }
                        end_migration(dev, super, map_state);
                        map = get_imsm_map(dev, MAP_0);
                        map->failed_disk_num = ~0;
@@ -9470,7 +9532,8 @@ static int imsm_find_array_minor_by_subdev(int subdev, int container, int *minor
 
 static int imsm_reshape_is_allowed_on_container(struct supertype *st,
                                                struct geo_params *geo,
-                                               int *old_raid_disks)
+                                               int *old_raid_disks,
+                                               int direction)
 {
        /* currently we only support increasing the number of devices
         * for a container.  This increases the number of device for each
@@ -9494,6 +9557,12 @@ static int imsm_reshape_is_allowed_on_container(struct supertype *st,
                return ret_val;
        }
 
+       if (direction == ROLLBACK_METADATA_CHANGES) {
+               dprintf("imsm: Metadata changes rollback is not supported for "
+                       "container operation.\n");
+               return ret_val;
+       }
+
        info = container_content_imsm(st, NULL);
        for (member = info; member; member = member->next) {
                int result;
@@ -9814,11 +9883,13 @@ 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
+* Parameters:  Geometry parameters, supertype structure,
+*              metadata change direction (apply/rollback)
 * Returns:     Operation type code on success, -1 if fail
 ****************************************************************************/
 enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
-                                          struct geo_params *geo)
+                                          struct geo_params *geo,
+                                          int direction)
 {
        struct mdinfo info;
        int change = -1;
@@ -9832,6 +9903,9 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        struct imsm_dev *dev;
        struct intel_super *super;
        long long current_size;
+       unsigned long long free_size;
+       long long max_size;
+       int rv;
 
        getinfo_super_imsm_volume(st, &info, NULL);
        if ((geo->level != info.array.level) &&
@@ -9917,11 +9991,20 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        super = st->sb;
        dev = get_imsm_dev(super, super->current_vol);
        data_disks = imsm_num_data_members(dev , MAP_0);
-       /* compute current size in K per disk member
+       /* compute current size per disk member
         */
-       current_size = info.custom_array_size / 2 / data_disks;
+       current_size = info.custom_array_size / data_disks;
+
+       if (geo->size > 0) {
+               /* align component size
+                */
+               geo->size = imsm_component_size_aligment_check(
+                                   get_imsm_raid_level(dev->vol.map),
+                                   chunk * 1024,
+                                   geo->size * 2);
+       }
 
-       if ((current_size != geo->size) && (geo->size > 0)) {
+       if ((current_size != geo->size) && (geo->size >= 0)) {
                if (change != -1) {
                        fprintf(stderr,
                                Name " Error. Size change should be the only "
@@ -9936,19 +10019,62 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
                                super->current_vol, st->devnum);
                        goto analyse_change_exit;
                }
-               geo->size *= 2;
-               /* round size due to metadata compatibility
+               /* check the maximum available size
                 */
-               geo->size = (geo->size >> SECT_PER_MB_SHIFT)
-                           << SECT_PER_MB_SHIFT;
-               dprintf("Prepare update for size change to %llu\n", geo->size );
-               if (current_size >= geo->size) {
-                       fprintf(stderr,
-                               Name " Error. Size expanssion is supported only"
-                               " (current size is %llu, requested size "
-                               "/rounded/ is %llu).\n",
-                               current_size, geo->size);
-                       goto analyse_change_exit;
+               rv =  imsm_get_free_size(st, dev->vol.map->num_members,
+                                        0, chunk, &free_size);
+               if (rv == 0)
+                       /* Cannot find maximum available space
+                        */
+                       max_size = 0;
+               else {
+                       max_size = free_size + current_size;
+                       /* align component size
+                        */
+                       max_size = imsm_component_size_aligment_check(
+                                       get_imsm_raid_level(dev->vol.map),
+                                       chunk * 1024,
+                                       max_size);
+               }
+               if (geo->size == 0) {
+                       /* requested size change to the maximum available size
+                        */
+                       if (max_size == 0) {
+                               fprintf(stderr, Name " Error. Cannot find "
+                                       "maximum available space.\n");
+                               change = -1;
+                               goto analyse_change_exit;
+                       } else
+                               geo->size = max_size;
+               }
+
+               if ((direction == ROLLBACK_METADATA_CHANGES)) {
+                       /* accept size for rollback only
+                       */
+               } else {
+                       /* round size due to metadata compatibility
+                       */
+                       geo->size = (geo->size >> SECT_PER_MB_SHIFT)
+                                   << SECT_PER_MB_SHIFT;
+                       dprintf("Prepare update for size change to %llu\n",
+                               geo->size );
+                       if (current_size >= geo->size) {
+                               fprintf(stderr,
+                                       Name " Error. Size expansion is "
+                                       "supported only (current size is %llu, "
+                                       "requested size /rounded/ is %llu).\n",
+                                       current_size, geo->size);
+                               goto analyse_change_exit;
+                       }
+                       if (max_size && geo->size > max_size) {
+                               fprintf(stderr,
+                                       Name " Error. Requested size is larger "
+                                       "than maximum available size (maximum "
+                                       "available size is %llu, "
+                                       "requested size /rounded/ is %llu).\n",
+                                       max_size, geo->size);
+                               goto analyse_change_exit;
+                       }
                }
                geo->size *= data_disks;
                geo->raid_disks = dev->vol.map->num_members;
@@ -9978,7 +10104,12 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
        }
 
 analyse_change_exit:
-
+       if ((direction == ROLLBACK_METADATA_CHANGES) &&
+            ((change == CH_MIGRATION) || (change == CH_TAKEOVER))) {
+               dprintf("imsm: Metadata changes rollback is not supported for "
+                       "migration and takeover operations.\n");
+               change = -1;
+       }
        return change;
 }
 
@@ -10049,7 +10180,7 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
                int old_raid_disks = 0;
 
                if (imsm_reshape_is_allowed_on_container(
-                           st, &geo, &old_raid_disks)) {
+                           st, &geo, &old_raid_disks, direction)) {
                        struct imsm_update_reshape *u = NULL;
                        int len;
 
@@ -10098,7 +10229,7 @@ static int imsm_reshape_super(struct supertype *st, long long size, int level,
                        goto exit_imsm_reshape_super;
                }
                super->current_vol = dev->index;
-               change = imsm_analyze_change(st, &geo);
+               change = imsm_analyze_change(st, &geo, direction);
                switch (change) {
                case CH_TAKEOVER:
                        ret_val = imsm_takeover(st, &geo);
@@ -10240,8 +10371,10 @@ int check_degradation_change(struct mdinfo *info,
                             int degraded)
 {
        unsigned long long new_degraded;
-       sysfs_get_ll(info, NULL, "degraded", &new_degraded);
-       if (new_degraded != (unsigned long long)degraded) {
+       int rv;
+
+       rv = sysfs_get_ll(info, NULL, "degraded", &new_degraded);
+       if ((rv == -1) || (new_degraded != (unsigned long long)degraded)) {
                /* check each device to ensure it is still working */
                struct mdinfo *sd;
                new_degraded = 0;