From 7dae2c81e7b2e68a596c5b431444be0fae308156 Mon Sep 17 00:00:00 2001
From: Brian Behlendorf <behlendorf1@llnl.gov>
Date: Tue, 2 May 2017 09:46:18 -0700
Subject: [PATCH] Linux 4.12 compat: super_setup_bdi_name()

All filesystems were converted to dynamically allocated BDIs.  The
destruction of backing_dev_info structures is handled as part of
super block destruction.  Refactor the code to abstract away the
details of creating and destroying a BDI.

Reviewed-by: Chunwei Chen <david.chen@osnexus.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #6089
---
 config/kernel-bdi-setup-and-register.m4 | 38 -----------
 config/kernel-bdi.m4                    | 56 ++++++++++++++++
 config/kernel.m4                        |  2 +-
 include/linux/vfs_compat.h              | 87 ++++++++++++++++++++++---
 include/sys/zfs_vfsops.h                |  1 -
 module/zfs/zfs_vfsops.c                 | 11 ++--
 6 files changed, 141 insertions(+), 54 deletions(-)
 delete mode 100644 config/kernel-bdi-setup-and-register.m4
 create mode 100644 config/kernel-bdi.m4

diff --git a/config/kernel-bdi-setup-and-register.m4 b/config/kernel-bdi-setup-and-register.m4
deleted file mode 100644
index d1062e17ec1e..000000000000
--- a/config/kernel-bdi-setup-and-register.m4
+++ /dev/null
@@ -1,38 +0,0 @@
-dnl #
-dnl # 2.6.32 - 2.6.33, bdi_setup_and_register() is not exported.
-dnl # 2.6.34 - 3.19, bdi_setup_and_register() takes 3 arguments.
-dnl # 4.0 - x.y, bdi_setup_and_register() takes 2 arguments.
-dnl #
-AC_DEFUN([ZFS_AC_KERNEL_BDI_SETUP_AND_REGISTER], [
-	AC_MSG_CHECKING([whether bdi_setup_and_register() wants 2 args])
-	ZFS_LINUX_TRY_COMPILE_SYMBOL([
-		#include <linux/backing-dev.h>
-		struct backing_dev_info bdi;
-	], [
-		char *name = "bdi";
-		int error __attribute__((unused)) =
-		    bdi_setup_and_register(&bdi, name);
-	], [bdi_setup_and_register], [mm/backing-dev.c], [
-		AC_MSG_RESULT(yes)
-		AC_DEFINE(HAVE_2ARGS_BDI_SETUP_AND_REGISTER, 1,
-		    [bdi_setup_and_register() wants 2 args])
-	], [
-		AC_MSG_RESULT(no)
-		AC_MSG_CHECKING([whether bdi_setup_and_register() wants 3 args])
-		ZFS_LINUX_TRY_COMPILE_SYMBOL([
-			#include <linux/backing-dev.h>
-			struct backing_dev_info bdi;
-		], [
-			char *name = "bdi";
-			unsigned int cap = BDI_CAP_MAP_COPY;
-			int error __attribute__((unused)) =
-			    bdi_setup_and_register(&bdi, name, cap);
-		], [bdi_setup_and_register], [mm/backing-dev.c], [
-			AC_MSG_RESULT(yes)
-			AC_DEFINE(HAVE_3ARGS_BDI_SETUP_AND_REGISTER, 1,
-			    [bdi_setup_and_register() wants 3 args])
-		], [
-			AC_MSG_RESULT(no)
-		])
-	])
-])
diff --git a/config/kernel-bdi.m4 b/config/kernel-bdi.m4
new file mode 100644
index 000000000000..c2a9dd28bf4e
--- /dev/null
+++ b/config/kernel-bdi.m4
@@ -0,0 +1,56 @@
+dnl #
+dnl # 2.6.32 - 2.6.33, bdi_setup_and_register() is not exported.
+dnl # 2.6.34 - 3.19, bdi_setup_and_register() takes 3 arguments.
+dnl # 4.0 - 4.11, bdi_setup_and_register() takes 2 arguments.
+dnl # 4.12 - x.y, super_setup_bdi_name() new interface.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_BDI], [
+	AC_MSG_CHECKING([whether super_setup_bdi_name() exists])
+	ZFS_LINUX_TRY_COMPILE_SYMBOL([
+		#include <linux/fs.h>
+		struct super_block sb;
+	], [
+		char *name = "bdi";
+		int error __attribute__((unused)) =
+		    super_setup_bdi_name(&sb, name);
+	], [super_setup_bdi_name], [fs/super.c], [
+		AC_MSG_RESULT(yes)
+		AC_DEFINE(HAVE_SUPER_SETUP_BDI_NAME, 1,
+                    [super_setup_bdi_name() exits])
+	], [
+		AC_MSG_RESULT(no)
+		AC_MSG_CHECKING(
+		    [whether bdi_setup_and_register() wants 2 args])
+		ZFS_LINUX_TRY_COMPILE_SYMBOL([
+			#include <linux/backing-dev.h>
+			struct backing_dev_info bdi;
+		], [
+			char *name = "bdi";
+			int error __attribute__((unused)) =
+			    bdi_setup_and_register(&bdi, name);
+		], [bdi_setup_and_register], [mm/backing-dev.c], [
+			AC_MSG_RESULT(yes)
+			AC_DEFINE(HAVE_2ARGS_BDI_SETUP_AND_REGISTER, 1,
+			    [bdi_setup_and_register() wants 2 args])
+		], [
+			AC_MSG_RESULT(no)
+			AC_MSG_CHECKING(
+			    [whether bdi_setup_and_register() wants 3 args])
+			ZFS_LINUX_TRY_COMPILE_SYMBOL([
+				#include <linux/backing-dev.h>
+				struct backing_dev_info bdi;
+			], [
+				char *name = "bdi";
+				unsigned int cap = BDI_CAP_MAP_COPY;
+				int error __attribute__((unused)) =
+				    bdi_setup_and_register(&bdi, name, cap);
+			], [bdi_setup_and_register], [mm/backing-dev.c], [
+				AC_MSG_RESULT(yes)
+				AC_DEFINE(HAVE_3ARGS_BDI_SETUP_AND_REGISTER, 1,
+				    [bdi_setup_and_register() wants 3 args])
+			], [
+				AC_MSG_RESULT(no)
+			])
+		])
+	])
+])
diff --git a/config/kernel.m4 b/config/kernel.m4
index 71c88aa36cef..638d9e143bba 100644
--- a/config/kernel.m4
+++ b/config/kernel.m4
@@ -100,7 +100,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
 	ZFS_AC_KERNEL_SHRINK_CONTROL_HAS_NID
 	ZFS_AC_KERNEL_S_INSTANCES_LIST_HEAD
 	ZFS_AC_KERNEL_S_D_OP
