]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
Fix readding of a readwrite drive into a writemostly array
authorDoug Ledford <dledford@redhat.com>
Mon, 19 Sep 2011 03:06:38 +0000 (13:06 +1000)
committerNeilBrown <neilb@suse.de>
Mon, 19 Sep 2011 03:06:38 +0000 (13:06 +1000)
If you create a two drive raid1 array with one device writemostly, then
fail the readwrite drive, when you add a new device, it will get the
writemostly bit copied out of the remaining device's superblock into
it's own.  You can then remove the new drive and readd it as readwrite,
which will work for the readd, but it leaves the stale WriteMostly1 bit
in devflags resulting in the device going back to writemostly on the
next assembly.

The fix is to make sure that A) when we readd a device and we might have
filled the st->sb info from a running device instead of the device being
readded, then clear/set the WriteMostly1 bit in the super1 struct in
addition to setting the disk state (ditto for super0, but slightly
different mechanism) and B) when adding a clean device to an array (when
we most certainly did copy the superblock info from an existing device),
then clear any writemostly bits.

Signed-off-by: Doug Ledford <dledford@redhat.com>
Signed-off-by: NeilBrown <neilb@suse.de>
Manage.c
mdadm.h
super0.c
super1.c

index 1b2b75afbd99b6717c16a61d08d554506e8e8992..2d8c9165ce0aae4c4f3ff0dbe9d4672e5a5224b2 100644 (file)
--- a/Manage.c
+++ b/Manage.c
@@ -762,11 +762,24 @@ int Manage_subdevs(char *devname, int fd,
                                                remove_partitions(tfd);
                                                close(tfd);
                                                tfd = -1;
-                                               if (update) {
+                                               if (update || dv->writemostly > 0) {
                                                        int rv = -1;
                                                        tfd = dev_open(dv->devname, O_RDWR);
+                                                       if (tfd < 0) {
+                                                               fprintf(stderr, Name ": failed to open %s for"
+                                                                       " superblock update during re-add\n", dv->devname);
+                                                               return 1;
+                                                       }
 
-                                                       if (tfd >= 0)
+                                                       if (dv->writemostly == 1)
+                                                               rv = st->ss->update_super(
+                                                                       st, NULL, "writemostly",
+                                                                       devname, verbose, 0, NULL);
+                                                       if (dv->writemostly == 2)
+                                                               rv = st->ss->update_super(
+                                                                       st, NULL, "readwrite",
+                                                                       devname, verbose, 0, NULL);
+                                                       if (update)
                                                                rv = st->ss->update_super(
                                                                        st, NULL, update,
                                                                        devname, verbose, 0, NULL);
diff --git a/mdadm.h b/mdadm.h
index d616966f3675775ae6e7ee5c5e8812dfcf0ecb00..6a36f43c9771e15a258f287dd8448cd930a23296 100644 (file)
--- a/mdadm.h
+++ b/mdadm.h
@@ -646,6 +646,8 @@ extern struct superswitch {
         *   linear-grow-new - add a new device to a linear array, but don't
         *                   change the size: so superblock still matches
         *   linear-grow-update - now change the size of the array.
+        *   writemostly - set the WriteMostly1 bit in the superblock devflags
+        *   readwrite - clear the WriteMostly1 bit in the superblock devflags
         */
        int (*update_super)(struct supertype *st, struct mdinfo *info,
                            char *update,
index 62c4ff0c518cd0296dcbad798ab59c9f681ca980..f791e9d713841eefe121eb75af9ef7697769f9c4 100644 (file)
--- a/super0.c
+++ b/super0.c
@@ -570,6 +570,10 @@ static int update_super0(struct supertype *st, struct mdinfo *info,
                sb->state &= ~(1<<MD_SB_BITMAP_PRESENT);
        } else if (strcmp(update, "_reshape_progress")==0)
                sb->reshape_position = info->reshape_progress;
+       else if (strcmp(update, "writemostly")==0)
+               sb->state |= (1<<MD_DISK_WRITEMOSTLY);
+       else if (strcmp(update, "readwrite")==0)
+               sb->state &= ~(1<<MD_DISK_WRITEMOSTLY);
        else
                rv = -1;
 
@@ -688,6 +692,8 @@ static int add_to_super0(struct supertype *st, mdu_disk_info_t *dinfo,
        dk->minor = dinfo->minor;
        dk->raid_disk = dinfo->raid_disk;
        dk->state = dinfo->state;
+       /* In case our source disk was writemostly, don't copy that bit */
+       dk->state &= ~(1<<MD_DISK_WRITEMOSTLY);
 
        sb->this_disk = sb->disks[dinfo->number];
        sb->sb_csum = calc_sb0_csum(sb);
index 35e92a3145578ed3e0277da6254a4a4ce238f735..0cd412400a2fd49824c0ac12cdca15e5759466ac 100644 (file)
--- a/super1.c
+++ b/super1.c
@@ -803,6 +803,10 @@ static int update_super1(struct supertype *st, struct mdinfo *info,
                       __le64_to_cpu(sb->data_size));
        } else if (strcmp(update, "_reshape_progress")==0)
                sb->reshape_position = __cpu_to_le64(info->reshape_progress);
+       else if (strcmp(update, "writemostly")==0)
+               sb->devflags |= WriteMostly1;
+       else if (strcmp(update, "readwrite")==0)
+               sb->devflags &= ~WriteMostly1;
        else
                rv = -1;
 
@@ -923,6 +927,7 @@ static int add_to_super1(struct supertype *st, mdu_disk_info_t *dk,
                sb->max_dev = __cpu_to_le32(dk->number+1);
 
        sb->dev_number = __cpu_to_le32(dk->number);
+       sb->devflags = 0; /* don't copy another disks flags */
        sb->sb_csum = calc_sb_1_csum(sb);
 
        dip = (struct devinfo **)&st->info;