]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
imsm: provide for a larger mpb buffer when necessary
[thirdparty/mdadm.git] / super-intel.c
index 7a199353183be4e0b708c0355a69f53666e1f151..1dfcfa1d2c41b6c8b5471b2ee8e2e272eb629a18 100644 (file)
@@ -160,6 +160,8 @@ struct intel_super {
                struct imsm_super *anchor; /* immovable parameters */
        };
        size_t len; /* size of the 'buf' allocation */
+       void *next_buf; /* for realloc'ing buf from the manager */
+       size_t next_len;
        int updates_pending; /* count of pending updates for mdmon */
        int creating_imsm; /* flag to indicate container creation */
        int current_vol; /* index of raid device undergoing creation */
@@ -358,9 +360,9 @@ static __u32 get_imsm_ord_tbl_ent(struct imsm_dev *dev, int slot)
        struct imsm_map *map;
 
        if (dev->vol.migr_state)
-               map = get_imsm_map(dev, 0);
-       else
                map = get_imsm_map(dev, 1);
+       else
+               map = get_imsm_map(dev, 0);
 
        return map->disk_ord_tbl[slot];
 }
@@ -789,10 +791,6 @@ static int compare_super_imsm(struct supertype *st, struct supertype *tst)
            sec->anchor->num_raid_devs > 0) {
                if (first->anchor->family_num != sec->anchor->family_num)
                        return 3;
-               if (first->anchor->mpb_size != sec->anchor->mpb_size)
-                       return 3;
-               if (first->anchor->check_sum != sec->anchor->check_sum)
-                       return 3;
        }
 
        return 0;
@@ -910,6 +908,7 @@ load_imsm_disk(int fd, struct intel_super *super, char *devname, int keep_fd)
                dl->fd = keep_fd ? fd : -1;
                dl->devname = devname ? strdup(devname) : NULL;
                strncpy((char *) dl->serial, (char *) serial, MAX_RAID_SERIAL_LEN);
+               dl->index = -3;
        } else if (keep_fd) {
                close(dl->fd);
                dl->fd = fd;
@@ -930,7 +929,9 @@ load_imsm_disk(int fd, struct intel_super *super, char *devname, int keep_fd)
                        /* only set index on disks that are a member of a
                         * populated contianer, i.e. one with raid_devs
                         */
-                       if (status & SPARE_DISK)
+                       if (status & FAILED_DISK)
+                               dl->index = -2;
+                       else if (status & SPARE_DISK)
                                dl->index = -1;
                        else
                                dl->index = i;
@@ -938,26 +939,16 @@ load_imsm_disk(int fd, struct intel_super *super, char *devname, int keep_fd)
                }
        }
 
-       if (i == super->anchor->num_disks && alloc) {
-               if (devname)
-                       fprintf(stderr,
-                               Name ": failed to load disk with serial \'%s\' for %s\n",
-                               dl->serial, devname);
-               free(dl);
-               return 1;
-       }
-       if (i == super->anchor->num_disks && dl->index >= 0) {
-               if (devname)
-                       fprintf(stderr,
-                               Name ": confused... disk %d with serial \'%s\' "
-                               "is not listed in the current anchor\n",
-                               dl->index, dl->serial);
-               return 1;
+       if (dl->index == -3) {
+               fprintf(stderr, Name ": device %x:%x with serial %s"
+                       " does not belong to this container\n",
+                       dl->major, dl->minor, (char *) serial);
+               return 2;
        }
 
        if (alloc)
                super->disks = dl;
-               
+
        return 0;
 }
 
@@ -978,19 +969,41 @@ static int parse_raid_devices(struct intel_super *super)
 {
        int i;
        struct imsm_dev *dev_new;
-       size_t len;
+       size_t len, len_migr;
+       size_t space_needed = 0;
+       struct imsm_super *mpb = super->anchor;
 
        for (i = 0; i < super->anchor->num_raid_devs; i++) {
                struct imsm_dev *dev_iter = __get_imsm_dev(super->anchor, i);
 
-               len = sizeof_imsm_dev(dev_iter, 1);
-               dev_new = malloc(len);
+               len = sizeof_imsm_dev(dev_iter, 0);
+               len_migr = sizeof_imsm_dev(dev_iter, 1);
+               if (len_migr > len)
+                       space_needed += len_migr - len;
+               
+               dev_new = malloc(len_migr);
                if (!dev_new)
                        return 1;
                imsm_copy_dev(dev_new, dev_iter);
                super->dev_tbl[i] = dev_new;
        }
 
+       /* ensure that super->buf is large enough when all raid devices
+        * are migrating
+        */
+       if (__le32_to_cpu(mpb->mpb_size) + space_needed > super->len) {
+               void *buf;
+
+               len = ROUND_UP(__le32_to_cpu(mpb->mpb_size) + space_needed, 512);
+               if (posix_memalign(&buf, 512, len) != 0)
+                       return 1;
+
+               memcpy(buf, super->buf, len);
+               free(super->buf);
+               super->buf = buf;
+               super->len = len;
+       }
+               
        return 0;
 }
 
