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

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