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

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