-	ZFS_AC_KERNEL_BDI_SETUP_AND_REGISTER
+	ZFS_AC_KERNEL_BDI
 	ZFS_AC_KERNEL_SET_NLINK
 	ZFS_AC_KERNEL_ELEVATOR_CHANGE
 	ZFS_AC_KERNEL_5ARG_SGET
diff --git a/include/linux/vfs_compat.h b/include/linux/vfs_compat.h
index cd22b522ab79..4e61b1d09017 100644
--- a/include/linux/vfs_compat.h
+++ b/include/linux/vfs_compat.h
@@ -70,45 +70,114 @@ truncate_setsize(struct inode *ip, loff_t new)
 /*
  * 2.6.32 - 2.6.33, bdi_setup_and_register() is not available.
  * 2.6.34 - 3.19, bdi_setup_and_register() takes 3 arguments.
- * 4.0 - x.y, bdi_setup_and_register() takes 2 arguments.
+ * 4.0 - 4.11, bdi_setup_and_register() takes 2 arguments.
+ * 4.12 - x.y, super_setup_bdi_name() new interface.
  */
-#if defined(HAVE_2ARGS_BDI_SETUP_AND_REGISTER)
+#if defined(HAVE_SUPER_SETUP_BDI_NAME)
 static inline int
-zpl_bdi_setup_and_register(struct backing_dev_info *bdi, char *name)
+zpl_bdi_setup(struct super_block *sb, char *name)
 {
-	return (bdi_setup_and_register(bdi, name));
+	return (super_setup_bdi_name(sb, name));
+}
+static inline void
+zpl_bdi_destroy(struct super_block *sb)
+{
+}
+#elif defined(HAVE_2ARGS_BDI_SETUP_AND_REGISTER)
+static inline int
+zpl_bdi_setup(struct super_block *sb, char *name)
+{
+	struct backing_dev_info *bdi;
+	int error;
+
+	bdi = kmem_zalloc(sizeof (struct backing_dev_info), KM_SLEEP);
+	error = bdi_setup_and_register(bdi, name);
+	if (error) {
+		kmem_free(bdi, sizeof (struct backing_dev_info));
+		return (error);
+	}
+
+	sb->s_bdi = bdi;
+
+	return (0);
+}
+static inline void
+zpl_bdi_destroy(struct super_block *sb)
+{
+	struct backing_dev_info *bdi = sb->s_bdi;
+
+	bdi_destroy(bdi);
+	kmem_free(bdi, sizeof (struct backing_dev_info));
+	sb->s_bdi = NULL;
 }
 #elif defined(HAVE_3ARGS_BDI_SETUP_AND_REGISTER)
 static inline int
