]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super-intel.c
Incremental: fix adding devices with --incremental
[thirdparty/mdadm.git] / super-intel.c
index 1cf8716962052c8813cd3cb6db1725674b9725cf..a351b60fc7021b8e0633fb710aa9be30a15c4d92 100644 (file)
@@ -318,7 +318,7 @@ static void set_migr_type(struct imsm_dev *dev, __u8 migr_type)
 
 static unsigned int sector_count(__u32 bytes)
 {
-       return ((bytes + (512-1)) & (~(512-1))) / 512;
+       return ROUND_UP(bytes, 512) / 512;
 }
 
 static unsigned int mpb_sectors(struct imsm_super *mpb)
@@ -353,6 +353,9 @@ struct intel_super {
                void *migr_rec_buf; /* buffer for I/O operations */
                struct migr_record *migr_rec; /* migration record */
        };
+       int clean_migration_record_by_mdmon; /* when reshape is switched to next
+               array, it indicates that mdmon is allowed to clean migration
+               record */
        size_t len; /* size of the 'buf' allocation */
        void *next_buf; /* for realloc'ing buf from the manager */
        size_t next_len;
@@ -2683,7 +2686,17 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char *
                        enough = 0;
                else /* we're normal, or already degraded */
                        enough = 1;
-
+               if (is_gen_migration(dev) && missing) {
+                       /* during general migration we need all disks
+                        * that process is running on.
+                        * No new missing disk is allowed.
+                        */
+                       max_enough = -1;
+                       enough = -1;
+                       /* no more checks necessary
+                        */
+                       break;
+               }
                /* in the missing/failed disk case check to see
                 * if at least one array is runnable
                 */
@@ -3285,7 +3298,7 @@ static int parse_raid_devices(struct intel_super *super)
                len_migr = sizeof_imsm_dev(dev_iter, 1);
                if (len_migr > len)
                        space_needed += len_migr - len;
-               
+
                dv = malloc(sizeof(*dv));
                if (!dv)
                        return 1;
@@ -3321,7 +3334,7 @@ static int parse_raid_devices(struct intel_super *super)
                super->buf = buf;
                super->len = len;
        }
-               
+
        return 0;
 }
 
@@ -3455,6 +3468,7 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
                free(super->buf);
                return 2;
        }
