int second_missing = subdevs * 2;
int missing_disks = 0;
int insert_point = subdevs * 2; /* where to insert a missing drive */
+ int total_slots;
int pass;
int vers;
int rv;
int bitmap_fd;
+ int have_container = 0;
+ int container_fd = -1;
+ int need_mdmon = 0;
unsigned long long bitmapsize;
- struct mdinfo *sra;
- struct mdinfo info;
+ struct mdinfo info, *infos;
+ int did_default = 0;
+ unsigned long safe_mode_delay = 0;
int major_num = BITMAP_MAJOR_HI;
}
}
if (level == UnSet) {
- /* "ddf" metadata only supports one level - should possibly
+ /* "ddf" and "imsm" metadata only supports one level - should possibly
* push this into metadata handler??
*/
- if (st && st->ss == &super_ddf)
+ if (st && (st->ss == &super_ddf || st->ss == &super_imsm))
level = LEVEL_CONTAINER;
}
Name ": This level does not support spare devices\n");
return 1;
}
+
+ if (subdevs == 1 && strcmp(devlist->devname, "missing") != 0) {
+ /* If given a single device, it might be a container, and we can
+ * extract a device list from there
+ */
+ mdu_array_info_t inf;
+ int fd;
+
+ memset(&inf, 0, sizeof(inf));
+ fd = open(devlist->devname, O_RDONLY);
+ if (fd >= 0 &&
+ ioctl(fd, GET_ARRAY_INFO, &inf) == 0 &&
+ inf.raid_disks == 0) {
+ /* yep, looks like a container */
+ if (st) {
+ rv = st->ss->load_super(st, fd,
+ devlist->devname);
+ if (rv == 0)
+ have_container = 1;
+ } else {
+ st = guess_super(fd);
+ if (st && !(rv = st->ss->
+ load_super(st, fd,
+ devlist->devname)))
+ have_container = 1;
+ else
+ st = NULL;
+ }
+ }
+ if (fd >= 0)
+ close(fd);
+ if (have_container) {
+ subdevs = 0;
+ devlist = NULL;
+ }
+ }
+ if (st && st->ss->external && sparedisks) {
+ fprintf(stderr,
+ Name ": This metadata type does not support "
+ "spare disks are create time\n");
+ return 1;
+ }
if (subdevs > raiddisks+sparedisks) {
fprintf(stderr, Name ": You have listed more devices (%d) than are in the array(%d)!\n", subdevs, raiddisks+sparedisks);
return 1;
}
- if (subdevs < raiddisks+sparedisks) {
+ if (!have_container && subdevs < raiddisks+sparedisks) {
fprintf(stderr, Name ": You haven't given enough devices (real or missing) to create this array\n");
return 1;
}
}
if (st && ! st->ss->validate_geometry(st, level, layout, raiddisks,
- chunk, size, NULL, NULL))
+ chunk, size, NULL, NULL, verbose>=0))
return 1;
/* now look at the subdevs */
st = superlist[i]->match_metadata_desc(name);
if (st && !st->ss->validate_geometry
(st, level, layout, raiddisks,
- chunk, size, dname, &freesize))
+ chunk, size, dname, &freesize,
+ verbose > 0))
st = NULL;
}
dname);
exit(2);
}
- if (st->ss->major != 0 ||
+ if (st->ss != &super0 ||
st->minor_version != 90)
- fprintf(stderr, Name ": Defaulting to version"
- " %d.%d metadata\n",
- st->ss->major,
- st->minor_version);
+ did_default = 1;
} else {
if (!st->ss->validate_geometry(st, level, layout,
raiddisks,
chunk, size, dname,
- &freesize)) {
+ &freesize,
+ verbose > 0)) {
fprintf(stderr,
Name ": %s is not suitable for "
minsize = freesize;
}
if (runstop != 1 || verbose >= 0) {
- int fd = open(dname, O_RDONLY, 0);
+ int fd = open(dname, O_RDONLY);
if (fd <0 ) {
fprintf(stderr, Name ": Cannot open %s: %s\n",
dname, strerror(errno));
return 1;
}
if (size == 0) {
- if (mindisc == NULL) {
+ if (mindisc == NULL && !have_container) {
fprintf(stderr, Name ": no size and no drives given - aborting create.\n");
return 1;
}
- if (level > 0 || level == LEVEL_MULTIPATH || level == LEVEL_FAULTY) {
+ if (level > 0 || level == LEVEL_MULTIPATH
+ || level == LEVEL_FAULTY
+ || st->ss->external ) {
/* size is meaningful */
- if (minsize > 0x100000000ULL && st->ss->major == 0) {
+ if (!st->ss->validate_geometry(st, level, layout,
+ raiddisks,
+ chunk, minsize,
+ NULL, NULL, 0)) {
fprintf(stderr, Name ": devices too large for RAID level %d\n", level);
return 1;
}
* into a spare, else the create will fail
*/
if (assume_clean == 0 && force == 0 && first_missing < raiddisks &&
+ st->ss->external == 0 &&
second_missing >= raiddisks && level == 6) {
insert_point = raiddisks - 1;
if (insert_point == first_missing)
missing_disks++;
}
- if (level <= 0 && first_missing != subdevs * 2) {
+ if (level <= 0 && first_missing < subdevs * 2) {
fprintf(stderr,
Name ": This level does not support missing devices\n");
return 1;
( level == 6 && (insert_point < raiddisks
|| second_missing < raiddisks))
||
+ ( level <= 0 )
+ ||
assume_clean
- )
+ ) {
info.array.state = 1; /* clean, but one+ drive will be missing*/
- else
+ info.resync_start = ~0ULL;
+ } else {
info.array.state = 0; /* not clean, but no errors */
-
+ info.resync_start = 0;
+ }
if (level == 10) {
/* for raid10, the bitmap size is the capacity of the array,
* which is array.size * raid_disks / ncopies;
+ info.array.failed_disks;
info.array.layout = layout;
info.array.chunk_size = chunk*1024;
- info.array.major_version = st->ss->major;
if (name == NULL || *name == 0) {
/* base name on mddev */
if (!st->ss->init_super(st, &info.array, size, name, homehost, uuid))
return 1;
+ total_slots = info.array.nr_disks;
+ sysfs_init(&info, mdfd, 0);
+ st->ss->getinfo_super(st, &info);
+
+ if (did_default && verbose >= 0) {
+ if (is_subarray(info.text_version)) {
+ int dnum = devname2devnum(info.text_version+1);
+ char *path;
+ int mdp = get_mdp_major();
+ struct mdinfo *mdi;
+ if (dnum > 0)
+ path = map_dev(MD_MAJOR, dnum, 1);
+ else
+ path = map_dev(mdp, (-1-dnum)<< 6, 1);
+
+ mdi = sysfs_read(-1, dnum, GET_VERSION);
+
+ fprintf(stderr, Name ": Creating array inside "
+ "%s container %s\n",
+ mdi?mdi->text_version:"managed", path);
+ sysfs_free(mdi);
+ } else
+ fprintf(stderr, Name ": Defaulting to version"
+ " %s metadata\n", info.text_version);
+ }
+
if (bitmap_file && vers < 9003) {
major_num = BITMAP_MAJOR_HOSTENDIAN;
#ifdef __BIG_ENDIAN
}
- sra = sysfs_read(mdfd, 0, 0);
+ sysfs_init(&info, mdfd, 0);
+
+ if (st->ss->external && st->subarray[0]) {
+ /* member */
- if (st->ss->external) {
- char ver[100];
- strcat(strcpy(ver, "external:"), st->ss->text_version);
- if ((vers % 100) < 2 ||
- sra == NULL ||
- sysfs_set_str(sra, NULL, "metadata_version",
- ver) < 0) {
- fprintf(stderr, Name ": This kernel does not "
- "support external metadata.\n");
+ /* When creating a member, we need to be careful
+ * to negotiate with mdmon properly.
+ * If it is already running, we cannot write to
+ * the devices and must ask it to do that part.
+ * If it isn't running, we write to the devices,
+ * and then start it.
+ * We hold an exclusive open on the container
+ * device to make sure mdmon doesn't exit after
+ * we checked that it is running.
+ *
+ * For now, fail if it is already running.
+ */
+ container_fd = open_dev_excl(st->container_dev);
+ if (container_fd < 0) {
+ fprintf(stderr, Name ": Cannot get exclusive "
+ "open on container - weird.\n");
return 1;
}
- rv = 0;
- } else if ((vers % 100) >= 1) { /* can use different versions */
- mdu_array_info_t inf;
- memset(&inf, 0, sizeof(inf));
- inf.major_version = st->ss->major;
- inf.minor_version = st->minor_version;
- rv = ioctl(mdfd, SET_ARRAY_INFO, &inf);
- } else
- rv = ioctl(mdfd, SET_ARRAY_INFO, NULL);
+ if (mdmon_running(st->container_dev)) {
+ if (verbose)
+ fprintf(stderr, Name ": reusing mdmon "
+ "for %s.\n",
+ devnum2devname(st->container_dev));
+ st->update_tail = &st->updates;
+ } else
+ need_mdmon = 1;
+ }
+ rv = set_array_info(mdfd, st, &info);
if (rv) {
- fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n",
+ fprintf(stderr, Name ": failed to set array info for %s: %s\n",
mddev, strerror(errno));
return 1;
}
}
}
+ infos = malloc(sizeof(*infos) * total_slots);
for (pass=1; pass <=2 ; pass++) {
mddev_dev_t moved_disk = NULL; /* the disk that was moved out of the insert point */
dv=(dv->next)?(dv->next):moved_disk, dnum++) {
int fd;
struct stat stb;
+ struct mdinfo *inf = &infos[dnum];
- info.disk.number = dnum;
+ if (dnum >= total_slots)
+ abort();
if (dnum == insert_point) {
moved_disk = dv;
}
- info.disk.raid_disk = info.disk.number;
- if (info.disk.raid_disk < raiddisks)
- info.disk.state = (1<<MD_DISK_ACTIVE) |
- (1<<MD_DISK_SYNC);
- else
- info.disk.state = 0;
- if (dv->writemostly)
- info.disk.state |= (1<<MD_DISK_WRITEMOSTLY);
-
if (dnum == insert_point ||
strcasecmp(dv->devname, "missing")==0)
continue;
- fd = open(dv->devname, O_RDWR|O_EXCL, 0);
- if (fd < 0) {
- fprintf(stderr, Name ": failed to open %s "
- "after earlier success - aborting\n",
- dv->devname);
- return 1;
- }
- fstat(fd, &stb);
- info.disk.major = major(stb.st_rdev);
- info.disk.minor = minor(stb.st_rdev);
-
- switch(pass){
+ switch(pass) {
case 1:
+ *inf = info;
+
+ inf->disk.number = dnum;
+ inf->disk.raid_disk = dnum;
+ if (inf->disk.raid_disk < raiddisks)
+ inf->disk.state = (1<<MD_DISK_ACTIVE) |
+ (1<<MD_DISK_SYNC);
+ else
+ inf->disk.state = 0;
+
+ if (dv->writemostly == 1)
+ inf->disk.state |= (1<<MD_DISK_WRITEMOSTLY);
+
+ 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);
+ return 1;
+ }
+ fstat(fd, &stb);
+ inf->disk.major = major(stb.st_rdev);
+ inf->disk.minor = minor(stb.st_rdev);
+
remove_partitions(fd);
- st->ss->add_to_super(st, &info.disk,
+ st->ss->add_to_super(st, &inf->disk,
fd, dv->devname);
+ 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);
break;
case 2:
- close(fd);
-
- if (st->ss->external) {
- char dv[100];
- sprintf(dv, "%d:%d\n",
- info.disk.major,
- info.disk.minor);
- sysfs_set_str(sra, NULL, "new_dev", dv);
- /* FIXME check error */
- /*FIXME find that device and set it up*/
- } else if (ioctl(mdfd, ADD_NEW_DISK,
- &info.disk)) {
+ inf->errors = 0;
+ rv = 0;
+
+ rv = add_disk(mdfd, st, &info, inf);
+
+ if (rv) {
fprintf(stderr,
Name ": ADD_NEW_DISK for %s "
"failed: %s\n",
}
if (dv == moved_disk && dnum != insert_point) break;
}
- if (pass == 1)
+ if (pass == 1) {
st->ss->write_init_super(st);
+ flush_metadata_updates(st);
+ }
}
+ free(infos);
st->ss->free_super(st);
/* param is not actually used */
/* No need to start */
;
else if (runstop == 1 || subdevs >= raiddisks) {
- mdu_param_t param;
- if (ioctl(mdfd, RUN_ARRAY, ¶m)) {
- fprintf(stderr, Name ": RUN_ARRAY failed: %s\n",
- strerror(errno));
- Manage_runstop(mddev, mdfd, -1, 0);
- return 1;
+ if (st->ss->external) {
+ switch(level) {
+ case LEVEL_LINEAR:
+ case LEVEL_MULTIPATH:
+ case 0:
+ sysfs_set_str(&info, NULL, "array_state",
+ "active");
+ need_mdmon = 0;
+ break;
+ default:
+ sysfs_set_str(&info, NULL, "array_state",
+ "readonly");
+ break;
+ }
+ sysfs_set_safemode(&info, safe_mode_delay);
+ } else {
+ mdu_param_t param;
+ if (ioctl(mdfd, RUN_ARRAY, ¶m)) {
+ fprintf(stderr, Name ": RUN_ARRAY failed: %s\n",
+ strerror(errno));
+ Manage_runstop(mddev, mdfd, -1, 0);
+ return 1;
+ }
}
if (verbose >= 0)
fprintf(stderr, Name ": array %s started.\n", mddev);
+ if (st->ss->external && st->subarray[0]) {
+ if (need_mdmon)
+ start_mdmon(st->container_dev);
+
+ ping_monitor(devnum2devname(st->container_dev));
+ close(container_fd);
+ }
} else {
fprintf(stderr, Name ": not starting array - not enough devices.\n");
}