]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
Assemble: start dirty and degraded array.
authorMariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
Wed, 21 Jul 2021 15:47:54 +0000 (17:47 +0200)
committerJes Sorensen <jsorensen@fb.com>
Tue, 27 Jul 2021 21:35:33 +0000 (17:35 -0400)
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@sigmalabsinc.com>
Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
Assemble.c
mdadm.8.in
super-intel.c

index 5c6aca9253ac8da062b254847410ee2729562b45..f954b4db20419fd3b86d3c6f01f24ed711308d0d 100644 (file)
 #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 */
 }
index 7cdb465de373c1f9e5fdf5aeb57c1c0b817d4260..8d7aad0c76a51f35dc311aa21c0fe88e7ade2ec9 100644 (file)
@@ -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.
index afb0fca7ee37c160a1654fb6e238941dd0e952c7..83ddc000e6bb16e982c12b650a06ddd9c6dd3d11 100644 (file)
@@ -8081,10 +8081,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)