+ /* 'generation' is incremented everytime the metadata is written */
+ generation = __le32_to_cpu(mpb->generation_num);
+ generation++;
+ mpb->generation_num = __cpu_to_le32(generation);
+
+ /* recalculate checksum */
+ sum = gen_imsm_checksum(mpb);
+ mpb->check_sum = __cpu_to_le32(sum);
+
+ for (d = super->disks; d ; d = d->next) {
+ if (store_imsm_mpb(d->fd, super)) {
+ fprintf(stderr, "%s: failed for device %d:%d %s\n",
+ __func__, d->major, d->minor, strerror(errno));
+ return 0;
+ }
+ if (doclose) {
+ close(d->fd);
+ d->fd = -1;
+ }
+ }
+
+ return 1;
+}
+
+static int write_init_super_imsm(struct supertype *st)
+{
+ if (st->update_tail) {
+ /* queue the recently created array as a metadata update */
+ size_t len;
+ struct imsm_update_create_array *u;
+ struct intel_super *super = st->sb;
+ struct imsm_super *mpb = super->mpb;
+ struct imsm_dev *dev;
+ struct imsm_map *map;
+ struct dl *d;
+
+ if (super->current_vol < 0 ||
+ !(dev = get_imsm_dev(mpb, super->current_vol))) {
+ fprintf(stderr, "%s: could not determine sub-array\n",
+ __func__);
+ return 1;
+ }
+
+
+ map = &dev->vol.map[0];
+ len = sizeof(*u) + sizeof(__u32) * (map->num_members - 1);
+ u = malloc(len);
+ if (!u) {
+ fprintf(stderr, "%s: failed to allocate update buffer\n",
+ __func__);
+ return 1;
+ }
+
+ u->type = update_create_array;
+ u->dev_idx = super->current_vol;
+ memcpy(&u->dev, dev, sizeof(*dev));
+ memcpy(u->dev.vol.map[0].disk_ord_tbl, map->disk_ord_tbl,
+ sizeof(__u32) * map->num_members);
+ append_metadata_update(st, u, len);
+
+ for (d = super->disks; d ; d = d->next) {
+ close(d->fd);
+ d->fd = -1;
+ }
+
+ return 0;
+ } else
+ return write_super_imsm(st->sb, 1);
+}
+
+static int store_zero_imsm(struct supertype *st, int fd)
+{
+ unsigned long long dsize;
+ void *buf;
+
+ get_dev_size(fd, NULL, &dsize);
+
+ /* first block is stored on second to last sector of the disk */
+ if (lseek64(fd, dsize - (512 * 2), SEEK_SET) < 0)
+ return 1;
+
+ if (posix_memalign(&buf, 512, 512) != 0)
+ return 1;
+
+ memset(buf, 0, sizeof(buf));
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf))
+ return 1;
+ return 0;
+}
+
+static int validate_geometry_imsm_container(struct supertype *st, int level,
+ int layout, int raiddisks, int chunk,
+ unsigned long long size, char *dev,
+ unsigned long long *freesize,
+ int verbose)
+{
+ int fd;
+ unsigned long long ldsize;
+
+ if (level != LEVEL_CONTAINER)
+ return 0;
+ if (!dev)
+ return 1;
+
+ fd = open(dev, O_RDONLY|O_EXCL, 0);
+ if (fd < 0) {
+ if (verbose)
+ fprintf(stderr, Name ": imsm: Cannot open %s: %s\n",
+ dev, strerror(errno));
+ return 0;
+ }
+ if (!get_dev_size(fd, dev, &ldsize)) {
+ close(fd);
+ return 0;
+ }
+ close(fd);
+
+ *freesize = avail_size_imsm(st, ldsize >> 9);
+
+ return 1;
+}
+
+/* validate_geometry_imsm_volume - lifted from validate_geometry_ddf_bvd
+ * FIX ME add ahci details
+ */
+static int validate_geometry_imsm_volume(struct supertype *st, int level,
+ int layout, int raiddisks, int chunk,
+ unsigned long long size, char *dev,
+ unsigned long long *freesize,
+ int verbose)
+{
+ struct stat stb;
+ struct intel_super *super = st->sb;
+ struct dl *dl;
+ unsigned long long pos = 0;
+ unsigned long long maxsize;
+ struct extent *e;
+ int i;
+
+ if (level == LEVEL_CONTAINER)
+ return 0;
+
+ if (level == 1 && raiddisks > 2) {
+ if (verbose)
+ fprintf(stderr, Name ": imsm does not support more "
+ "than 2 in a raid1 configuration\n");
+ return 0;
+ }
+
+ /* We must have the container info already read in. */
+ if (!super)
+ return 0;
+
+ if (!dev) {
+ /* General test: make sure there is space for
+ * 'raiddisks' device extents of size 'size' at a given
+ * offset
+ */
+ unsigned long long minsize = size*2 /* convert to blocks */;
+ unsigned long long start_offset = ~0ULL;
+ int dcnt = 0;
+ if (minsize == 0)
+ minsize = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS;
+ for (dl = super->disks; dl ; dl = dl->next) {
+ int found = 0;
+
+ pos = 0;
+ i = 0;
+ e = get_extents(super, dl);
+ if (!e) continue;
+ do {
+ unsigned long long esize;
+ esize = e[i].start - pos;
+ if (esize >= minsize)
+ found = 1;
+ if (found && start_offset == ~0ULL) {
+ start_offset = pos;
+ break;
+ } else if (found && pos != start_offset) {
+ found = 0;
+ break;
+ }
+ pos = e[i].start + e[i].size;
+ i++;
+ } while (e[i-1].size);
+ if (found)
+ dcnt++;
+ free(e);
+ }
+ if (dcnt < raiddisks) {
+ if (verbose)
+ fprintf(stderr, Name ": imsm: Not enough "
+ "devices with space for this array "
+ "(%d < %d)\n",
+ dcnt, raiddisks);
+ return 0;
+ }
+ return 1;
+ }
+ /* This device must be a member of the set */
+ if (stat(dev, &stb) < 0)
+ return 0;
+ if ((S_IFMT & stb.st_mode) != S_IFBLK)
+ return 0;
+ for (dl = super->disks ; dl ; dl = dl->next) {
+ if (dl->major == major(stb.st_rdev) &&
+ dl->minor == minor(stb.st_rdev))
+ break;
+ }
+ if (!dl) {
+ if (verbose)
+ fprintf(stderr, Name ": %s is not in the "
+ "same imsm set\n", dev);
+ return 0;
+ }
+ e = get_extents(super, dl);
+ maxsize = 0;
+ i = 0;
+ if (e) do {
+ unsigned long long esize;
+ esize = e[i].start - pos;
+ if (esize >= maxsize)
+ maxsize = esize;
+ pos = e[i].start + e[i].size;
+ i++;
+ } while (e[i-1].size);
+ *freesize = maxsize;
+
+ return 1;
+}
+
+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,
+ int verbose)
+{
+ int fd, cfd;
+ struct mdinfo *sra;
+
+ /* if given unused devices create a container
+ * if given given devices in a container create a member volume
+ */
+ if (level == LEVEL_CONTAINER) {
+ /* Must be a fresh device to add to a container */
+ return validate_geometry_imsm_container(st, level, layout,
+ raiddisks, chunk, size,
+ dev, freesize,
+ verbose);
+ }
+
+ if (st->sb) {
+ /* creating in a given container */
+ return validate_geometry_imsm_volume(st, level, layout,
+ raiddisks, chunk, size,
+ dev, freesize, verbose);
+ }
+
+ /* limit creation to the following levels */
+ if (!dev)
+ switch (level) {
+ case 0:
+ case 1:
+ case 10:
+ case 5:
+ break;
+ default:
+ return 1;
+ }
+
+ /* This device needs to be a device in an 'imsm' container */
+ fd = open(dev, O_RDONLY|O_EXCL, 0);
+ if (fd >= 0) {
+ if (verbose)
+ fprintf(stderr,
+ Name ": Cannot create this array on device %s\n",
+ dev);
+ close(fd);
+ return 0;
+ }
+ if (errno != EBUSY || (fd = open(dev, O_RDONLY, 0)) < 0) {
+ if (verbose)
+ fprintf(stderr, Name ": Cannot open %s: %s\n",
+ dev, strerror(errno));
+ return 0;
+ }
+ /* Well, it is in use by someone, maybe an 'imsm' container. */
+ cfd = open_container(fd);
+ if (cfd < 0) {
+ close(fd);
+ if (verbose)
+ fprintf(stderr, Name ": Cannot use %s: It is busy\n",
+ dev);
+ return 0;
+ }
+ sra = sysfs_read(cfd, 0, GET_VERSION);
+ close(fd);
+ if (sra && sra->array.major_version == -1 &&
+ strcmp(sra->text_version, "imsm") == 0) {
+ /* This is a member of a imsm container. Load the container
+ * and try to create a volume
+ */
+ struct intel_super *super;
+
+ if (load_super_imsm_all(st, cfd, (void **) &super, NULL, 1) == 0) {
+ st->sb = super;
+ st->container_dev = fd2devnum(cfd);
+ close(cfd);
+ return validate_geometry_imsm_volume(st, level, layout,
+ raiddisks, chunk,
+ size, dev,
+ freesize, verbose);
+ }
+ close(cfd);
+ } else /* may belong to another container */
+ return 0;
+
+ return 1;
+}
+
+static struct mdinfo *container_content_imsm(struct supertype *st)
+{
+ /* Given a container loaded by load_super_imsm_all,
+ * extract information about all the arrays into
+ * an mdinfo tree.
+ *
+ * For each imsm_dev create an mdinfo, fill it in,
+ * then look for matching devices in super->disks
+ * and create appropriate device mdinfo.
+ */
+ struct intel_super *super = st->sb;
+ struct imsm_super *mpb = super->mpb;
+ struct mdinfo *rest = NULL;
+ int i;
+
+ for (i = 0; i < mpb->num_raid_devs; i++) {