#include "mdadm.h"
#include <ctype.h>
+mapping_t assemble_statuses[] = {
+ { "but cannot be started", INCR_NO },
+ { "but not safe to start", INCR_UNSAFE },
+ { "and started", INCR_YES },
+ { NULL, INCR_ALREADY }
+};
+
+
+/**
+ * struct assembly_array_info - General, meaningful information for assembly.
+ * @name: Array name.
+ * @new_cnt: Count of drives known to be members, recently added.
+ * @preexist_cnt: Count of member drives in pre-assembled array.
+ * @exp_cnt: Count of known expansion targets.
+ *
+ * FIXME: @exp_new_cnt for recently added expansion targets.
+ */
+struct assembly_array_info {
+ char *name;
+ int new_cnt;
+ int preexist_cnt;
+ int exp_cnt;
+};
+
+/**
+ * set_array_assembly_status() - generate status of assembly for an array.
+ * @c: Global settings.
+ * @result: Pointer to status mask.
+ * @status: Status to be set/printed.
+ * @arr: Array information.
+ *
+ * Print status message to user or set it in @result if it is not NULL.
+ */
+static void set_array_assembly_status(struct context *c,
+ int *result, int status,
+ struct assembly_array_info *arr)
+{
+ int raid_disks = arr->preexist_cnt + arr->new_cnt;
+ char *status_msg = map_num_s(assemble_statuses, status);
+
+ if (c->export && result)
+ *result |= status;
+
+ if (c->export || c->verbose < 0)
+ return;
+
+ pr_err("%s has been assembled with %d device%s", arr->name,
+ raid_disks, raid_disks == 1 ? "":"s");
+ if (arr->preexist_cnt > 0)
+ fprintf(stderr, " (%d new)", arr->new_cnt);
+ if (arr->exp_cnt)
+ fprintf(stderr, " ( + %d for expansion)", arr->exp_cnt);
+ fprintf(stderr, " %s.\n", status_msg);
+}
+
static int name_matches(char *found, char *required, char *homehost, int require_homehost)
{
/* See if the name found matches the required name, possibly
struct mdinfo *content,
struct supertype *tst,
char *homehost, int require_homehost,
- char *update, char *devname)
+ enum update_opt update, char *devname)
{
- if (ident->uuid_set && (!update || strcmp(update, "uuid")!= 0) &&
+ if (ident->uuid_set && update != UOPT_UUID &&
same_uuid(content->uuid, ident->uuid, tst->ss->swapuuid)==0 &&
memcmp(content->uuid, uuid_zero, sizeof(int[4])) != 0) {
if (devname)
pr_err("%s has wrong uuid.\n", devname);
return 0;
}
- if (ident->name[0] && (!update || strcmp(update, "name")!= 0) &&
+ if (ident->name[0] && update != UOPT_NAME &&
name_matches(content->name, ident->name, homehost, require_homehost)==0) {
if (devname)
pr_err("%s has wrong name.\n", devname);
if (auto_assem || !inargv)
/* Ignore unrecognised devices during auto-assembly */
goto loop;
- if (ident->uuid_set || ident->name[0] ||
+ if (ident->name[0] ||
ident->super_minor != UnSet)
/* Ignore unrecognised device if looking for
* specific array */
goto loop;
+ if (ident->uuid_set)
+ /* ignore unrecognized device if looking for
+ * specific uuid
+ */
+ goto loop;
pr_err("%s has no superblock - assembly aborted\n",
devname);
st->ss->free_super(st);
dev_policy_free(pol);
domain_free(domains);
- if (tst)
+ if (tst) {
tst->ss->free_super(tst);
+ free(tst);
+ }
return -1;
}
st->ss->free_super(st);
dev_policy_free(pol);
domain_free(domains);
+ free(st);
return -1;
}
if (c->verbose > 0)
if (st->ss != tst->ss ||
st->minor_version != tst->minor_version ||
- st->ss->compare_super(st, tst) != 0) {
+ st->ss->compare_super(st, tst, 1) != 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
st->ss->free_super(st);
dev_policy_free(pol);
domain_free(domains);
+ free(tst);
return -1;
}
tmpdev->used = 1;
}
dev_policy_free(pol);
pol = NULL;
- if (tst)
+ if (tst) {
tst->ss->free_super(tst);
+ free(tst);
+ }
}
/* Check if we found some imsm spares but no members */
if (dfd < 0 ||
st->ss->load_super(st, dfd, NULL))
tmpdev->used = 2;
- if (dfd > 0)
- close(dfd);
+ close_fd(&dfd);
}
}
struct supertype *tst;
int i;
int dfd;
+ int disk_state;
if (tmpdev->used != 1)
continue;
int err;
fstat(mdfd, &stb2);
- if (strcmp(c->update, "uuid") == 0 && !ident->uuid_set)
+ if (c->update == UOPT_UUID && !ident->uuid_set)
random_uuid((__u8 *)ident->uuid);
- if (strcmp(c->update, "ppl") == 0 &&
- ident->bitmap_fd >= 0) {
+ if (c->update == UOPT_PPL && ident->bitmap_fd >= 0) {
pr_err("PPL is not compatible with bitmap\n");
+ close(mdfd);
+ free(devices);
+ free(devmap);
return -1;
}
strcpy(content->name, ident->name);
content->array.md_minor = minor(stb2.st_rdev);
- if (strcmp(c->update, "byteorder") == 0)
+ if (c->update == UOPT_BYTEORDER)
err = 0;
- else if (strcmp(c->update, "home-cluster") == 0) {
+ else if (c->update == UOPT_HOME_CLUSTER) {
tst->cluster_name = c->homecluster;
err = tst->ss->write_bitmap(tst, dfd, NameUpdate);
- } else if (strcmp(c->update, "nodes") == 0) {
+ } else if (c->update == UOPT_NODES) {
tst->nodes = c->nodes;
err = tst->ss->write_bitmap(tst, dfd, NodeNumUpdate);
- } else if (strcmp(c->update, "revert-reshape") == 0 &&
- c->invalid_backup)
+ } else if (c->update == UOPT_REVERT_RESHAPE && c->invalid_backup)
err = tst->ss->update_super(tst, content,
- "revert-reshape-nobackup",
+ UOPT_SPEC_REVERT_RESHAPE_NOBACKUP,
devname, c->verbose,
ident->uuid_set,
c->homehost);
else
- err = tst->ss->update_super(tst, content, c->update,
+ err = tst->ss->update_super(tst, content,
+ c->update,
devname, c->verbose,
ident->uuid_set,
c->homehost);
if (err < 0) {
if (err == -1)
pr_err("--update=%s not understood for %s metadata\n",
- c->update, tst->ss->name);
+ map_num(update_options, c->update), tst->ss->name);
tst->ss->free_super(tst);
free(tst);
close(mdfd);
*stp = st;
return -1;
}
- if (strcmp(c->update, "uuid")==0 &&
+ if (c->update == UOPT_UUID &&
!ident->uuid_set) {
ident->uuid_set = 1;
memcpy(ident->uuid, content->uuid, 16);
pr_err("Could not re-write superblock on %s.\n",
devname);
- if (strcmp(c->update, "uuid")==0 &&
+ if (c->update == UOPT_UUID &&
ident->bitmap_fd >= 0 && !bitmap_done) {
if (bitmap_update_uuid(ident->bitmap_fd,
content->uuid,
devices[devcnt].i.disk.major = major(stb.st_rdev);
devices[devcnt].i.disk.minor = minor(stb.st_rdev);
- if (devices[devcnt].i.disk.state == 6) {
+ disk_state = devices[devcnt].i.disk.state & ~((1<<MD_DISK_FAILFAST) |
+ (1<<MD_DISK_WRITEMOSTLY));
+ if (disk_state == ((1<<MD_DISK_ACTIVE) | (1<<MD_DISK_SYNC))) {
if (most_recent < 0 ||
devices[devcnt].i.events
> devices[most_recent].i.events) {
close(mdfd);
free(devices);
free(devmap);
+ free(best);
*stp = st;
return -1;
}
* devices in RAID4 or last devices in RAID4/5/6.
*/
delta = devices[j].i.delta_disks;
- if (devices[j].i.array.level >= 4 &&
- devices[j].i.array.level <= 6 &&
+ if (is_level456(devices[j].i.array.level) &&
i/2 >= content->array.raid_disks - delta)
/* OK */;
else if (devices[j].i.array.level == 4 &&
current_events = devices[chosen_drive].i.events;
add_another:
if (c->verbose >= 0)
- pr_err("forcing event count in %s(%d) from %d upto %d\n",
+ pr_err("forcing event count in %s(%d) from %d up to %d\n",
devices[chosen_drive].devname,
devices[chosen_drive].i.disk.raid_disk,
(int)(devices[chosen_drive].i.events),
continue;
}
content->events = devices[most_recent].i.events;
- tst->ss->update_super(tst, content, "force-one",
+ tst->ss->update_super(tst, content, UOPT_SPEC_FORCE_ONE,
devices[chosen_drive].devname, c->verbose,
0, NULL);
pr_err("failed to add %s to %s: %s\n",
devices[j].devname, mddev,
strerror(errno));
+ if (errno == EINVAL && content->array.level == 0 &&
+ content->array.layout != 0) {
+ cont_err("Possibly your kernel doesn't support RAID0 layouts.\n");
+ cont_err("Please upgrade.\n");
+ }
if (i < content->array.raid_disks * 2 ||
i == bestcnt)
okcnt--;
i/2, mddev);
}
- if (content->array.level == LEVEL_CONTAINER) {
+ if (is_container(content->array.level)) {
+ sysfs_rules_apply(mddev, content);
if (c->verbose >= 0) {
pr_err("Container %s has been assembled with %d drive%s",
mddev, okcnt + sparecnt + journalcnt,
okcnt + sparecnt + journalcnt == 1 ? "" : "s");
if (okcnt < (unsigned)content->array.raid_disks)
- fprintf(stderr, " (out of %d)",
+ fprintf(stderr, " (out of %d)\n",
content->array.raid_disks);
- fprintf(stderr, "\n");
+ else
+ fprintf(stderr, "\n");
}
if (st->ss->validate_container) {
pr_err("%s: Need a backup file to complete reshape of this array.\n",
mddev);
pr_err("Please provided one with \"--backup-file=...\"\n");
- if (c->update &&
- strcmp(c->update, "revert-reshape") == 0)
+ if (c->update == UOPT_REVERT_RESHAPE)
pr_err("(Don't specify --update=revert-reshape again, that part succeeded.)\n");
return 1;
}
rv = ioctl(mdfd, RUN_ARRAY, NULL);
reopen_mddev(mdfd); /* drop O_EXCL */
if (rv == 0) {
+ sysfs_rules_apply(mddev, content);
if (c->verbose >= 0) {
pr_err("%s has been started with %d drive%s",
mddev, okcnt, okcnt==1?"":"s");
fprintf(stderr, ".\n");
}
if (content->reshape_active &&
- content->array.level >= 4 &&
- content->array.level <= 6) {
+ is_level456(content->array.level)) {
/* might need to increase the size
* of the stripe cache - default is 256
*/
return 0;
}
pr_err("failed to RUN_ARRAY %s: %s\n", mddev, strerror(errno));
+ if (errno == 524 /* ENOTSUP */ &&
+ content->array.level == 0 && content->array.layout == 0)
+ cont_err("Please use --update=layout-original or --update=layout-alternate\n");
if (!enough(content->array.level, content->array.raid_disks,
content->array.layout, 1, avail))
*/
if (map_lock(&map))
pr_err("failed to get exclusive lock on mapfile - continue anyway...\n");
- if (c->update && strcmp(c->update,"uuid") == 0)
+ if (c->update == UOPT_UUID)
mp = NULL;
else
mp = map_by_uuid(&map, content->uuid);
name = content->name;
break;
}
+ if (mddev && map_by_name(&map, mddev) != NULL) {
+ pr_err("Cannot create device with %s because is in use\n", mddev);
+ goto out;
+ }
if (!auto_assem)
/* If the array is listed in mdadm.conf or on
* command line, then we trust the name
*/
trustworthy = LOCAL;
- if (name[0] == 0 &&
- content->array.level == LEVEL_CONTAINER) {
+ if (!name[0] && is_container(content->array.level)) {
name = content->text_version;
trustworthy = METADATA;
}
&most_recent, &bestcnt, &best, inargv);
if (devcnt < 0) {
mdfd = -3;
+ /*
+ * devices is already freed in load_devices, so set devices
+ * to NULL to avoid double free devices.
+ */
+ devices = NULL;
goto out;
}
goto out;
}
- if (c->update && strcmp(c->update, "byteorder")==0)
+ if (c->update == UOPT_BYTEORDER)
st->minor_version = 90;
st->ss->getinfo_super(st, content, NULL);
else
desired_state = (1<<MD_DISK_ACTIVE) | (1<<MD_DISK_SYNC);
+ desired_state |= devices[j].i.disk.state & ((1<<MD_DISK_FAILFAST) |
+ (1<<MD_DISK_WRITEMOSTLY));
+
if (!devices[j].uptodate)
continue;
if (!(devices[j].i.array.state & 1))
clean = 0;
- if (st->ss->update_super(st, &devices[j].i, "assemble", NULL,
+ if (st->ss->update_super(st, &devices[j].i, UOPT_SPEC_ASSEMBLE, NULL,
c->verbose, 0, NULL)) {
if (c->force) {
if (c->verbose >= 0)
i, mddev, devices[j].devname);
}
}
-#if 0
- if (!(super.disks[i].i.disk.state & (1 << MD_DISK_FAULTY))) {
- pr_err("devices %d of %s is not marked FAULTY in superblock, but cannot be found\n",
- i, mddev);
- }
-#endif
}
- if (c->force && !clean &&
+ if (c->force && !clean && !is_container(content->array.level) &&
!enough(content->array.level, content->array.raid_disks,
- content->array.layout, clean,
- avail)) {
- change += st->ss->update_super(st, content, "force-array",
+ content->array.layout, clean, avail)) {
+ change += st->ss->update_super(st, content, UOPT_SPEC_FORCE_ARRAY,
devices[chosen_drive].devname, c->verbose,
0, NULL);
was_forced = 1;
/* First, fill in the map, so that udev can find our name
* as soon as we become active.
*/
- if (c->update && strcmp(c->update, "metadata")==0) {
+ if (c->update == UOPT_METADATA) {
content->array.major_version = 1;
content->array.minor_version = 0;
strcpy(content->text_version, "1.0");
break;
close(mdfd);
}
- usleep(usecs);
+ sleep_for(0, USEC_TO_NSEC(usecs), true);
usecs <<= 1;
}
}
} else if (mdfd >= 0)
close(mdfd);
+ free(best);
/* '2' means 'OK, but not started yet' */
if (rv == -1) {
free(devices);
char *chosen_name, int *result)
{
struct mdinfo *dev, *sra, *dev2;
- int working = 0, preexist = 0;
- int expansion = 0;
+ struct assembly_array_info array = {chosen_name, 0, 0, 0};
int old_raid_disks;
int start_reshape;
char *avail;
int err;
+ int is_clean, all_disks;
+ bool is_raid456;
if (sysfs_init(content, mdfd, NULL)) {
pr_err("Unable to initialize sysfs\n");
}
sra = sysfs_read(mdfd, NULL, GET_VERSION|GET_DEVS);
- if (sra == NULL || strcmp(sra->text_version, content->text_version) != 0) {
- if (content->array.major_version == -1 &&
- content->array.minor_version == -2 &&
- c->readonly &&
- content->text_version[0] == '/')
- content->text_version[0] = '-';
- if (sysfs_set_array(content, 9003) != 0) {
+ if (sra == NULL) {
+ pr_err("Failed to read sysfs parameters\n");
+ return 1;
+ }
+
+ /* Fill sysfs properties only if they are not set. Determine it by checking text_version
+ * and ignoring special character on the first place.
+ */
+ if (strcmp(sra->text_version + 1, content->text_version + 1) != 0) {
+ if (sysfs_set_array(content) != 0) {
sysfs_free(sra);
return 1;
}
if (dev)
continue;
/* Don't want this one any more */
- if (sysfs_set_str(sra, dev2, "slot", "none") < 0 &&
- errno == EBUSY) {
+ if (sysfs_set_str(sra, dev2, "slot", STR_COMMON_NONE) < 0 && errno == EBUSY) {
pr_err("Cannot remove old device %s: not updating %s\n", dev2->sys_name, sra->sys_name);
sysfs_free(sra);
return 1;
if (sysfs_add_disk(content, dev, 1) == 0) {
if (dev->disk.raid_disk >= old_raid_disks &&
content->reshape_active)
- expansion++;
+ array.exp_cnt++;
else
- working++;
+ array.new_cnt++;
} else if (errno == EEXIST)
- preexist++;
+ array.preexist_cnt++;
}
sysfs_free(sra);
- if (working + expansion == 0 && c->runstop <= 0) {
- free(avail);
- return 1;/* Nothing new, don't try to start */
- }
+
+ all_disks = array.new_cnt + array.exp_cnt + array.preexist_cnt;
+
map_update(NULL, fd2devnm(mdfd), content->text_version,
content->uuid, chosen_name);
free(avail);
return err;
}
+ } else if (c->force) {
+ /* Set the array as 'clean' so that we can proceed with starting
+ * it even if we don't have all devices. Mdmon doesn't care
+ * if the dirty flag is set in metadata, it will start managing
+ * it anyway.
+ * This is really important for raid456 (RWH case), other levels
+ * are started anyway.
+ */
+ content->array.state |= 1;
}
+ is_raid456 = is_level456(content->array.level);
+ is_clean = content->array.state & 1;
+
if (enough(content->array.level, content->array.raid_disks,
- content->array.layout, content->array.state & 1, avail) == 0) {
- if (c->export && result)
- *result |= INCR_NO;
- else if (c->verbose >= 0) {
- pr_err("%s assembled with %d device%s",
- chosen_name, preexist + working,
- preexist + working == 1 ? "":"s");
- if (preexist)
- fprintf(stderr, " (%d new)", working);
- fprintf(stderr, " but not started\n");
- }
+ content->array.layout, is_clean, avail) == 0) {
+ set_array_assembly_status(c, result, INCR_NO, &array);
+
+ if (c->verbose >= 0 && is_raid456 && !is_clean)
+ pr_err("Consider --force to start dirty degraded array\n");
+
free(avail);
return 1;
}
free(avail);
- if (c->runstop <= 0 &&
- (working + preexist + expansion) <
- content->array.working_disks) {
- if (c->export && result)
- *result |= INCR_UNSAFE;
- else if (c->verbose >= 0) {
- pr_err("%s assembled with %d device%s",
- chosen_name, preexist + working,
- preexist + working == 1 ? "":"s");
- if (preexist)
- fprintf(stderr, " (%d new)", working);
- fprintf(stderr, " but not safe to start\n");
- }
+ if (c->runstop <= 0 && all_disks < content->array.working_disks) {
+
+ set_array_assembly_status(c, result, INCR_UNSAFE, &array);
+
+ if (c->verbose >= 0 && c->force)
+ pr_err("Consider --run to start array as degraded.\n");
return 1;
}
+ if (is_raid456 && content->resync_start != MaxSector && c->force &&
+ all_disks < content->array.raid_disks) {
+
+ content->resync_start = MaxSector;
+ err = sysfs_set_num(content, NULL, "resync_start", MaxSector);
+ if (err)
+ return 1;
+
+ pr_err("%s array state forced to clean. It may cause data corruption.\n",
+ chosen_name);
+ }
+
+ /*
+ * Before activating the array, perform extra steps required
+ * to configure the internal write-intent bitmap.
+ */
+ if (content->consistency_policy == CONSISTENCY_POLICY_BITMAP &&
+ st->ss->set_bitmap)
+ st->ss->set_bitmap(st, content);
if (start_reshape) {
- int spare = content->array.raid_disks + expansion;
+ int spare = content->array.raid_disks + array.exp_cnt;
if (restore_backup(st, content,
- working,
+ array.new_cnt,
spare, &c->backup_file, c->verbose) == 1)
return 1;
- err = sysfs_set_str(content, NULL,
- "array_state", "readonly");
+ if (content->reshape_progress == 0) {
+ /* If reshape progress is 0 - we are assembling the
+ * array that was stopped, before reshape has started.
+ * Array needs to be started as active, Grow_continue()
+ * will start the reshape.
+ */
+ sysfs_set_num(content, NULL, "reshape_position",
+ MaxSector);
+ err = sysfs_set_str(content, NULL,
+ "array_state", "active");
+ sysfs_set_num(content, NULL, "reshape_position", 0);
+ } else {
+ err = sysfs_set_str(content, NULL,
+ "array_state", "readonly");
+ }
+
if (err)
return 1;
!start_reshape)
block_subarray(content);
- if (c->export && result) {
- if (err)
- *result |= INCR_NO;
- else
- *result |= INCR_YES;
- } else if (c->verbose >= 0) {
- if (err)
- pr_err("array %s now has %d device%s",
- chosen_name, working + preexist,
- working + preexist == 1 ? "":"s");
- else
- pr_err("Started %s with %d device%s",
- chosen_name, working + preexist,
- working + preexist == 1 ? "":"s");
- if (preexist)
- fprintf(stderr, " (%d new)", working);
- if (expansion)
- fprintf(stderr, " ( + %d for expansion)",
- expansion);
- fprintf(stderr, "\n");
- }
- if (!err)
+ if (err)
+ set_array_assembly_status(c, result, INCR_NO, &array);
+ else {
+ set_array_assembly_status(c, result, INCR_YES, &array);
wait_for(chosen_name, mdfd);
+ sysfs_rules_apply(chosen_name, content);
+ }
+
return err;
/* FIXME should have an O_EXCL and wait for read-auto */
}