File 0154-imsm-rebuild-from-2-disk-RAID10.patch of Package mdadm.7989

From fb12a7454000c56aa0439c5bc07fb29de2f3f2a1 Mon Sep 17 00:00:00 2001
From: Tomasz Majchrzak <tomasz.majchrzak@intel.com>
Date: Thu, 10 Aug 2017 15:47:22 +0200
Subject: [PATCH] imsm: rebuild from 2-disk RAID10
Git-commit: fb12a7454000c56aa0439c5bc07fb29de2f3f2a1
Patch-mainline: mdadm-4.0+
References: bsc#1069165, bsc#1069167, bsc#1068030

When RAID10 loses 2 disks and it is still operational, it cannot be
rebuilt. The rebuild process starts for the first disk and completes,
however completion is not recorded in metadata. There is an assumption
that rebuild completion corresponds to transition from degraded to
normal state. It's not the case for 2-disk RAID10 as it's still degraded
after rebuild to first disk completes.

Check if disk rebuild flag is set in the second map and clear it. So far it
has been checked only in the first map (where it was not set). The flag in
the second map has not been cleared but rebuild completion dropped second
map so the problem was not visible.

If rebuild completion is notified and array still has failed disks and is in
degraded state, check first if rebuild position is really unset (the same
check as for array in normal state). If so, mark migration as done but don't
change array state (it should remain degraded). Update failed disk number.

On rebuild start don't clear the rebuild flag in the destination map for all
the drives because failed state is lost for one of them. Just do a copy of
a map and clear the flag in the destination map for the disk that goes into
rebuild. Similarily preserve the rebuild flag in the map during disk removal.

If the disk is missing on array start and migration has been in progress,
don't just cancel it. Check first if maybe one of the disks was not under
rebuild (rebuild flag present both in source and destination map). If so,
rebuild was running despite of failed disk so there is no need to cancel
migration.

Signed-off-by: Tomasz Majchrzak <tomasz.majchrzak@intel.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
Signed-off-by: Coly Li <colyli@suse.de>

---
 super-intel.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 67 insertions(+), 10 deletions(-)

diff --git a/super-intel.c b/super-intel.c
index 51b7cc3..125c3a9 100644
--- a/super-intel.c
+++ b/super-intel.c
@@ -4023,7 +4023,7 @@ static void migrate(struct imsm_dev *dev, struct intel_super *super,
 
 	/* duplicate and then set the target end state in map[0] */
 	memcpy(dest, src, sizeof_imsm_map(src));
-	if (migr_type == MIGR_REBUILD || migr_type == MIGR_GEN_MIGR) {
+	if (migr_type == MIGR_GEN_MIGR) {
 		__u32 ord;
 		int i;
 
@@ -7936,14 +7936,35 @@ static void handle_missing(struct intel_super *super, struct imsm_dev *dev)
 	/* end process for initialization and rebuild only
 	 */
 	if (is_gen_migration(dev) == 0) {
-		__u8 map_state;
-		int failed;
+		int failed = imsm_count_failed(super, dev, MAP_0);
 
-		failed = imsm_count_failed(super, dev, MAP_0);
-		map_state = imsm_check_degraded(super, dev, failed, MAP_0);
+		if (failed) {
+			__u8 map_state;
+			struct imsm_map *map = get_imsm_map(dev, MAP_0);
+			struct imsm_map *map1;
+			int i, ord, ord_map1;
+			int rebuilt = 1;
 
-		if (failed)
-			end_migration(dev, super, map_state);
+			for (i = 0; i < map->num_members; i++) {
+				ord = get_imsm_ord_tbl_ent(dev, i, MAP_0);
+				if (!(ord & IMSM_ORD_REBUILD))
+					continue;
+
+				map1 = get_imsm_map(dev, MAP_1);
+				if (!map1)
+					continue;
+
+				ord_map1 = __le32_to_cpu(map1->disk_ord_tbl[i]);
+				if (ord_map1 & IMSM_ORD_REBUILD)
+					rebuilt = 0;
+			}
+
+			if (rebuilt) {
+				map_state = imsm_check_degraded(super, dev,
+								failed, MAP_0);
+				end_migration(dev, super, map_state);
+			}
+		}
 	}
 	for (dl = super->missing; dl; dl = dl->next)
 		mark_missing(super, dev, &dl->disk, dl->index);
@@ -8225,8 +8246,10 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
 	int failed;
 	int ord;
 	__u8 map_state;
+	int rebuild_done = 0;
+	int i;
 
-	ord = imsm_disk_slot_to_ord(a, n);
+	ord = get_imsm_ord_tbl_ent(dev, n, MAP_X);
 	if (ord < 0)
 		return;
 
@@ -8244,6 +8267,7 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
 		struct imsm_map *migr_map = get_imsm_map(dev, MAP_1);
 
 		set_imsm_ord_tbl_ent(migr_map, n, ord_to_idx(ord));
+		rebuild_done = 1;
 		super->updates_pending++;
 	}
 
@@ -8306,7 +8330,39 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
 				dprintf_cont(" Map state change");
 				end_migration(dev, super, map_state);
 				super->updates_pending++;
+			} else if (!rebuild_done) {
+				break;
+			}
+
+			/* check if recovery is really finished */
+			for (mdi = a->info.devs; mdi ; mdi = mdi->next)
+				if (mdi->recovery_start != MaxSector) {
+					recovery_not_finished = 1;
+					break;
+				}
+			if (recovery_not_finished) {
+				dprintf_cont("\n");
+				dprintf("Rebuild has not finished yet, state not changed");
+				if (a->last_checkpoint < mdi->recovery_start) {
+					a->last_checkpoint =
+						mdi->recovery_start;
+					super->updates_pending++;
+				}
+				break;
 			}
+
+			dprintf_cont(" Rebuild done, still degraded");
+			dev->vol.migr_state = 0;
+			set_migr_type(dev, 0);
+			dev->vol.curr_migr_unit = 0;
+
+			for (i = 0; i < map->num_members; i++) {
+				int idx = get_imsm_ord_tbl_ent(dev, i, MAP_0);
+
+				if (idx & IMSM_ORD_REBUILD)
+					map->failed_disk_num = i;
+			}
+			super->updates_pending++;
 			break;
 		}
 		if (is_gen_migration(dev)) {
@@ -9936,7 +9992,7 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind
 	struct imsm_dev *dev;
 	struct imsm_map *map;
 	unsigned int i, j, num_members;
-	__u32 ord;
+	__u32 ord, ord_map0;
 	struct bbm_log *log = super->bbm_log;
 
 	dprintf("deleting device[%d] from imsm_super\n", index);
@@ -9958,12 +10014,13 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind
 			 * ord-flags to the first map
 			 */
 			ord = get_imsm_ord_tbl_ent(dev, j, MAP_X);
+			ord_map0 = get_imsm_ord_tbl_ent(dev, j, MAP_0);
 
 			if (ord_to_idx(ord) <= index)
 				continue;
 
 			map = get_imsm_map(dev, MAP_0);
-			set_imsm_ord_tbl_ent(map, j, ord_to_idx(ord - 1));
+			set_imsm_ord_tbl_ent(map, j, ord_map0 - 1);
 			map = get_imsm_map(dev, MAP_1);
 			if (map)
 				set_imsm_ord_tbl_ent(map, j, ord - 1);
-- 
2.13.6
openSUSE Build Service is sponsored by