+       super->clean_migration_record_by_mdmon = 0;
 
        if (!sectors) {
                check_sum = __gen_imsm_checksum(super->anchor);
@@ -4122,12 +4136,12 @@ get_devlist_super_block(struct md_list *devlist, struct intel_super **super_list
        struct md_list *tmpdev;
        int err = 0;
        int i = 0;
-       int lmax = 0;
 
        for (i = 0, tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) {
                if (tmpdev->used != 1)
                        continue;
                if (tmpdev->container == 1) {
+                       int lmax = 0;
                        int fd = dev_open(tmpdev->devname, O_RDONLY|O_EXCL);
                        if (fd < 0) {
                                fprintf(stderr, Name ": cannot open device %s: %s\n",
@@ -5019,6 +5033,10 @@ static int write_super_imsm(struct supertype *st, int doclose)
        sum = __gen_imsm_checksum(mpb);
        mpb->check_sum = __cpu_to_le32(sum);
 
+       if (super->clean_migration_record_by_mdmon) {
+               clear_migration_record = 1;
+               super->clean_migration_record_by_mdmon = 0;
+       }
        if (clear_migration_record)
                memset(super->migr_rec_buf, 0, MIGR_REC_BUF_SIZE);
 
@@ -5026,9 +5044,7 @@ static int write_super_imsm(struct supertype *st, int doclose)
        for (d = super->disks; d ; d = d->next) {
                if (d->index < 0 || is_failed(&d->disk))
                        continue;
-               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;
 
@@ -5039,6 +5055,13 @@ static int write_super_imsm(struct supertype *st, int doclose)
                                        perror("Write migr_rec failed");
                        }
                }
+
+               if (store_imsm_mpb(d->fd, mpb))
+                       fprintf(stderr,
+                               "%s: failed for device %d:%d (fd: %d)%s\n",
+                               __func__, d->major, d->minor,
+                               d->fd, strerror(errno));
+
                if (doclose) {
                        close(d->fd);
                        d->fd = -1;
@@ -5363,6 +5386,377 @@ static int is_raid_level_supported(const struct imsm_orom *orom, int level, int
        return 0;
 }
 
+
+static int
+active_arrays_by_format(char *name, char* hba, struct md_list **devlist,
+                       int dpa, int verbose)
+{
+       struct mdstat_ent *mdstat = mdstat_read(0, 0);
+       struct mdstat_ent *memb = NULL;
+       int count = 0;
+       int num = 0;
+       struct md_list *dv = NULL;
+       int found;
+
+       for (memb = mdstat ; memb ; memb = memb->next) {
+               if (memb->metadata_version &&
+                   (strncmp(memb->metadata_version, "external:", 9) == 0)  &&
+                   (strcmp(&memb->metadata_version[9], name) == 0) &&
+                   !is_subarray(memb->metadata_version+9) &&
+                   memb->members) {
+                       struct dev_member *dev = memb->members;
+                       int fd = -1;
+                       while(dev && (fd < 0)) {
+                               char *path = malloc(strlen(dev->name) + strlen("/dev/") + 1);
+                               if (path) {
+                                       num = sprintf(path, "%s%s", "/dev/", dev->name);
+                                       if (num > 0)
+                                               fd = open(path, O_RDONLY, 0);
+                                       if ((num <= 0) || (fd < 0)) {
+                                               pr_vrb(": Cannot open %s: %s\n",
+                                                      dev->name, strerror(errno));
+                                       }
+                                       free(path);
+                               }
+                               dev = dev->next;
+                       }
+                       found = 0;
+                       if ((fd >= 0) && disk_attached_to_hba(fd, hba)) {
+                               struct mdstat_ent *vol;
+                               for (vol = mdstat ; vol ; vol = vol->next) {
+                                       if ((vol->active > 0) &&
+                                           vol->metadata_version &&
+                                           is_container_member(vol, memb->dev)) {
+                                               found++;
+                                               count++;
+                                       }
+                               }
+                               if (*devlist && (found < dpa)) {
+                                 dv = calloc(1, sizeof(*dv));
+                                       if (dv == NULL)
+                                               fprintf(stderr, Name ": calloc failed\n");
+                                       else {
+                                               dv->devname = malloc(strlen(memb->dev) + strlen("/dev/") + 1);
+                                               if (dv->devname != NULL) {
+                                                       sprintf(dv->devname, "%s%s", "/dev/", memb->dev);
+                                                       dv->found = found;
+                                                       dv->used = 0;
+                                                       dv->next = *devlist;
+                                                       *devlist = dv;
+                                               } else
+                                                       free(dv);
+                                       }
+                               }
+                       }
+                       if (fd >= 0)
+                               close(fd);
+               }
+       }
+       free_mdstat(mdstat);
+       return count;
+}
+
+#ifdef DEBUG_LOOP
+static struct md_list*
+get_loop_devices(void)
+{
+       int i;
+       struct md_list *devlist = NULL;
+       struct md_list *dv = NULL;
+
+       for(i = 0; i < 12; i++) {
+               dv = calloc(1, sizeof(*dv));
+               if (dv == NULL) {
+                       fprintf(stderr, Name ": calloc failed\n");
+                       break;
+               }
+               dv->devname = malloc(40);
+               if (dv->devname == NULL) {
+                       fprintf(stderr, Name ": malloc failed\n");
+                       free(dv);
+                       break;
+               }
+               sprintf(dv->devname, "/dev/loop%d", i);
+               dv->next = devlist;
+               devlist = dv;
+       }
+       return devlist;
+}
+#endif
+
+static struct md_list*
+get_devices(const char *hba_path)
+{
+       struct md_list *devlist = NULL;
+       struct md_list *dv = NULL;
+       struct dirent *ent;
+       DIR *dir;
+       int err = 0;
+
+#if DEBUG_LOOP
+       devlist = get_loop_devices();
+       return devlist;
+#endif
+       /* scroll through /sys/dev/block looking for devices attached to
+        * this hba
+        */
+       dir = opendir("/sys/dev/block");
+       for (ent = dir ? readdir(dir) : NULL; ent; ent = readdir(dir)) {
+               int fd;
+               char buf[1024];
+               int major, minor;
+               char *path = NULL;
+               if (sscanf(ent->d_name, "%d:%d", &major, &minor) != 2)
+                       continue;
+               path = devt_to_devpath(makedev(major, minor));
+               if (!path)
+                       continue;
+               if (!path_attached_to_hba(path, hba_path)) {
+                       free(path);
+                       path = NULL;
+                       continue;
+               }
+               free(path);
+               path = NULL;
+               fd = dev_open(ent->d_name, O_RDONLY);
+               if (fd >= 0) {
+                       fd2devname(fd, buf);
+                       close(fd);
+               } else {
+                       fprintf(stderr, Name ": cannot open device: %s\n",
+                               ent->d_name);
+                       continue;
+               }
+
+
+               dv = calloc(1, sizeof(*dv));
+               if (dv == NULL) {
+                       fprintf(stderr, Name ": malloc failed\n");
+                       err = 1;
+                       break;
+               }
+               dv->devname = strdup(buf);
+               if (dv->devname == NULL) {
+                       fprintf(stderr, Name ": malloc failed\n");
+                       err = 1;
+                       free(dv);
+                       break;
+               }
+               dv->next = devlist;
+               devlist = dv;
+       }
+       if (err) {
+               while(devlist) {
+                       dv = devlist;
+                       devlist = devlist->next;
+                       free(dv->devname);
+                       free(dv);
+               }
+       }
+       return devlist;
+}
+
+static int
+count_volumes_list(struct md_list *devlist, char *homehost,
+                  int verbose, int *found)
+{
+       struct md_list *tmpdev;
+       int count = 0;
+       struct supertype *st = NULL;
+
+       /* first walk the list of devices to find a consistent set
+        * that match the criterea, if that is possible.
+        * We flag the ones we like with 'used'.
+        */
+       *found = 0;
+       st = match_metadata_desc_imsm("imsm");
+       if (st == NULL) {
+               pr_vrb(": cannot allocate memory for imsm supertype\n");
+               return 0;
+       }
+
+       for (tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) {
+               char *devname = tmpdev->devname;
+               struct stat stb;
+               struct supertype *tst;
+               int dfd;
+               if (tmpdev->used > 1)
+                       continue;
+               tst = dup_super(st);
+               if (tst == NULL) {
+                       pr_vrb(": cannot allocate memory for imsm supertype\n");
+                       goto err_1;
+               }
+               tmpdev->container = 0;
+               dfd = dev_open(devname, O_RDONLY|O_EXCL);
+               if (dfd < 0) {
+                       dprintf(": cannot open device %s: %s\n",
+                               devname, strerror(errno));
+                       tmpdev->used = 2;
+               } else if (fstat(dfd, &stb)< 0) {
+                       /* Impossible! */
+                       dprintf(": fstat failed for %s: %s\n",
+                               devname, strerror(errno));
+                       tmpdev->used = 2;
+               } else if ((stb.st_mode & S_IFMT) != S_IFBLK) {
+                       dprintf(": %s is not a block device.\n",
+                               devname);
+                       tmpdev->used = 2;
+               } else if (must_be_container(dfd)) {
+                       struct supertype *cst;
+                       cst = super_by_fd(dfd, NULL);
+                       if (cst == NULL) {
+                               dprintf(": cannot recognize container type %s\n",
+                                       devname);
+                               tmpdev->used = 2;
+                       } else if (tst->ss != st->ss) {
+                               dprintf(": non-imsm container - ignore it: %s\n",
+                                       devname);
+                               tmpdev->used = 2;
+                       } else if (!tst->ss->load_container ||
+                                  tst->ss->load_container(tst, dfd, NULL))
+                               tmpdev->used = 2;
+                       else {
+                               tmpdev->container = 1;
+                       }
+                       if (cst)
+                               cst->ss->free_super(cst);
+               } else {
+                       tmpdev->st_rdev = stb.st_rdev;
+                       if (tst->ss->load_super(tst,dfd, NULL)) {
+                               dprintf(": no RAID superblock on %s\n",
+                                       devname);
+                               tmpdev->used = 2;
+                       } else if (tst->ss->compare_super == NULL) {
+                               dprintf(": Cannot assemble %s metadata on %s\n",
+                                       tst->ss->name, devname);
+                               tmpdev->used = 2;
+                       }
+               }
+               if (dfd >= 0)
+                       close(dfd);
+               if (tmpdev->used == 2 || tmpdev->used == 4) {
+                       /* Ignore unrecognised devices during auto-assembly */
+                       goto loop;
+               }
+               else {
+                       struct mdinfo info;
+                       tst->ss->getinfo_super(tst, &info, NULL);
+
+                       if (st->minor_version == -1)
+                               st->minor_version = tst->minor_version;
+
+                       if (memcmp(info.uuid, uuid_zero,
+                                  sizeof(int[4])) == 0) {
+                               /* this is a floating spare.  It cannot define
+                                * an array unless there are no more arrays of
+                                * this type to be found.  It can be included
+                                * in an array of this type though.
+                                */
+                               tmpdev->used = 3;
+                               goto loop;
+                       }
+
+                       if (st->ss != tst->ss ||
+                           st->minor_version != tst->minor_version ||
+                           st->ss->compare_super(st, tst) != 0) {
+                               /* Some mismatch. If exactly one array matches this host,
+                                * we can resolve on that one.
+                                * Or, if we are auto assembling, we just ignore the second
+                                * for now.
+                                */
+                               dprintf(": superblock on %s doesn't match others - assembly aborted\n",
+                                       devname);
+                               goto loop;
+                       }
+                       tmpdev->used = 1;
+                       *found = 1;
+                       dprintf("found: devname: %s\n", devname);
+               }
+       loop:
+               if (tst)
+                       tst->ss->free_super(tst);
+       }
+       if (*found != 0) {
+               int err;
+               if ((err = load_super_imsm_all(st, -1, &st->sb, NULL, devlist, 0)) == 0) {
+                       struct mdinfo *iter, *head = st->ss->container_content(st, NULL);
+                       for (iter = head; iter; iter = iter->next) {
+                               dprintf("content->text_version: %s vol\n",
+                                       iter->text_version);
+                               if (iter->array.state & (1<<MD_SB_BLOCK_VOLUME)) {
+                                       /* do not assemble arrays with unsupported
+                                          configurations */
+                                       dprintf(": Cannot activate member %s.\n",
+                                               iter->text_version);
+                               } else
+                                       count++;
+                       }
+                       sysfs_free(head);
+
+               } else {
+                       dprintf(" no valid super block on device list: err: %d %p\n",
+                               err, st->sb);
+               }
+       } else {
+               dprintf(" no more devices to examin\n");
+       }
+
+       for (tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) {
+               if ((tmpdev->used == 1) && (tmpdev->found)) {
+                       if (count) {
+                               if (count < tmpdev->found)
+                                       count = 0;
+                               else
+                                       count -= tmpdev->found;
+                       }
+               }
+               if (tmpdev->used == 1)
+                       tmpdev->used = 4;
+       }
+       err_1:
+       if (st)
+               st->ss->free_super(st);
+       return count;
+}
+
+
+static int
+count_volumes(char *hba, int dpa, int verbose)
+{
+       struct md_list *devlist = NULL;
+       int count = 0;
+       int found = 0;;
+
+       devlist = get_devices(hba);
+       /* if no intel devices return zero volumes */
+       if (devlist == NULL)
+               return 0;
+
+       count = active_arrays_by_format("imsm", hba, &devlist, dpa, verbose);
+       dprintf(" path: %s active arrays: %d\n", hba, count);
+       if (devlist == NULL)
+               return 0;
+       do  {
+               found = 0;
+               count += count_volumes_list(devlist,
+                                           NULL,
+                                           verbose,
+                                           &found);
+               dprintf("found %d count: %d\n", found, count);
+       } while (found);
+
+       dprintf("path: %s total number of volumes: %d\n", hba, count);
+
+       while(devlist) {
+               struct md_list *dv = devlist;
+               devlist = devlist->next;
+               free(dv->devname);
+               free(dv);
+       }
+       return count;
+}
+
 static int imsm_default_chunk(const struct imsm_orom *orom)
 {
        /* up to 512 if the plaform supports it, otherwise the platform max.
@@ -5583,6 +5977,15 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
 
        *freesize = maxsize;
 
+       if (super->orom) {
+               int count = count_volumes(super->hba->path,
+                                     super->orom->dpa, verbose);
+               if (super->orom->vphba <= count) {
+                       pr_vrb(": platform does not support more then %d raid volumes.\n",
+                              super->orom->vphba);
+                       return 0;
+               }
+       }
        return 1;
 }
 
@@ -5648,8 +6051,15 @@ static int reserve_space(struct supertype *st, int raiddisks,
                        size /= 2 * chunk;
                        size *= 2 * chunk;
                }
+               maxsize = size;
+       }
+       if (!check_env("IMSM_NO_PLATFORM") &&
+           mpb->num_raid_devs > 0 && size && size != maxsize) {
+               fprintf(stderr, Name ": attempting to create a second "
+                       "volume with size less then remaining space. "
+                       "Aborting...\n");
+               return 0;
        }
-
        cnt = 0;
        for (dl = super->disks; dl; dl = dl->next)
                if (dl->e)
@@ -5684,6 +6094,7 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
 
        if (!dev) {
                if (st->sb) {
+                       struct intel_super *super = st->sb;
                        if (!validate_geometry_imsm_orom(st->sb, level, layout,
                                                         raiddisks, chunk,
                                                         verbose))
@@ -5696,6 +6107,19 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
                         * created.  add_to_super and getinfo_super
                         * detect when autolayout is in progress.
                         */
+                       /* assuming that freesize is always given when array is
+                          created */
+                       if (super->orom && freesize) {
+                               int count;
+                               count = count_volumes(super->hba->path,
+                                                     super->orom->dpa, verbose);
+                               if (super->orom->vphba <= count) {
+                                       pr_vrb(": platform does not support more"
+                                              "then %d raid volumes.\n",
+                                              super->orom->vphba);
+                                       return 0;
+                               }
+                       }
                        if (freesize)
                                return reserve_space(st, raiddisks, size,
                                                     chunk?*chunk:0, freesize);
@@ -6516,6 +6940,7 @@ static void imsm_progress_container_reshape(struct intel_super *super)
                map2->num_members = prev_num_members;
 
                imsm_set_array_size(dev);
+               super->clean_migration_record_by_mdmon = 1;
                super->updates_pending++;
        }
 }
@@ -7176,7 +7601,7 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
                }
                return NULL;
        }
-                       
+
        mu->space = NULL;
        mu->space_list = NULL;
        mu->len = sizeof(struct imsm_update_activate_spare) * num_spares;
@@ -7420,7 +7845,7 @@ error_disk_add:
 }
 
 static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
-                                      struct intel_super *super,       
+                                      struct intel_super *super,
                                       struct active_array *active_array)
 {
        struct imsm_super *mpb = super->anchor;
@@ -8359,6 +8784,7 @@ static int imsm_get_allowed_degradation(int level, int raid_disks,
                                        struct imsm_dev *dev)
 {
        switch (level) {
+       case 1:
        case 10:{
                int ret_val = 0;
                struct imsm_map *map;
@@ -8500,7 +8926,8 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev,
 
        migr_rec->dest_depth_per_unit = GEN_MIGR_AREA_SIZE /
                max(map_dest->blocks_per_strip, map_src->blocks_per_strip);
-       migr_rec->dest_depth_per_unit *= map_dest->blocks_per_strip;
+       migr_rec->dest_depth_per_unit *=
+               max(map_dest->blocks_per_strip, map_src->blocks_per_strip);
        new_data_disks = imsm_num_data_members(dev, MAP_0);
        migr_rec->blocks_per_unit =
                __cpu_to_le32(migr_rec->dest_depth_per_unit * new_data_disks);
@@ -9653,6 +10080,18 @@ static int imsm_manage_reshape(
                                "are present in copy area.\n");
                        goto abort;
                }
+               /* Save checkpoint to update migration record for current
+                * reshape position (in md). It can be farther than current
+                * reshape position in metadata.
+                */
+               if (save_checkpoint_imsm(st, sra, UNIT_SRC_NORMAL) == 1) {
+                       /* ignore error == 2, this can mean end of reshape here
+                        */
+                       dprintf("imsm: Cannot write checkpoint to "
+                               "migration record (UNIT_SRC_NORMAL, "
+                               "initial save)\n");
+                       goto abort;
+               }
        }
 
        /* size for data */