Blob Blame History Raw
From: Thomas Bogendoerfer <tbogendoerfer@suse.de>
Subject: kabi protect fwnode_handle
Patch-Mainline: never, kABI
References: bsc#1098633

Commit 3708184afc77 ("device property: Move FW type specific functionality to FW specific files")
added field ops to struct fwnode_handle, which breaks kabi. Simply
hiding the new field from kabi checker isn't enough, because fwnode_handle
is embedded in the middle of struct device_node. This kabi workaround
uses thetype field in struct fwnode_handle to map to the appropriate
fwnode_operations.

Signed-off-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
---
 drivers/acpi/property.c |    1 -
 drivers/acpi/scan.c     |    1 -
 drivers/base/property.c |   15 ++++++++++++++-
 include/linux/acpi.h    |    1 -
 include/linux/fwnode.h  |   21 +++++++++++++++------
 include/linux/of.h      |    1 -
 6 files changed, 29 insertions(+), 11 deletions(-)

--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -57,7 +57,6 @@ static bool acpi_nondev_subnode_extract(
 
 	dn->name = link->package.elements[0].string.pointer;
 	dn->fwnode.type = FWNODE_ACPI_DATA;
-	dn->fwnode.ops = &acpi_fwnode_ops;
 	dn->parent = parent;
 	INIT_LIST_HEAD(&dn->data.subnodes);
 
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1474,7 +1474,6 @@ void acpi_init_device_object(struct acpi
 	device->handle = handle;
 	device->parent = acpi_bus_get_parent(handle);
 	device->fwnode.type = FWNODE_ACPI;
-	device->fwnode.ops = &acpi_fwnode_ops;
 	acpi_set_device_status(device, sta);
 	acpi_device_get_busid(device);
 	acpi_set_pnp_ids(handle, &device->pnp, type);
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -906,7 +906,6 @@ int device_add_properties(struct device
 		return PTR_ERR(p);
 
 	p->fwnode.type = FWNODE_PDATA;
-	p->fwnode.ops = &pset_fwnode_ops;
 	set_secondary_fwnode(dev, &p->fwnode);
 	p->dev = dev;
 	return 0;
@@ -1315,3 +1314,17 @@ int fwnode_graph_parse_endpoint(struct f
 	return fwnode_call_int_op(fwnode, graph_parse_endpoint, endpoint);
 }
 EXPORT_SYMBOL(fwnode_graph_parse_endpoint);
+
+const struct fwnode_operations *fwnode_ops[FWNODE_MAX] =
+{
+#ifdef CONFIG_OF
+        [FWNODE_OF]             = &of_fwnode_ops,
+#endif
+#ifdef CONFIG_ACPI
+        [FWNODE_ACPI]           = &acpi_fwnode_ops,
+        [FWNODE_ACPI_DATA]      = &acpi_fwnode_ops,
+        [FWNODE_ACPI_STATIC]    = &acpi_fwnode_ops,
+#endif
+        [FWNODE_PDATA]          = &pset_fwnode_ops
+};
+EXPORT_SYMBOL(fwnode_ops);
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -71,7 +71,6 @@ static inline struct fwnode_handle *acpi
 		return NULL;
 
 	fwnode->type = FWNODE_ACPI_STATIC;
-	fwnode->ops = &acpi_fwnode_ops;
 
 	return fwnode;
 }
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -22,6 +22,10 @@ enum fwnode_type {
 	FWNODE_ACPI_STATIC,
 	FWNODE_PDATA,
 	FWNODE_IRQCHIP
+#ifndef __GENKSYMS__
+	,
+	FWNODE_MAX
+#endif
 };
 
 struct fwnode_operations;
@@ -29,7 +33,6 @@ struct fwnode_operations;
 struct fwnode_handle {
 	enum fwnode_type type;
 	struct fwnode_handle *secondary;
-	const struct fwnode_operations *ops;
 };
 
 /**
@@ -93,23 +96,29 @@ struct fwnode_operations {
 				    struct fwnode_endpoint *endpoint);
 };
 
+extern const struct fwnode_operations *fwnode_ops[FWNODE_MAX];
+
+#define fwops(fwnode)						\
+	(((fwnode)->type >= FWNODE_INVALID && (fwnode)->type < FWNODE_MAX) ? \
+		fwnode_ops[fwnode->type] : NULL)
+
 #define fwnode_has_op(fwnode, op)				\
-	((fwnode) && (fwnode)->ops && (fwnode)->ops->op)
+	((fwnode) && (fwops(fwnode)) && (fwops(fwnode))->op)
 #define fwnode_call_int_op(fwnode, op, ...)				\
 	(fwnode ? (fwnode_has_op(fwnode, op) ?				\
-		   (fwnode)->ops->op(fwnode, ## __VA_ARGS__) : -ENXIO) : \
+		   (fwops(fwnode))->op(fwnode, ## __VA_ARGS__) : -ENXIO) : \
 	 -EINVAL)
 #define fwnode_call_bool_op(fwnode, op, ...)				\
 	(fwnode ? (fwnode_has_op(fwnode, op) ?				\
-		   (fwnode)->ops->op(fwnode, ## __VA_ARGS__) : false) : \
+		   (fwops(fwnode))->op(fwnode, ## __VA_ARGS__) : false) : \
 	 false)
 #define fwnode_call_ptr_op(fwnode, op, ...)		\
 	(fwnode_has_op(fwnode, op) ?			\
-	 (fwnode)->ops->op(fwnode, ## __VA_ARGS__) : NULL)
+	 (fwops(fwnode))->op(fwnode, ## __VA_ARGS__) : NULL)
 #define fwnode_call_void_op(fwnode, op, ...)				\
 	do {								\
 		if (fwnode_has_op(fwnode, op))				\
-			(fwnode)->ops->op(fwnode, ## __VA_ARGS__);	\
+			(fwops(fwnode))->op(fwnode, ## __VA_ARGS__);	\
 	} while (false)
 
 #endif
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -105,7 +105,6 @@ static inline void of_node_init(struct d
 {
 	kobject_init(&node->kobj, &of_node_ktype);
 	node->fwnode.type = FWNODE_OF;
-	node->fwnode.ops = &of_fwnode_ops;
 }
 
 /* true when node is initialized */