Blame SOURCES/0050-mdadm-Don-t-open-md-device-for-CREATE-and-ASSEMBLE.patch

cd8c44
From 27ad4900501c615b7c6b266bf23948e5606dba53 Mon Sep 17 00:00:00 2001
cd8c44
From: Logan Gunthorpe <logang@deltatee.com>
cd8c44
Date: Wed, 27 Jul 2022 15:52:46 -0600
cd8c44
Subject: [PATCH 50/52] mdadm: Don't open md device for CREATE and ASSEMBLE
cd8c44
cd8c44
The mdadm command tries to open the md device for most modes, first
cd8c44
thing, no matter what. When running to create or assemble an array,
cd8c44
in most cases, the md device will not exist, the open call will fail
cd8c44
and everything will proceed correctly.
cd8c44
cd8c44
However, when running tests, a create or assembly command may be run
cd8c44
shortly after stopping an array and the old md device file may still
cd8c44
be around. Then, if create_on_open is set in the kernel, a new md
cd8c44
device will be created when mdadm does its initial open.
cd8c44
cd8c44
When mdadm gets around to creating the new device with the new_array
cd8c44
parameter it issues this error:
cd8c44
cd8c44
   mdadm: Fail to create md0 when using
cd8c44
   /sys/module/md_mod/parameters/new_array, fallback to creation via node
cd8c44
cd8c44
This is because an mddev was already created by the kernel with the
cd8c44
earlier open() call and thus the new one being created will fail with
cd8c44
EEXIST. The mdadm command will still successfully be created due to
cd8c44
falling back to the node creation method. However, the error message
cd8c44
itself will fail any test that's running it.
cd8c44
cd8c44
This issue is a race condition that is very rare, but a recent change
cd8c44
in the kernel caused this to happen more frequently: about 1 in 50
cd8c44
times.
cd8c44
cd8c44
To fix this, don't bother trying to open the md device for CREATE,
cd8c44
ASSEMBLE and BUILD commands, as the file descriptor will never be used
cd8c44
anyway even if it is successfully openned. The mdfd has not been used
cd8c44
for these commands since:
cd8c44
cd8c44
   7f91af49ad09 ("Delay creation of array devices for assemble/build/create")
cd8c44
cd8c44
The checks that were done on the open device can be changed to being
cd8c44
done with stat.
cd8c44
cd8c44
Side note: it would be nice to disable create_on_open as well to help
cd8c44
solve this, but it seems the work for this was never finished. By default,
cd8c44
mdadm will create using the old node interface when a name is specified
cd8c44
unless the user specifically puts names=yes in a config file, which
cd8c44
doesn't seem to be common or desirable to require this..
cd8c44
cd8c44
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
cd8c44
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
cd8c44
---
cd8c44
 lib.c   | 12 ++++++++++++
cd8c44
 mdadm.c | 40 ++++++++++++++++++++--------------------
cd8c44
 mdadm.h |  1 +
cd8c44
 3 files changed, 33 insertions(+), 20 deletions(-)
cd8c44
cd8c44
diff --git a/lib.c b/lib.c
cd8c44
index 7e3e3d47..e395b28d 100644
cd8c44
--- a/lib.c
cd8c44
+++ b/lib.c
cd8c44
@@ -164,6 +164,18 @@ char *stat2devnm(struct stat *st)
cd8c44
 	return devid2devnm(st->st_rdev);
cd8c44
 }
cd8c44
 
cd8c44
+bool stat_is_md_dev(struct stat *st)
cd8c44
+{
cd8c44
+	if ((S_IFMT & st->st_mode) != S_IFBLK)
cd8c44
+		return false;
cd8c44
+	if (major(st->st_rdev) == MD_MAJOR)
cd8c44
+		return true;
cd8c44
+	if (major(st->st_rdev) == (unsigned)get_mdp_major())
cd8c44
+		return true;
cd8c44
+
cd8c44
+	return false;
cd8c44
+}
cd8c44
+
cd8c44
 char *fd2devnm(int fd)
