Blame SOURCES/0054-Create-add-support-for-RAID0-layouts.patch

2910d5
From 329dfc28debb58ffe7bd1967cea00fc583139aca Mon Sep 17 00:00:00 2001
2910d5
From: NeilBrown <neilb@suse.de>
2910d5
Date: Mon, 4 Nov 2019 14:27:49 +1100
2910d5
Subject: [RHEL8.2 PATCH 54/61] Create: add support for RAID0 layouts.
2910d5
2910d5
Since Linux 5.4 a layout is needed for RAID0 arrays with
2910d5
varying device sizes.
2910d5
This patch makes the layout of an array visible (via --examine)
2910d5
and sets the layout on newly created arrays.
2910d5
--layout=dangerous
2910d5
can be used to avoid setting a layout so that they array
2910d5
can be used on older kernels.
2910d5
2910d5
Tested-by: dann frazier <dann.frazier@canonical.com>
2910d5
Signed-off-by: NeilBrown <neilb@suse.de>
2910d5
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
2910d5
---
2910d5
 Create.c   | 11 +++++++++++
2910d5
 Detail.c   |  5 +++++
2910d5
 maps.c     | 12 ++++++++++++
2910d5
 md.4       | 14 ++++++++++++++
2910d5
 mdadm.8.in | 30 +++++++++++++++++++++++++++++-
2910d5
 mdadm.c    |  8 ++++++++
2910d5
 mdadm.h    |  8 +++++++-
2910d5
 super0.c   |  6 ++++++
2910d5
 super1.c   | 30 +++++++++++++++++++++++++++++-
2910d5
 9 files changed, 121 insertions(+), 3 deletions(-)
2910d5
2910d5
diff --git a/Create.c b/Create.c
2910d5
index 292f92a..6f84e5b 100644
2910d5
--- a/Create.c
2910d5
+++ b/Create.c
2910d5
@@ -51,6 +51,9 @@ static int default_layout(struct supertype *st, int level, int verbose)
2910d5
 		default: /* no layout */
2910d5
 			layout = 0;
2910d5
 			break;
2910d5
+		case 0:
2910d5
+			layout = RAID0_ORIG_LAYOUT;
2910d5
+			break;
2910d5
 		case 10:
2910d5
 			layout = 0x102; /* near=2, far=1 */
2910d5
 			if (verbose > 0)
