]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: return free space after volume for expand
[thirdparty/mdadm.git] / super-intel.c
index 5bd703560eca6d6c94762732fcf25377be81a5a6..e249d9250428dd90cd56d77ea0b3e0f5dd09b818 100644 (file)
@@ -499,8 +499,15 @@ struct intel_disk {
        struct intel_disk *next;
 };
 
+/**
+ * struct extent - reserved space details.
+ * @start: start offset.
+ * @size: size of reservation, set to 0 for metadata reservation.
+ * @vol: index of the volume, meaningful if &size is set.
+ */
 struct extent {
        unsigned long long start, size;
+       int vol;
 };
 
 /* definitions of reshape process types */
@@ -1539,9 +1546,10 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
                                  int get_minimal_reservation)
 {
        /* find a list of used extents on the given physical device */
-       struct extent *rv, *e;
-       int i;
        int memberships = count_memberships(dl, super);
+       struct extent *rv = xcalloc(memberships + 1, sizeof(struct extent));
+       struct extent *e = rv;
+       int i;
        __u32 reservation;
 
        /* trim the reserved area for spares, so they can join any array
@@ -1553,9 +1561,6 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
        else
                reservation = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
 
-       rv = xcalloc(sizeof(struct extent), (memberships + 1));
-       e = rv;
-
        for (i = 0; i < super->anchor->num_raid_devs; i++) {
                struct imsm_dev *dev = get_imsm_dev(super, i);
                struct imsm_map *map = get_imsm_map(dev, MAP_0);
@@ -1563,6 +1568,7 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl,
                if (get_imsm_disk_slot(map, dl->index) >= 0) {
                        e->start = pba_of_lba0(map);
                        e->size = per_dev_array_size(map);
+                       e->vol = i;
                        e++;
                }
        }
@@ -6894,24 +6900,26 @@ static unsigned long long find_size(struct extent *e, int *idx, int num_extents)
        return end - base_start;
 }
 
-/** merge_extents() - analyze extents and get max common free size.
+/** merge_extents() - analyze extents and get free size.
  * @super: Intel metadata, not NULL.
+ * @expanding: if set, we are expanding &super->current_vol.
  *
- * Build a composite disk with all known extents and generate a new maxsize
- * given the "all disks in an array must share a common start offset"
- * constraint.
+ * Build a composite disk with all known extents and generate a size given the
+ * "all disks in an array must share a common start offset" constraint.
+ * If a volume is expanded, then return free space after the volume.
  *
- * Return: Max free space or 0 on failure.
+ * Return: Free space or 0 on failure.
  */
-static unsigned long long merge_extents(struct intel_super *super)
+static unsigned long long merge_extents(struct intel_super *super, const bool expanding)
 {
        struct extent *e;
        struct dl *dl;
-       int i, j;
-       int start_extent, sum_extents = 0;
-       unsigned long long pos;
+       int i, j, pos_vol_idx = -1;
+       int extent_idx = 0;
+       int sum_extents = 0;
+       unsigned long long pos = 0;
        unsigned long long start = 0;
-       unsigned long long maxsize;
+       unsigned long long maxsize = 0;
        unsigned long reserve;
 
        for (dl = super->disks; dl; dl = dl->next)
@@ -6936,26 +6944,26 @@ static unsigned long long merge_extents(struct intel_super *super)
        j = 0;
        while (i < sum_extents) {
                e[j].start = e[i].start;
+               e[j].vol = e[i].vol;
                e[j].size = find_size(e, &i, sum_extents);
                j++;
                if (e[j-1].size == 0)
                        break;
        }
 
-       pos = 0;
-       maxsize = 0;
-       start_extent = 0;
        i = 0;
        do {
-               unsigned long long esize;
+               unsigned long long esize = e[i].start - pos;
 
-               esize = e[i].start - pos;
-               if (esize >= maxsize) {
+               if (expanding ? pos_vol_idx == super->current_vol : esize >= maxsize) {
                        maxsize = esize;
                        start = pos;
-                       start_extent = i;
+                       extent_idx = i;
                }
+
                pos = e[i].start + e[i].size;
+               pos_vol_idx = e[i].vol;
+
                i++;
        } while (e[i-1].size);
        free(e);
@@ -6966,7 +6974,7 @@ static unsigned long long merge_extents(struct intel_super *super)
        /* FIXME assumes volume at offset 0 is the first volume in a
         * container
         */
-       if (start_extent > 0)
+       if (extent_idx > 0)
                reserve = IMSM_RESERVED_SECTORS; /* gap between raid regions */
        else
                reserve = 0;
@@ -7577,7 +7585,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
                return 0;
        }
 
-       maxsize = merge_extents(super);
+       maxsize = merge_extents(super, false);
 
        if (mpb->num_raid_devs > 0 && size && size != maxsize)
                pr_err("attempting to create a second volume with size less then remaining space.\n");
@@ -7626,7 +7634,8 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super,
                                        const int raiddisks,
                                        unsigned long long size,
                                        const int chunk,
-                                       unsigned long long *freesize)
+                                       unsigned long long *freesize,
+                                       bool expanding)
 {
        struct imsm_super *mpb = super->anchor;
        struct dl *dl;
@@ -7663,7 +7672,7 @@ static imsm_status_t imsm_get_free_size(struct intel_super *super,
                cnt++;
        }
 
-       maxsize = merge_extents(super);
+       maxsize = merge_extents(super, expanding);
        if (maxsize < minsize)  {
                pr_err("imsm: Free space is %llu but must be equal or larger than %llu.\n",
                       maxsize, minsize);
@@ -7721,7 +7730,7 @@ static imsm_status_t autolayout_imsm(struct intel_super *super,
        int vol_cnt = super->anchor->num_raid_devs;
        imsm_status_t rv;
 
-       rv = imsm_get_free_size(super, raiddisks, size, chunk, freesize);
+       rv = imsm_get_free_size(super, raiddisks, size, chunk, freesize, false);
        if (rv != IMSM_STATUS_OK)
                return IMSM_STATUS_ERROR;
 
@@ -11685,19 +11694,13 @@ static imsm_status_t imsm_analyze_expand(struct supertype *st,
                goto success;
        }
 
-       if (super->current_vol + 1 != super->anchor->num_raid_devs) {
-               pr_err("imsm: The last volume in container can be expanded only (%i/%s).\n",
-                      super->current_vol, st->devnm);
-               return IMSM_STATUS_ERROR;
-       }
-
        if (data_disks == 0) {
                pr_err("imsm: Cannot retrieve data disks.\n");
                return IMSM_STATUS_ERROR;
        }
        current_size = array->custom_array_size / data_disks;
 
-       rv = imsm_get_free_size(super, dev->vol.map->num_members, 0, chunk_kib, &free_size);
+       rv = imsm_get_free_size(super, dev->vol.map->num_members, 0, chunk_kib, &free_size, true);
        if (rv != IMSM_STATUS_OK) {
                pr_err("imsm: Cannot find free space for expand.\n");
                return IMSM_STATUS_ERROR;