From: Jeff Mahoney <jeffm@suse.com>
Subject: SUSE: factor out unsupported feature handling
Patch-mainline: Never, SUSE-specific
References: FATE#314697, FATE#317919, FATE#315263

With btrfs, ext4, and now XFS having unsupported features, it makes sense
to factor out the handling of them into a common implementation.  Later
we might use the same to make the messages more consistent.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 include/linux/unsupported-feature.h |   52 ++++++++++++++++++++++++++++
 kernel/Makefile                     |    1 
 kernel/unsupported-feature.c        |   65 ++++++++++++++++++++++++++++++++++++
 3 files changed, 118 insertions(+)

--- /dev/null
+++ b/include/linux/unsupported-feature.h
@@ -0,0 +1,52 @@
+#ifndef _UNSUPPORTED_FEATURE_H_
+#define _UNSUPPORTED_FEATURE_H_
+
+#include <linux/types.h>
+
+struct module;
+
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+struct unsupported_feature {
+	const char *subsys_name;
+	bool allowed;
+};
+
+static inline bool suse_allow_unsupported(struct unsupported_feature *uf)
+{
+	return uf->allowed;
+}
+
+extern struct kernel_param_ops suse_allow_unsupported_param_ops;
+void suse_mark_unsupported(const struct unsupported_feature *uf,
+			   struct module *module);
+
+#define DECLARE_SUSE_UNSUPPORTED_FEATURE(name)				    \
+extern struct unsupported_feature name ##__allow_unsupported;		    \
+static inline bool name ## _allow_unsupported(void)			    \
+{									    \
+	return suse_allow_unsupported(&name ##__allow_unsupported);	    \
+}									    \
+									    \
+static inline void name ## _mark_unsupported(void)			    \
+{									    \
+	suse_mark_unsupported(&name ##__allow_unsupported, THIS_MODULE);    \
+}
+
+#define DEFINE_SUSE_UNSUPPORTED_FEATURE(name) \
+struct unsupported_feature name ##__allow_unsupported = {		    \
+	.subsys_name = __stringify(name),				    \
+	.allowed = false,						    \
+};									    \
+module_param_cb(allow_unsupported, &suse_allow_unsupported_param_ops,	    \
+		&name ## __allow_unsupported, 0644);			    \
+MODULE_PARM_DESC(allow_unsupported,					    \
+		 "Allow using feature that are out of supported scope");
+
+#else
+#define DECLARE_SUSE_UNSUPPORTED_FEATURE(name)				    \
+static inline bool name ## _allow_unsupported(void) { return true; }	    \
+static inline void name ## _mark_unsupported(void) { }
+#define DEFINE_SUSE_UNSUPPORTED_FEATURE(name)
+#endif
+
+#endif /* _UNSUPPORTED_FEATURE_H_ */
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_TORTURE_TEST) += torture.o
 obj-$(CONFIG_HAS_IOMEM) += iomem.o
 obj-$(CONFIG_RSEQ) += rseq.o
 obj-$(CONFIG_WATCH_QUEUE) += watch_queue.o
+obj-$(CONFIG_SUSE_KERNEL_SUPPORTED) += unsupported-feature.o
 
 obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o
 obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
--- /dev/null
+++ b/kernel/unsupported-feature.c
@@ -0,0 +1,65 @@
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/unsupported-feature.h>
+
+static inline struct unsupported_feature *
+to_unsupported_feature(const struct kernel_param *kp)
+{
+	return kp->arg;
+}
+
+static int suse_set_allow_unsupported(const char *buffer,
+				      const struct kernel_param *kp)
+{
+	int ret;
+	struct unsupported_feature *uf = to_unsupported_feature(kp);
+	struct kernel_param dummy_kp = *kp;
+	bool newval;
+
+	dummy_kp.arg = &newval;
+
+	ret = param_set_bool(buffer, &dummy_kp);
+	if (ret)
+		return ret;
+	if (uf->allowed && !newval) {
+		pr_info("%s: disallowing unsupported features, kernel remains tainted.\n",
+			uf->subsys_name);
+		uf->allowed = false;
+	} else if (!uf->allowed && newval) {
+		pr_info("%s: allowing unsupported features, kernel tainted.\n",
+			uf->subsys_name);
+		add_taint(TAINT_NO_SUPPORT, LOCKDEP_STILL_OK);
+		uf->allowed = true;
+	}
+	return 0;
+}
+
+static int suse_get_allow_unsupported(char *buffer,
+				      const struct kernel_param *kp)
+{
+	struct unsupported_feature *uf = to_unsupported_feature(kp);
+	struct kernel_param dummy_kp = *kp;
+
+	dummy_kp.arg = &uf->allowed;
+	return param_get_bool(buffer, &dummy_kp);
+}
+
+struct kernel_param_ops suse_allow_unsupported_param_ops = {
+	.set = suse_set_allow_unsupported,
+	.get = suse_get_allow_unsupported,
+};
+EXPORT_SYMBOL_GPL(suse_allow_unsupported_param_ops);
+
+/* including above breaks kABI due to struct module becoming defined */
+#include <linux/module.h>
+
+void suse_mark_unsupported(const struct unsupported_feature *uf,
+			   struct module *module)
+{
+	if (module && !test_and_set_bit(TAINT_NO_SUPPORT, &module->taints))
+		pr_warn("%s: marking %s unsupported\n",
+		        uf->subsys_name, module_name(module));
+}
+EXPORT_SYMBOL_GPL(suse_mark_unsupported);
+
+