cd8c44
 {
cd8c44
 	struct stat stb;
cd8c44
diff --git a/mdadm.c b/mdadm.c
cd8c44
index 845e4466..972adb52 100644
cd8c44
--- a/mdadm.c
cd8c44
+++ b/mdadm.c
cd8c44
@@ -1329,6 +1329,9 @@ int main(int argc, char *argv[])
cd8c44
 
cd8c44
 	if (mode == MANAGE || mode == BUILD || mode == CREATE ||
cd8c44
 	    mode == GROW || (mode == ASSEMBLE && ! c.scan)) {
cd8c44
+		struct stat stb;
cd8c44
+		int ret;
cd8c44
+
cd8c44
 		if (devs_found < 1) {
cd8c44
 			pr_err("an md device must be given in this mode\n");
cd8c44
 			exit(2);
cd8c44
@@ -1341,6 +1344,12 @@ int main(int argc, char *argv[])
cd8c44
 			mdfd = open_mddev(devlist->devname, 1);
cd8c44
 			if (mdfd < 0)
cd8c44
 				exit(1);
cd8c44
+
cd8c44
+			ret = fstat(mdfd, &stb;;
cd8c44
+			if (ret) {
cd8c44
+				pr_err("fstat failed on %s.\n", devlist->devname);
cd8c44
+				exit(1);
cd8c44
+			}
cd8c44
 		} else {
cd8c44
 			char *bname = basename(devlist->devname);
cd8c44
 
cd8c44
@@ -1348,30 +1357,21 @@ int main(int argc, char *argv[])
cd8c44
 				pr_err("Name %s is too long.\n", devlist->devname);
cd8c44
 				exit(1);
cd8c44
 			}
cd8c44
-			/* non-existent device is OK */
cd8c44
-			mdfd = open_mddev(devlist->devname, 0);
cd8c44
-		}
cd8c44
-		if (mdfd == -2) {
cd8c44
-			pr_err("device %s exists but is not an md array.\n", devlist->devname);
cd8c44
-			exit(1);
cd8c44
-		}
cd8c44
-		if ((int)ident.super_minor == -2) {
cd8c44
-			struct stat stb;
cd8c44
-			if (mdfd < 0) {
cd8c44
+
cd8c44
+			ret = stat(devlist->devname, &stb;;
cd8c44
+			if (ident.super_minor == -2 && ret != 0) {
cd8c44
 				pr_err("--super-minor=dev given, and listed device %s doesn't exist.\n",
cd8c44
-					devlist->devname);
cd8c44
+				       devlist->devname);
cd8c44
+				exit(1);
cd8c44
+			}
cd8c44
+
cd8c44
+			if (!ret && !stat_is_md_dev(&stb)) {
cd8c44
+				pr_err("device %s exists but is not an md array.\n", devlist->devname);
cd8c44
 				exit(1);
cd8c44
 			}
cd8c44
-			fstat(mdfd, &stb;;
cd8c44
-			ident.super_minor = minor(stb.st_rdev);
cd8c44
-		}
cd8c44
-		if (mdfd >= 0 && mode != MANAGE && mode != GROW) {
cd8c44
-			/* We don't really want this open yet, we just might
cd8c44
-			 * have wanted to check some things
cd8c44
-			 */
cd8c44
-			close(mdfd);
cd8c44
-			mdfd = -1;
cd8c44
 		}
cd8c44
+		if (ident.super_minor == -2)
cd8c44
+			ident.super_minor = minor(stb.st_rdev);
cd8c44
 	}
cd8c44
 
cd8c44
 	if (s.raiddisks) {
cd8c44
diff --git a/mdadm.h b/mdadm.h
cd8c44
index adb7cdaa..8208b81e 100644
cd8c44
--- a/mdadm.h
cd8c44
+++ b/mdadm.h
cd8c44
@@ -1672,6 +1672,7 @@ void *super1_make_v0(struct supertype *st, struct mdinfo *info, mdp_super_t *sb0
cd8c44
 extern char *stat2kname(struct stat *st);
cd8c44
 extern char *fd2kname(int fd);
cd8c44
 extern char *stat2devnm(struct stat *st);
cd8c44
+bool stat_is_md_dev(struct stat *st);
cd8c44
 extern char *fd2devnm(int fd);
cd8c44
 extern void udev_block(char *devnm);
cd8c44
 extern void udev_unblock(void);
cd8c44
-- 
cd8c44
2.31.1
cd8c44