The case when array is already degraded has been omitted by commit 7b99edab2834 ("Assemble.c: respect force flag."). Appropriative support has been added now. Handlers for "run" and "force" have been divided into independent routines. Especially force has to be as meaningless as possible. It respects following rules: - user agrees to start array as degraded (by --run) or is already degraded - raid456 module is in use - some drives are missing (to limit potential abuses) It doesn't allow to skip resync on dirty, but not degraded array. This patch cleans up message generation for external array and makes it consistent. Following code could be reused also for native. In current implementation assemble_container_content is called once, in both Incremental or Assembly mode. Thus makes that partial assembly is not likely to happen. It is possible, but requires user input. Partial assembly during reshape fails (sysfs_set_array error - not yet investigated). For now I put FIXME to mark current logic as known to be buggy because preexist_cnt contains both exp_cnt and new_cnt which may produce an incorrect message. Check for new disks and runstop is unnecessary, so has been removed. This allows to print assemble status in every case, even if nothing new happens. Reported-by: Devon Beets <devon@xxxxxxxxxxxxxxxx> Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@xxxxxxxxxxxxxxx> --- Assemble.c | 176 +++++++++++++++++++++++++++++--------------------- mdadm.8.in | 3 +- super-intel.c | 4 -- 3 files changed, 103 insertions(+), 80 deletions(-) diff --git a/Assemble.c b/Assemble.c index 5c6aca92..f954b4db 100644 --- a/Assemble.c +++ b/Assemble.c @@ -25,6 +25,63 @@ #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(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); + if (status_msg) + fprintf(stderr, " %s", status_msg); + fprintf(stderr, ".\n"); +} + static int name_matches(char *found, char *required, char *homehost, int require_homehost) { /* See if the name found matches the required name, possibly @@ -1911,12 +1968,12 @@ int assemble_container_content(struct supertype *st, int mdfd, 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_raid456, is_clean, all_disks; if (sysfs_init(content, mdfd, NULL)) { pr_err("Unable to initialize sysfs\n"); @@ -1973,17 +2030,16 @@ int assemble_container_content(struct supertype *st, int mdfd, 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); @@ -2045,53 +2101,40 @@ int assemble_container_content(struct supertype *st, int mdfd, content->array.state |= 1; } + is_raid456 = (content->array.level >= 4 && content->array.level <= 6); + 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 ((working + preexist + expansion) < content->array.working_disks) { - if (c->runstop <= 0) { - 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->force) - pr_err("Consider --run to start array as degraded.\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; - } else if (content->array.level >= 4 && - content->array.level <= 6 && - content->resync_start != MaxSector && - c->force) { - /* Don't inform the kernel that the array is not - * clean and requires resync. - */ - 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); - } + + pr_err("%s array state forced to clean. It may cause data corruption.\n", + chosen_name); } /* @@ -2103,9 +2146,9 @@ int assemble_container_content(struct supertype *st, int mdfd, 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; @@ -2168,31 +2211,14 @@ int assemble_container_content(struct supertype *st, int mdfd, !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 { - sysfs_rules_apply(chosen_name, content); - 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 */ } diff --git a/mdadm.8.in b/mdadm.8.in index 7cdb465d..8d7aad0c 100644 --- a/mdadm.8.in +++ b/mdadm.8.in @@ -1132,7 +1132,8 @@ out-of-date. If .I mdadm cannot find enough working devices to start the array, but can find some devices that are recorded as having failed, then it will mark -those devices as working so that the array can be started. +those devices as working so that the array can be started. This works only for +native. For external metadata it allows to start dirty degraded RAID 4, 5, 6. An array which requires .B \-\-force to be started may contain data corruption. Use it carefully. diff --git a/super-intel.c b/super-intel.c index 54699129..c8fd0971 100644 --- a/super-intel.c +++ b/super-intel.c @@ -8108,10 +8108,6 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra if ((!able_to_resync(level, missing) || recovery_start == 0)) this->resync_start = MaxSector; - } else { - /* - * FIXME handle dirty degraded - */ } if (skip) -- 2.26.2