-zpl_bdi_setup_and_register(struct backing_dev_info *bdi, char *name)
+zpl_bdi_setup(struct super_block *sb, char *name)
 {
-	return (bdi_setup_and_register(bdi, name, BDI_CAP_MAP_COPY));
+	struct backing_dev_info *bdi;
+	int error;
+
+	bdi = kmem_zalloc(sizeof (struct backing_dev_info), KM_SLEEP);
+	error = bdi_setup_and_register(bdi, name, BDI_CAP_MAP_COPY);
+	if (error) {
+		kmem_free(sb->s_bdi, sizeof (struct backing_dev_info));
+		return (error);
+	}
+
+	sb->s_bdi = bdi;
+
+	return (0);
+}
+static inline void
+zpl_bdi_destroy(struct super_block *sb)
+{
+	struct backing_dev_info *bdi = sb->s_bdi;
+
+	bdi_destroy(bdi);
+	kmem_free(bdi, sizeof (struct backing_dev_info));
+	sb->s_bdi = NULL;
 }
 #else
 extern atomic_long_t zfs_bdi_seq;
 
 static inline int
-zpl_bdi_setup_and_register(struct backing_dev_info *bdi, char *name)
+zpl_bdi_setup(struct super_block *sb, char *name)
 {
+	struct backing_dev_info *bdi;
 	char tmp[32];
 	int error;
 
+	bdi = kmem_zalloc(sizeof (struct backing_dev_info), KM_SLEEP);
 	bdi->name = name;
 	bdi->capabilities = BDI_CAP_MAP_COPY;
 
 	error = bdi_init(bdi);
-	if (error)
+	if (error) {
+		kmem_free(bdi, sizeof (struct backing_dev_info));
 		return (error);
+	}
 
 	sprintf(tmp, "%.28s%s", name, "-%d");
 	error = bdi_register(bdi, NULL, tmp,
 	    atomic_long_inc_return(&zfs_bdi_seq));
 	if (error) {
 		bdi_destroy(bdi);
+		kmem_free(bdi, sizeof (struct backing_dev_info));
 		return (error);
 	}
 
-	return (error);
+	sb->s_bdi = bdi;
+
+	return (0);
+}
+static inline void
+zpl_bdi_destroy(struct super_block *sb)
+{
+	struct backing_dev_info *bdi = sb->s_bdi;
+
+	bdi_destroy(bdi);
+	kmem_free(bdi, sizeof (struct backing_dev_info));
+	sb->s_bdi = NULL;
 }
 #endif
 
diff --git a/include/sys/zfs_vfsops.h b/include/sys/zfs_vfsops.h
index aeecc472dabc..2326da422183 100644
--- a/include/sys/zfs_vfsops.h
+++ b/include/sys/zfs_vfsops.h
@@ -75,7 +75,6 @@ typedef struct zfs_mnt {
 struct zfsvfs {
 	vfs_t		*z_vfs;		/* generic fs struct */
 	struct super_block *z_sb;	/* generic super_block */
-	struct backing_dev_info z_bdi;	/* generic backing dev info */
 	struct zfsvfs	*z_parent;	/* parent fs */
 	objset_t	*z_os;		/* objset reference */
 	uint64_t	z_flags;	/* super_block flags */
diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c
index 40fd5f7648d5..15cadcad5123 100644
--- a/module/zfs/zfs_vfsops.c
+++ b/module/zfs/zfs_vfsops.c
@@ -1568,7 +1568,8 @@ zfsvfs_teardown(zfsvfs_t *zfsvfs, boolean_t unmounting)
 	return (0);
 }
 
-#if !defined(HAVE_2ARGS_BDI_SETUP_AND_REGISTER) && \
+#if !defined(HAVE_SUPER_SETUP_BDI_NAME) && \
+	!defined(HAVE_2ARGS_BDI_SETUP_AND_REGISTER) && \
 	!defined(HAVE_3ARGS_BDI_SETUP_AND_REGISTER)
 atomic_long_t zfs_bdi_seq = ATOMIC_LONG_INIT(0);
 #endif
@@ -1605,13 +1606,13 @@ zfs_domount(struct super_block *sb, zfs_mnt_t *zm, int silent)
 	sb->s_time_gran = 1;
 	sb->s_blocksize = recordsize;
 	sb->s_blocksize_bits = ilog2(recordsize);
-	zfsvfs->z_bdi.ra_pages = 0;
-	sb->s_bdi = &zfsvfs->z_bdi;
 
-	error = -zpl_bdi_setup_and_register(&zfsvfs->z_bdi, "zfs");
+	error = -zpl_bdi_setup(sb, "zfs");
 	if (error)
 		goto out;
 
+	sb->s_bdi->ra_pages = 0;
+
 	/* Set callback operations for the file system. */
 	sb->s_op = &zpl_super_operations;
 	sb->s_xattr = zpl_xattr_handlers;
@@ -1732,7 +1733,7 @@ zfs_umount(struct super_block *sb)
 	arc_remove_prune_callback(zfsvfs->z_arc_prune);
 	VERIFY(zfsvfs_teardown(zfsvfs, B_TRUE) == 0);
 	os = zfsvfs->z_os;
-	bdi_destroy(sb->s_bdi);
+	zpl_bdi_destroy(sb);
 
 	/*
 	 * z_os will be NULL if there was an error in