From 8592f29d642655a2b337b010dfa435a10b362769 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 4 Dec 2008 16:08:33 +1100 Subject: [PATCH] Create: support autolayout when creating in a DDF If, when creating an array, a signal target device is given which is a container, then allow the metadata handler to choose which devices to use. This is currently only supported for DDF. Signed-off-by: NeilBrown --- Create.c | 86 ++++++++++++++++++---------- mdadm.h | 2 +- super-ddf.c | 156 ++++++++++++++++++++++++++++++++++++++++++++------ super-intel.c | 8 +++ 4 files changed, 205 insertions(+), 47 deletions(-) diff --git a/Create.c b/Create.c index 7ea39c4..422b46d 100644 --- a/Create.c +++ b/Create.c @@ -81,6 +81,7 @@ int Create(struct supertype *st, char *mddev, unsigned long safe_mode_delay = 0; char chosen_name[1024]; struct map_ent *map = NULL; + unsigned long long newsize; int major_num = BITMAP_MAJOR_HI; @@ -147,13 +148,11 @@ int Create(struct supertype *st, char *mddev, else st = NULL; } + if (have_container) + subdevs = raiddisks; } if (fd >= 0) close(fd); - if (have_container) { - subdevs = 0; - devlist = NULL; - } } if (st && st->ss->external && sparedisks) { fprintf(stderr, @@ -238,16 +237,23 @@ int Create(struct supertype *st, char *mddev, fprintf(stderr, Name ": unknown level %d\n", level); return 1; } - + + newsize = size * 2; if (st && ! st->ss->validate_geometry(st, level, layout, raiddisks, - chunk, size*2, NULL, NULL, verbose>=0)) + chunk, size*2, NULL, &newsize, verbose>=0)) return 1; + if (size == 0) { + size = newsize / 2; + if (size && verbose > 0) + fprintf(stderr, Name ": setting size to %lluK\n", + (unsigned long long)size); + } /* now look at the subdevs */ info.array.active_disks = 0; info.array.working_disks = 0; dnum = 0; - for (dv=devlist; dv; dv=dv->next, dnum++) { + for (dv=devlist; dv && !have_container; dv=dv->next, dnum++) { char *dname = dv->devname; unsigned long long freesize; if (strcasecmp(dname, "missing")==0) { @@ -341,6 +347,8 @@ int Create(struct supertype *st, char *mddev, close(fd); } } + if (have_container) + info.array.working_disks = raiddisks; if (fail) { fprintf(stderr, Name ": create aborted\n"); return 1; @@ -366,9 +374,9 @@ int Create(struct supertype *st, char *mddev, fprintf(stderr, Name ": size set to %lluK\n", size); } } - if (level > 0 && ((maxsize-size)*100 > maxsize)) { + if (!have_container && level > 0 && ((maxsize-size)*100 > maxsize)) { if (runstop != 1 || verbose >= 0) - fprintf(stderr, Name ": largest drive (%s) exceed size (%lluK) by more than 1%%\n", + fprintf(stderr, Name ": largest drive (%s) exceeds size (%lluK) by more than 1%%\n", maxdisc, size); warn = 1; } @@ -669,10 +677,15 @@ int Create(struct supertype *st, char *mddev, abort(); if (dnum == insert_point) { moved_disk = dv; - } - if (dnum == insert_point || - strcasecmp(dv->devname, "missing")==0) continue; + } + if (strcasecmp(dv->devname, "missing")==0) + continue; + if (have_container) + moved_disk = NULL; + if (have_container && dnum < info.array.raid_disks - 1) + /* repeatedly use the container */ + moved_disk = dv; switch(pass) { case 1: @@ -689,31 +702,43 @@ int Create(struct supertype *st, char *mddev, if (dv->writemostly == 1) inf->disk.state |= (1<ss->external && st->subarray[0]) - fd = open(dv->devname, O_RDWR); - else - fd = open(dv->devname, O_RDWR|O_EXCL); + if (have_container) + fd = -1; + else { + if (st->ss->external && st->subarray[0]) + fd = open(dv->devname, O_RDWR); + else + fd = open(dv->devname, O_RDWR|O_EXCL); - if (fd < 0) { - fprintf(stderr, Name ": failed to open %s " - "after earlier success - aborting\n", - dv->devname); - goto abort; + if (fd < 0) { + fprintf(stderr, Name ": failed to open %s " + "after earlier success - aborting\n", + dv->devname); + goto abort; + } + fstat(fd, &stb); + inf->disk.major = major(stb.st_rdev); + inf->disk.minor = minor(stb.st_rdev); } - fstat(fd, &stb); - inf->disk.major = major(stb.st_rdev); - inf->disk.minor = minor(stb.st_rdev); - - remove_partitions(fd); + if (fd >= 0) + remove_partitions(fd); if (st->ss->add_to_super(st, &inf->disk, fd, dv->devname)) goto abort; st->ss->getinfo_super(st, inf); safe_mode_delay = inf->safe_mode_delay; - /* getinfo_super might have lost these ... */ - inf->disk.major = major(stb.st_rdev); - inf->disk.minor = minor(stb.st_rdev); + if (have_container && verbose > 0) + fprintf(stderr, Name ": Using %s for device %d\n", + map_dev(inf->disk.major, + inf->disk.minor, + 0), dnum); + + if (!have_container) { + /* getinfo_super might have lost these ... */ + inf->disk.major = major(stb.st_rdev); + inf->disk.minor = minor(stb.st_rdev); + } break; case 2: inf->errors = 0; @@ -731,7 +756,8 @@ int Create(struct supertype *st, char *mddev, } break; } - if (dv == moved_disk && dnum != insert_point) break; + if (!have_container && + dv == moved_disk && dnum != insert_point) break; } if (pass == 1) { st->ss->write_init_super(st); diff --git a/mdadm.h b/mdadm.h index b0966e6..610e1d1 100644 --- a/mdadm.h +++ b/mdadm.h @@ -448,7 +448,7 @@ extern struct superswitch { */ void (*uuid_from_super)(struct supertype *st, int uuid[4]); - /* Extra generic details from metadata. This could be details about + /* Extract generic details from metadata. This could be details about * the container, or about an individual array within the container. * The determination is made either by: * load_super being given a 'component' string. diff --git a/super-ddf.c b/super-ddf.c index 3c97bb6..3b3cac0 100644 --- a/super-ddf.c +++ b/super-ddf.c @@ -423,10 +423,14 @@ struct ddf_super { unsigned long long size; /* sectors */ int pdnum; /* index in ->phys */ struct spare_assign *spare; + void *mdupdate; /* hold metadata update */ + + /* These fields used by auto-layout */ + int raiddisk; /* slot to fill in autolayout */ + __u64 esize; }; }; struct disk_data disk; - void *mdupdate; /* hold metadata update */ struct vcl *vlist[0]; /* max_part in size */ } *dlist, *add_list; }; @@ -1325,6 +1329,7 @@ static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info) struct vcl *vc = ddf->currentconf; int cd = ddf->currentdev; int j; + struct dl *dl; /* FIXME this returns BVD info - what if we want SVD ?? */ @@ -1346,8 +1351,15 @@ static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info) info->component_size = __be64_to_cpu(vc->conf.blocks); } + for (dl = ddf->dlist; dl ; dl = dl->next) + if (dl->raiddisk == info->disk.raid_disk) + break; info->disk.major = 0; info->disk.minor = 0; + if (dl) { + info->disk.major = dl->major; + info->disk.minor = dl->minor; + } // info->disk.number = __be32_to_cpu(ddf->disk.refnum); // info->disk.raid_disk = find refnum in the table and use index; // info->disk.state = ???; @@ -1965,6 +1977,9 @@ static void add_to_super_ddf_bvd(struct supertype *st, * the phys_refnum and lba_offset for the newly created vd_config. * We might also want to update the type in the phys_disk * section. + * + * Alternately: fd == -1 and we have already chosen which device to + * use and recorded in dlist->raid_disk; */ struct dl *dl; struct ddf_super *ddf = st->sb; @@ -1975,10 +1990,16 @@ static void add_to_super_ddf_bvd(struct supertype *st, unsigned long long blocks, pos, esize; struct extent *ex; - for (dl = ddf->dlist; dl ; dl = dl->next) - if (dl->major == dk->major && - dl->minor == dk->minor) - break; + if (fd == -1) { + for (dl = ddf->dlist; dl ; dl = dl->next) + if (dl->raiddisk == dk->raid_disk) + break; + } else { + for (dl = ddf->dlist; dl ; dl = dl->next) + if (dl->major == dk->major && + dl->minor == dk->minor) + break; + } if (!dl || ! (dk->state & (1<vlist[i] = ddf->currentconf; - dl->fd = fd; - dl->devname = devname; + if (fd >= 0) + dl->fd = fd; + if (devname) + dl->devname = devname; /* Check how many working raid_disks, and if we can mark * array as optimal yet @@ -2331,6 +2354,96 @@ static __u64 avail_size_ddf(struct supertype *st, __u64 devsize) } #ifndef MDASSEMBLE + +static int reserve_space(struct supertype *st, int raiddisks, + unsigned long long size, int chunk, + unsigned long long *freesize) +{ + /* Find 'raiddisks' spare extents at least 'size' big (but + * only caring about multiples of 'chunk') and remember + * them. + * If the cannot be found, fail. + */ + struct dl *dl; + struct ddf_super *ddf = st->sb; + int cnt = 0; + + for (dl = ddf->dlist; dl ; dl=dl->next) { + dl->raiddisk = -1; + dl->esize = 0; + } + /* Now find largest extent on each device */ + for (dl = ddf->dlist ; dl ; dl=dl->next) { + struct extent *e = get_extents(ddf, dl); + unsigned long long pos = 0; + int i = 0; + int found = 0; + unsigned long long minsize = size; + + if (size == 0) + minsize = chunk; + + if (!e) + continue; + do { + unsigned long long esize; + esize = e[i].start - pos; + if (esize >= minsize) { + found = 1; + minsize = esize; + } + pos = e[i].start + e[i].size; + i++; + } while (e[i-1].size); + if (found) { + cnt++; + dl->esize = minsize; + } + free(e); + } + if (cnt < raiddisks) { + fprintf(stderr, Name ": not enough devices with space to create array.\n"); + return 0; /* No enough free spaces large enough */ + } + if (size == 0) { + /* choose the largest size of which there are at least 'raiddisk' */ + for (dl = ddf->dlist ; dl ; dl=dl->next) { + struct dl *dl2; + if (dl->esize <= size) + continue; + /* This is bigger than 'size', see if there are enough */ + cnt = 0; + for (dl2 = dl; dl2 ; dl2=dl2->next) + if (dl2->esize >= dl->esize) + cnt++; + if (cnt >= raiddisks) + size = dl->esize; + } + if (chunk) { + size = size / chunk; + size *= chunk; + } + *freesize = size; + if (size < 32) { + fprintf(stderr, Name ": not enough spare devices to create array.\n"); + return 0; + } + } + /* We have a 'size' of which there are enough spaces. + * We simply do a first-fit */ + cnt = 0; + for (dl = ddf->dlist ; dl && cnt < raiddisks ; dl=dl->next) { + if (dl->esize < size) + continue; + + dl->raiddisk = cnt; + cnt++; + } + return 1; +} + + + static int validate_geometry_ddf_container(struct supertype *st, int level, int layout, int raiddisks, @@ -2369,15 +2482,6 @@ static int validate_geometry_ddf(struct supertype *st, verbose); } - if (st->sb) { - /* A container has already been opened, so we are - * creating in there. Maybe a BVD, maybe an SVD. - * Should make a distinction one day. - */ - return validate_geometry_ddf_bvd(st, level, layout, raiddisks, - chunk, size, dev, freesize, - verbose); - } if (!dev) { /* Initial sanity check. Exclude illegal levels. */ int i; @@ -2387,9 +2491,29 @@ static int validate_geometry_ddf(struct supertype *st, if (ddf_level_num[i].num1 == MAXINT) return 0; /* Should check layout? etc */ + + if (st->sb && freesize) { + /* --create was given a container to create in. + * So we need to check that there are enough + * free spaces and return the amount of space. + * We may as well remember which drives were + * chosen so that add_to_super/getinfo_super + * can return them. + */ + return reserve_space(st, raiddisks, size, chunk, freesize); + } return 1; } + if (st->sb) { + /* A container has already been opened, so we are + * creating in there. Maybe a BVD, maybe an SVD. + * Should make a distinction one day. + */ + return validate_geometry_ddf_bvd(st, level, layout, raiddisks, + chunk, size, dev, freesize, + verbose); + } /* This is the first device for the array. * If it is a container, we read it in and do automagic allocations, * no other devices should be given. diff --git a/super-intel.c b/super-intel.c index f78957f..b908375 100644 --- a/super-intel.c +++ b/super-intel.c @@ -2387,6 +2387,14 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout, verbose); } + if (!dev) { + if (st->sb && freesize) { + /* Should do auto-layout here */ + fprintf(stderr, Name ": IMSM does not support auto-layout yet\n"); + return 0; + } + return 1; + } if (st->sb) { /* creating in a given container */ return validate_geometry_imsm_volume(st, level, layout,