2910d5
@@ -950,6 +953,11 @@ int Create(struct supertype *st, char *mddev,
2910d5
 				if (rv) {
2910d5
 					pr_err("ADD_NEW_DISK for %s failed: %s\n",
2910d5
 					       dv->devname, strerror(errno));
2910d5
+					if (errno == EINVAL &&
2910d5
+					    info.array.level == 0) {
2910d5
+						pr_err("Possibly your kernel doesn't support RAID0 layouts.\n");
2910d5
+						pr_err("Either upgrade, or use --layout=dangerous\n");
2910d5
+					}
2910d5
 					goto abort_locked;
2910d5
 				}
2910d5
 				break;
2910d5
@@ -1046,6 +1054,9 @@ int Create(struct supertype *st, char *mddev,
2910d5
 			if (ioctl(mdfd, RUN_ARRAY, &param)) {
2910d5
 				pr_err("RUN_ARRAY failed: %s\n",
2910d5
 				       strerror(errno));
2910d5
+				if (errno == 524 /* ENOTSUP */ &&
2910d5
+				    info.array.level == 0)
2910d5
+					cont_err("Please use --layout=original or --layout=alternate\n");
2910d5
 				if (info.array.chunk_size & (info.array.chunk_size-1)) {
2910d5
 					cont_err("Problem may be that chunk size is not a power of 2\n");
2910d5
 				}
2910d5
diff --git a/Detail.c b/Detail.c
2910d5
index 24fa462..832485f 100644
2910d5
--- a/Detail.c
2910d5
+++ b/Detail.c
2910d5
@@ -525,6 +525,11 @@ int Detail(char *dev, struct context *c)
2910d5
 			printf("            Layout : %s\n",
2910d5
 			       str ? str : "-unknown-");
2910d5
 		}
2910d5
+		if (array.level == 0 && array.layout) {
2910d5
+			str = map_num(r0layout, array.layout);
2910d5
+			printf("            Layout : %s\n",
2910d5
+			       str ? str : "-unknown-");
2910d5
+		}
2910d5
 		if (array.level == 6) {
2910d5
 			str = map_num(r6layout, array.layout);
2910d5
 			printf("            Layout : %s\n",
2910d5
diff --git a/maps.c b/maps.c
2910d5
index 49b7f2c..a4fd279 100644
2910d5
--- a/maps.c
2910d5
+++ b/maps.c
2910d5
@@ -73,6 +73,18 @@ mapping_t r6layout[] = {
2910d5
 	{ NULL, UnSet }
2910d5
 };
2910d5
 
2910d5
+/* raid0 layout is only needed because of a bug in 3.14 which changed
2910d5
+ * the effective layout of raid0 arrays with varying device sizes.
2910d5
+ */
2910d5
+mapping_t r0layout[] = {
2910d5
+	{ "original", RAID0_ORIG_LAYOUT},
2910d5
+	{ "alternate", RAID0_ALT_MULTIZONE_LAYOUT},
2910d5
+	{ "1", 1}, /* aka ORIG */
2910d5
+	{ "2", 2}, /* aka ALT */
2910d5
+	{ "dangerous", 0},
2910d5
+	{ NULL, UnSet},
2910d5
+};
2910d5
+
2910d5
 mapping_t pers[] = {
2910d5
 	{ "linear", LEVEL_LINEAR},
2910d5
 	{ "raid0", 0},
2910d5
diff --git a/md.4 b/md.4
2910d5
index e86707a..6fe2755 100644
2910d5
--- a/md.4
2910d5
+++ b/md.4
2910d5
@@ -193,6 +193,20 @@ smallest device has been exhausted, the RAID0 driver starts
2910d5
 collecting chunks into smaller stripes that only span the drives which
2910d5
 still have remaining space.
2910d5
 
2910d5
+A bug was introduced in linux 3.14 which changed the layout of blocks in
2910d5
+a RAID0 beyond the region that is striped over all devices.  This bug
2910d5
+does not affect an array with all devices the same size, but can affect
2910d5
+other RAID0 arrays.
2910d5
+
2910d5
+Linux 5.4 (and some stable kernels to which the change was backported)
2910d5
+will not normally assemble such an array as it cannot know which layout
2910d5
+to use.  There is a module parameter "raid0.default_layout" which can be
2910d5
+set to "1" to force the kernel to use the pre-3.14 layout or to "2" to
2910d5
+force it to use the 3.14-and-later layout.  when creating a new RAID0
2910d5
+array,
2910d5
+.I mdadm
2910d5
+will record the chosen layout in the metadata in a way that allows newer
2910d5
+kernels to assemble the array without needing a module parameter.
2910d5
 
2910d5
 .SS RAID1
2910d5
 
2910d5
diff --git a/mdadm.8.in b/mdadm.8.in
2910d5
index 9aec9f4..fc9b6a6 100644
2910d5
--- a/mdadm.8.in
2910d5
+++ b/mdadm.8.in
2910d5
@@ -593,6 +593,8 @@ to change the RAID level in some cases.  See LEVEL CHANGES below.
2910d5
 This option configures the fine details of data layout for RAID5, RAID6,
2910d5
 and RAID10 arrays, and controls the failure modes for
2910d5
 .IR faulty .
2910d5
+It can also be used for working around a kernel bug with RAID0, but generally
2910d5
+doesn't need to be used explicitly.
2910d5
 
2910d5
 The layout of the RAID5 parity block can be one of
2910d5
 .BR left\-asymmetric ,
2910d5
@@ -652,7 +654,7 @@ option to set subsequent failure modes.
2910d5
 "clear" or "none" will remove any pending or periodic failure modes,
2910d5
 and "flush" will clear any persistent faults.
2910d5
 
2910d5
-Finally, the layout options for RAID10 are one of 'n', 'o' or 'f' followed
2910d5
+The layout options for RAID10 are one of 'n', 'o' or 'f' followed
2910d5
 by a small number.  The default is 'n2'.  The supported options are:
2910d5
 
2910d5
 .I 'n'
2910d5
@@ -677,6 +679,32 @@ devices in the array.  It does not need to divide evenly into that
2910d5
 number (e.g. it is perfectly legal to have an 'n2' layout for an array
2910d5
 with an odd number of devices).
2910d5
 
2910d5
+A bug introduced in Linux 3.14 means that RAID0 arrays
2910d5
+.B "with devices of differing sizes"
2910d5
+started using a different layout.  This could lead to
2910d5
+data corruption.  Since Linux 5.4 (and various stable releases that received
2910d5
+backports), the kernel will not accept such an array unless
2910d5
+a layout is explictly set.  It can be set to
2910d5
+.RB ' original '
2910d5
+or
2910d5
+.RB ' alternate '.
2910d5
+When creating a new array,
2910d5
+.I mdadm
2910d5
+will select
2910d5
+.RB ' original '
2910d5
+by default, so the layout does not normally need to be set.
2910d5
+An array created for either
2910d5
+.RB ' original '
2910d5
+or
2910d5
+.RB ' alternate '
2910d5
+will not be recognized by an (unpatched) kernel prior to 5.4.  To create
2910d5
+a RAID0 array with devices of differing sizes that can be used on an
2910d5
+older kernel, you can set the layout to
2910d5
+.RB ' dangerous '.
2910d5
+This will use whichever layout the running kernel supports, so the data
2910d5
+on the array may become corrupt when changing kernel from pre-3.14 to a
2910d5
+later kernel.
2910d5
+
2910d5
 When an array is converted between RAID5 and RAID6 an intermediate
2910d5
 RAID6 layout is used in which the second parity block (Q) is always on
2910d5
 the last device.  To convert a RAID5 to RAID6 and leave it in this new
2910d5
diff --git a/mdadm.c b/mdadm.c
2910d5
index 1fb8086..e438f9c 100644
2910d5
--- a/mdadm.c
2910d5
+++ b/mdadm.c
2910d5
@@ -550,6 +550,14 @@ int main(int argc, char *argv[])
2910d5
 				pr_err("raid level must be given before layout.\n");
2910d5
 				exit(2);
2910d5
 
2910d5
+			case 0:
2910d5
+				s.layout = map_name(r0layout, optarg);
2910d5
+				if (s.layout == UnSet) {
2910d5
+					pr_err("layout %s not understood for raid0.\n",
2910d5
+						optarg);
2910d5
+					exit(2);
2910d5
+				}
2910d5
+				break;
2910d5
 			case 5:
2910d5
 				s.layout = map_name(r5layout, optarg);
2910d5
 				if (s.layout == UnSet) {
2910d5
diff --git a/mdadm.h b/mdadm.h
2910d5
index 91f1338..9e98778 100644
2910d5
--- a/mdadm.h
2910d5
+++ b/mdadm.h
2910d5
@@ -763,7 +763,8 @@ extern int restore_stripes(int *dest, unsigned long long *offsets,
2910d5
 
2910d5
 extern char *map_num(mapping_t *map, int num);
2910d5
 extern int map_name(mapping_t *map, char *name);
2910d5
-extern mapping_t r5layout[], r6layout[], pers[], modes[], faultylayout[];
2910d5
+extern mapping_t r0layout[], r5layout[], r6layout[],
2910d5
+	pers[], modes[], faultylayout[];
2910d5
 extern mapping_t consistency_policies[], sysfs_array_states[];
2910d5
 
2910d5
 extern char *map_dev_preferred(int major, int minor, int create,
2910d5
@@ -1758,6 +1759,11 @@ char *xstrdup(const char *str);
2910d5
 #define makedev(M,m) (((M)<<8) | (m))
2910d5
 #endif
2910d5
 
2910d5
+enum r0layout {
2910d5
+	RAID0_ORIG_LAYOUT = 1,
2910d5
+	RAID0_ALT_MULTIZONE_LAYOUT = 2,
2910d5
+};
2910d5
+
2910d5
 /* for raid4/5/6 */
2910d5
 #define ALGORITHM_LEFT_ASYMMETRIC	0
2910d5
 #define ALGORITHM_RIGHT_ASYMMETRIC	1
2910d5
diff --git a/super0.c b/super0.c
2910d5
index 6b7c0e3..6af140b 100644
2910d5
--- a/super0.c
2910d5
+++ b/super0.c
2910d5
@@ -1291,6 +1291,12 @@ static int validate_geometry0(struct supertype *st, int level,
2910d5
 	if (*chunk == UnSet)
2910d5
 		*chunk = DEFAULT_CHUNK;
2910d5
 
2910d5
+	if (level == 0 && layout != UnSet) {
2910d5
+		if (verbose)
2910d5
+			pr_err("0.90 metadata does not support layouts for RAID0\n");
2910d5
+		return 0;
2910d5
+	}
2910d5
+
2910d5
 	if (!subdev)
2910d5
 		return 1;
2910d5
 
2910d5
diff --git a/super1.c b/super1.c
2910d5
index 929466d..cedbb53 100644
2910d5
--- a/super1.c
2910d5
+++ b/super1.c
2910d5
@@ -43,7 +43,7 @@ struct mdp_superblock_1 {
2910d5
 
2910d5
 	__u64	ctime;		/* lo 40 bits are seconds, top 24 are microseconds or 0*/
2910d5
 	__u32	level;		/* -4 (multipath), -1 (linear), 0,1,4,5 */
2910d5
-	__u32	layout;		/* only for raid5 currently */
2910d5
+	__u32	layout;		/* used for raid5, raid6, raid10, and raid0 */
2910d5
 	__u64	size;		/* used size of component devices, in 512byte sectors */
2910d5
 
2910d5
 	__u32	chunksize;	/* in 512byte sectors */
2910d5
@@ -144,6 +144,7 @@ struct misc_dev_info {
2910d5
 #define	MD_FEATURE_JOURNAL		512 /* support write journal */
2910d5
 #define	MD_FEATURE_PPL			1024 /* support PPL */
2910d5
 #define	MD_FEATURE_MUTLIPLE_PPLS	2048 /* support for multiple PPLs */
2910d5
+#define	MD_FEATURE_RAID0_LAYOUT		4096 /* layout is meaningful in RAID0 */
2910d5
 #define	MD_FEATURE_ALL			(MD_FEATURE_BITMAP_OFFSET	\
2910d5
 					|MD_FEATURE_RECOVERY_OFFSET	\
2910d5
 					|MD_FEATURE_RESHAPE_ACTIVE	\
2910d5
@@ -155,6 +156,7 @@ struct misc_dev_info {
2910d5
 					|MD_FEATURE_JOURNAL		\
2910d5
 					|MD_FEATURE_PPL			\
2910d5
 					|MD_FEATURE_MULTIPLE_PPLS	\
2910d5
+					|MD_FEATURE_RAID0_LAYOUT	\
2910d5
 					)
2910d5
 
2910d5
 static int role_from_sb(struct mdp_superblock_1 *sb)
2910d5
@@ -498,6 +500,11 @@ static void examine_super1(struct supertype *st, char *homehost)
2910d5
 	printf("         Events : %llu\n",
2910d5
 	       (unsigned long long)__le64_to_cpu(sb->events));
2910d5
 	printf("\n");
2910d5
+	if (__le32_to_cpu(sb->level) == 0 &&
2910d5
+	    (sb->feature_map & __cpu_to_le32(MD_FEATURE_RAID0_LAYOUT))) {
2910d5
+		c = map_num(r0layout, __le32_to_cpu(sb->layout));
2910d5
+		printf("         Layout : %s\n", c?c:"-unknown-");
2910d5
+	}
2910d5
 	if (__le32_to_cpu(sb->level) == 5) {
2910d5
 		c = map_num(r5layout, __le32_to_cpu(sb->layout));
2910d5
 		printf("         Layout : %s\n", c?c:"-unknown-");
2910d5
@@ -1646,6 +1653,7 @@ struct devinfo {
2910d5
 	int fd;
2910d5
 	char *devname;
2910d5
 	long long data_offset;
2910d5
+	unsigned long long dev_size;
2910d5
 	mdu_disk_info_t disk;
2910d5
 	struct devinfo *next;
2910d5
 };
2910d5
@@ -1687,6 +1695,7 @@ static int add_to_super1(struct supertype *st, mdu_disk_info_t *dk,
2910d5
 	di->devname = devname;
2910d5
 	di->disk = *dk;
2910d5
 	di->data_offset = data_offset;
2910d5
+	get_dev_size(fd, NULL, &di->dev_size);
2910d5
 	di->next = NULL;
2910d5
 	*dip = di;
2910d5
 
2910d5
@@ -1888,10 +1897,25 @@ static int write_init_super1(struct supertype *st)
2910d5
 	unsigned long long sb_offset;
2910d5
 	unsigned long long data_offset;
2910d5
 	long bm_offset;
2910d5
+	int raid0_need_layout = 0;
2910d5
 
2910d5
 	for (di = st->info; di; di = di->next) {
2910d5
 		if (di->disk.state & (1 << MD_DISK_JOURNAL))
2910d5
 			sb->feature_map |= __cpu_to_le32(MD_FEATURE_JOURNAL);
2910d5
+		if (sb->level == 0 && sb->layout != 0) {
2910d5
+			struct devinfo *di2 = st->info;
2910d5
+			unsigned long long s1, s2;
2910d5
+			s1 = di->dev_size;
2910d5
+			if (di->data_offset != INVALID_SECTORS)
2910d5
+				s1 -= di->data_offset;
2910d5
+			s1 /= __le32_to_cpu(sb->chunksize);
2910d5
+			s2 = di2->dev_size;
2910d5
+			if (di2->data_offset != INVALID_SECTORS)
2910d5
+				s2 -= di2->data_offset;
2910d5
+			s2 /= __le32_to_cpu(sb->chunksize);
2910d5
+			if (s1 != s2)
2910d5
+				raid0_need_layout = 1;
2910d5
+		}
2910d5
 	}
2910d5
 
2910d5
 	for (di = st->info; di; di = di->next) {
2910d5
@@ -2039,6 +2063,10 @@ static int write_init_super1(struct supertype *st)
2910d5
 			sb->bblog_offset = 0;
2910d5
 		}
2910d5
 
2910d5
+		/* RAID0 needs a layout if devices aren't all the same size */
2910d5
+		if (raid0_need_layout)
2910d5
+			sb->feature_map |= __cpu_to_le32(MD_FEATURE_RAID0_LAYOUT);
2910d5
+
2910d5
 		sb->sb_csum = calc_sb_1_csum(sb);
2910d5
 		rv = store_super1(st, di->fd);
2910d5
 
2910d5
-- 
2910d5
2.7.5
2910d5