@@ -1056,7 +1069,6 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
        }
 
        __free_imsm(super, 0);
-       super->len = __le32_to_cpu(anchor->mpb_size);
        super->len = ROUND_UP(anchor->mpb_size, 512);
        if (posix_memalign(&super->buf, 512, super->len) != 0) {
                if (devname)
@@ -1113,6 +1125,7 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
        rc = load_imsm_disk(fd, super, devname, 0);
        if (rc == 0)
                rc = parse_raid_devices(super);
+
        return rc;
 }
 
@@ -2671,9 +2684,27 @@ static void imsm_process_update(struct supertype *st,
         *      flag
         */
        struct intel_super *super = st->sb;
-       struct imsm_super *mpb = super->anchor;
+       struct imsm_super *mpb;
        enum imsm_update_type type = *(enum imsm_update_type *) update->buf;
 
+       /* update requires a larger buf but the allocation failed */
+       if (super->next_len && !super->next_buf) {
+               super->next_len = 0;
+               return;
+       }
+
+       if (super->next_buf) {
+               memcpy(super->next_buf, super->buf, super->len);
+               free(super->buf);
+               super->len = super->next_len;
+               super->buf = super->next_buf;
+
+               super->next_len = 0;
+               super->next_buf = NULL;
+       }
+
+       mpb = super->anchor;
+
        switch (type) {
        case update_activate_spare: {
                struct imsm_update_activate_spare *u = (void *) update->buf; 
@@ -2867,25 +2898,23 @@ static void imsm_prepare_update(struct supertype *st,
                                struct metadata_update *update)
 {
        /**
-        * Allocate space to hold new disk entries, raid-device entries or a
-        * new mpb if necessary.  We currently maintain an mpb large enough to
-        * hold 2 subarrays for the given number of disks.  This may not be
-        * sufficient when reshaping.
-        *
-        * FIX ME handle the reshape case.
-        *
-        * The monitor will be able to safely change super->mpb by arranging
-        * for it to be freed in check_update_queue().  I.e. the monitor thread
-        * will start using the new pointer and the manager can continue to use
-        * the old value until check_update_queue() runs.
+        * Allocate space to hold new disk entries, raid-device entries or a new
+        * mpb if necessary.  The manager synchronously waits for updates to
+        * complete in the monitor, so new mpb buffers allocated here can be
+        * integrated by the monitor thread without worrying about live pointers
+        * in the manager thread.
         */
        enum imsm_update_type type = *(enum imsm_update_type *) update->buf;
+       struct intel_super *super = st->sb;
+       struct imsm_super *mpb = super->anchor;
+       size_t buf_len;
+       size_t len = 0;
 
        switch (type) {
        case update_create_array: {
                struct imsm_update_create_array *u = (void *) update->buf;
-               size_t len = sizeof_imsm_dev(&u->dev, 1);
 
+               len = sizeof_imsm_dev(&u->dev, 1);
                update->space = malloc(len);
                break;
        default:
@@ -2893,7 +2922,25 @@ static void imsm_prepare_update(struct supertype *st,
        }
        }
 
-       return;
+       /* check if we need a larger metadata buffer */
+       if (super->next_buf)
+               buf_len = super->next_len;
+       else
+               buf_len = super->len;
+
+       if (__le32_to_cpu(mpb->mpb_size) + len > buf_len) {
+               /* ok we need a larger buf than what is currently allocated
+                * if this allocation fails process_update will notice that
+                * ->next_len is set and ->next_buf is NULL
+                */
+               buf_len = ROUND_UP(__le32_to_cpu(mpb->mpb_size) + len, 512);
+               if (super->next_buf)
+                       free(super->next_buf);
+
+               super->next_len = buf_len;
+               if (posix_memalign(&super->next_buf, buf_len, 512) != 0)
+                       super->next_buf = NULL;
+       }
 }
 
 /* must be called while manager is quiesced */