diff mbox series

[v2] fpga: bridge: add owner module and take its refcount

Message ID 20240319172026.76142-1-marpagan@redhat.com (mailing list archive)
State New
Headers show
Series [v2] fpga: bridge: add owner module and take its refcount | expand

Commit Message

Marco Pagani March 19, 2024, 5:20 p.m. UTC
The current implementation of the fpga bridge assumes that the low-level
module registers a driver for the parent device and uses its owner pointer
to take the module's refcount. This approach is problematic since it can
lead to a null pointer dereference while attempting to get the bridge if
the parent device does not have a driver.

To address this problem, add a module owner pointer to the fpga_bridge
struct and use it to take the module's refcount. Modify the function for
registering a bridge to take an additional owner module parameter and
rename it to avoid conflicts. Use the old function name for a helper macro
that automatically sets the module that registers the bridge as the owner.
This ensures compatibility with existing low-level control modules and
reduces the chances of registering a bridge without setting the owner.

Also, update the documentation to keep it consistent with the new interface
for registering an fpga bridge.

Other changes: opportunistically move put_device() from __fpga_bridge_get()
to fpga_bridge_get() and of_fpga_bridge_get() to improve code clarity since
the bridge device is taken in these functions.

Fixes: 21aeda950c5f ("fpga: add fpga bridge framework")
Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Suggested-by: Xu Yilun <yilun.xu@intel.com>
Signed-off-by: Marco Pagani <marpagan@redhat.com>
---

v2:
- Split out protection against races while taking the mod's refcount
---
 Documentation/driver-api/fpga/fpga-bridge.rst |  7 ++-
 drivers/fpga/fpga-bridge.c                    | 57 ++++++++++---------
 include/linux/fpga/fpga-bridge.h              | 10 +++-
 3 files changed, 43 insertions(+), 31 deletions(-)


base-commit: b1a91ca25f15b6d7b311de4465854a5981dee3d3

Comments

Russ Weight March 21, 2024, 5:36 p.m. UTC | #1
On Tue, Mar 19, 2024 at 06:20:24PM +0100, Marco Pagani wrote:
> The current implementation of the fpga bridge assumes that the low-level
> module registers a driver for the parent device and uses its owner pointer
> to take the module's refcount. This approach is problematic since it can
> lead to a null pointer dereference while attempting to get the bridge if
> the parent device does not have a driver.
> 
> To address this problem, add a module owner pointer to the fpga_bridge
> struct and use it to take the module's refcount. Modify the function for
> registering a bridge to take an additional owner module parameter and
> rename it to avoid conflicts. Use the old function name for a helper macro
> that automatically sets the module that registers the bridge as the owner.
> This ensures compatibility with existing low-level control modules and
> reduces the chances of registering a bridge without setting the owner.
> 
> Also, update the documentation to keep it consistent with the new interface
> for registering an fpga bridge.
> 
> Other changes: opportunistically move put_device() from __fpga_bridge_get()
> to fpga_bridge_get() and of_fpga_bridge_get() to improve code clarity since
> the bridge device is taken in these functions.
> 
> Fixes: 21aeda950c5f ("fpga: add fpga bridge framework")
> Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Suggested-by: Xu Yilun <yilun.xu@intel.com>

Reviewed-by: Russ Weight <russ.weight@linux.dev>

> Signed-off-by: Marco Pagani <marpagan@redhat.com>
> ---
> 
> v2:
> - Split out protection against races while taking the mod's refcount
> ---
>  Documentation/driver-api/fpga/fpga-bridge.rst |  7 ++-
>  drivers/fpga/fpga-bridge.c                    | 57 ++++++++++---------
>  include/linux/fpga/fpga-bridge.h              | 10 +++-
>  3 files changed, 43 insertions(+), 31 deletions(-)
> 
> diff --git a/Documentation/driver-api/fpga/fpga-bridge.rst b/Documentation/driver-api/fpga/fpga-bridge.rst
> index 604208534095..d831d5ab6b0d 100644
> --- a/Documentation/driver-api/fpga/fpga-bridge.rst
> +++ b/Documentation/driver-api/fpga/fpga-bridge.rst
> @@ -6,9 +6,12 @@ API to implement a new FPGA bridge
>  
>  * struct fpga_bridge - The FPGA Bridge structure
>  * struct fpga_bridge_ops - Low level Bridge driver ops
> -* fpga_bridge_register() - Create and register a bridge
> +* __fpga_bridge_register() - Create and register a bridge
>  * fpga_bridge_unregister() - Unregister a bridge
>  
> +The helper macro ``fpga_bridge_register()`` automatically sets
> +the module that registers the bridge as the owner.
> +
>  .. kernel-doc:: include/linux/fpga/fpga-bridge.h
>     :functions: fpga_bridge
>  
> @@ -16,7 +19,7 @@ API to implement a new FPGA bridge
>     :functions: fpga_bridge_ops
>  
>  .. kernel-doc:: drivers/fpga/fpga-bridge.c
> -   :functions: fpga_bridge_register
> +   :functions: __fpga_bridge_register
>  
>  .. kernel-doc:: drivers/fpga/fpga-bridge.c
>     :functions: fpga_bridge_unregister
> diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
> index 79c473b3c7c3..8ef395b49bf8 100644
> --- a/drivers/fpga/fpga-bridge.c
> +++ b/drivers/fpga/fpga-bridge.c
> @@ -55,33 +55,26 @@ int fpga_bridge_disable(struct fpga_bridge *bridge)
>  }
>  EXPORT_SYMBOL_GPL(fpga_bridge_disable);
>  
> -static struct fpga_bridge *__fpga_bridge_get(struct device *dev,
> +static struct fpga_bridge *__fpga_bridge_get(struct device *bridge_dev,
>  					     struct fpga_image_info *info)
>  {
>  	struct fpga_bridge *bridge;
> -	int ret = -ENODEV;
>  
> -	bridge = to_fpga_bridge(dev);
> +	bridge = to_fpga_bridge(bridge_dev);
>  
>  	bridge->info = info;
>  
> -	if (!mutex_trylock(&bridge->mutex)) {
> -		ret = -EBUSY;
> -		goto err_dev;
> -	}
> +	if (!mutex_trylock(&bridge->mutex))
> +		return ERR_PTR(-EBUSY);
>  
> -	if (!try_module_get(dev->parent->driver->owner))
> -		goto err_ll_mod;
> +	if (!try_module_get(bridge->br_ops_owner)) {
> +		mutex_unlock(&bridge->mutex);
> +		return ERR_PTR(-ENODEV);
> +	}
>  
>  	dev_dbg(&bridge->dev, "get\n");
>  
>  	return bridge;
> -
> -err_ll_mod:
> -	mutex_unlock(&bridge->mutex);
> -err_dev:
> -	put_device(dev);
> -	return ERR_PTR(ret);
>  }
>  
>  /**
> @@ -98,13 +91,18 @@ static struct fpga_bridge *__fpga_bridge_get(struct device *dev,
>  struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
>  				       struct fpga_image_info *info)
>  {
> -	struct device *dev;
> +	struct fpga_bridge *bridge;
> +	struct device *bridge_dev;
>  
> -	dev = class_find_device_by_of_node(&fpga_bridge_class, np);
> -	if (!dev)
> +	bridge_dev = class_find_device_by_of_node(&fpga_bridge_class, np);
> +	if (!bridge_dev)
>  		return ERR_PTR(-ENODEV);
>  
> -	return __fpga_bridge_get(dev, info);
> +	bridge = __fpga_bridge_get(bridge_dev, info);
> +	if (IS_ERR(bridge))
> +		put_device(bridge_dev);
> +
> +	return bridge;
>  }
>  EXPORT_SYMBOL_GPL(of_fpga_bridge_get);
>  
> @@ -125,6 +123,7 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data)
>  struct fpga_bridge *fpga_bridge_get(struct device *dev,
>  				    struct fpga_image_info *info)
>  {
> +	struct fpga_bridge *bridge;
>  	struct device *bridge_dev;
>  
>  	bridge_dev = class_find_device(&fpga_bridge_class, NULL, dev,
> @@ -132,7 +131,11 @@ struct fpga_bridge *fpga_bridge_get(struct device *dev,
>  	if (!bridge_dev)
>  		return ERR_PTR(-ENODEV);
>  
> -	return __fpga_bridge_get(bridge_dev, info);
> +	bridge = __fpga_bridge_get(bridge_dev, info);
> +	if (IS_ERR(bridge))
> +		put_device(bridge_dev);
> +
> +	return bridge;
>  }
>  EXPORT_SYMBOL_GPL(fpga_bridge_get);
>  
> @@ -146,7 +149,7 @@ void fpga_bridge_put(struct fpga_bridge *bridge)
>  	dev_dbg(&bridge->dev, "put\n");
>  
>  	bridge->info = NULL;
> -	module_put(bridge->dev.parent->driver->owner);
> +	module_put(bridge->br_ops_owner);
>  	mutex_unlock(&bridge->mutex);
>  	put_device(&bridge->dev);
>  }
> @@ -316,18 +319,19 @@ static struct attribute *fpga_bridge_attrs[] = {
>  ATTRIBUTE_GROUPS(fpga_bridge);
>  
>  /**
> - * fpga_bridge_register - create and register an FPGA Bridge device
> + * __fpga_bridge_register - create and register an FPGA Bridge device
>   * @parent:	FPGA bridge device from pdev
>   * @name:	FPGA bridge name
>   * @br_ops:	pointer to structure of fpga bridge ops
>   * @priv:	FPGA bridge private data
> + * @owner:	owner module containing the br_ops
>   *
>   * Return: struct fpga_bridge pointer or ERR_PTR()
>   */
>  struct fpga_bridge *
> -fpga_bridge_register(struct device *parent, const char *name,
> -		     const struct fpga_bridge_ops *br_ops,
> -		     void *priv)
> +__fpga_bridge_register(struct device *parent, const char *name,
> +		       const struct fpga_bridge_ops *br_ops,
> +		       void *priv, struct module *owner)
>  {
>  	struct fpga_bridge *bridge;
>  	int id, ret;
> @@ -357,6 +361,7 @@ fpga_bridge_register(struct device *parent, const char *name,
>  
>  	bridge->name = name;
>  	bridge->br_ops = br_ops;
> +	bridge->br_ops_owner = owner;
>  	bridge->priv = priv;
>  
>  	bridge->dev.groups = br_ops->groups;
> @@ -386,7 +391,7 @@ fpga_bridge_register(struct device *parent, const char *name,
>  
>  	return ERR_PTR(ret);
>  }
> -EXPORT_SYMBOL_GPL(fpga_bridge_register);
> +EXPORT_SYMBOL_GPL(__fpga_bridge_register);
>  
>  /**
>   * fpga_bridge_unregister - unregister an FPGA bridge
> diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
> index 223da48a6d18..94c4edd047e5 100644
> --- a/include/linux/fpga/fpga-bridge.h
> +++ b/include/linux/fpga/fpga-bridge.h
> @@ -45,6 +45,7 @@ struct fpga_bridge_info {
>   * @dev: FPGA bridge device
>   * @mutex: enforces exclusive reference to bridge
>   * @br_ops: pointer to struct of FPGA bridge ops
> + * @br_ops_owner: module containing the br_ops
>   * @info: fpga image specific information
>   * @node: FPGA bridge list node
>   * @priv: low level driver private date
> @@ -54,6 +55,7 @@ struct fpga_bridge {
>  	struct device dev;
>  	struct mutex mutex; /* for exclusive reference to bridge */
>  	const struct fpga_bridge_ops *br_ops;
> +	struct module *br_ops_owner;
>  	struct fpga_image_info *info;
>  	struct list_head node;
>  	void *priv;
> @@ -79,10 +81,12 @@ int of_fpga_bridge_get_to_list(struct device_node *np,
>  			       struct fpga_image_info *info,
>  			       struct list_head *bridge_list);
>  
> +#define fpga_bridge_register(parent, name, br_ops, priv) \
> +	__fpga_bridge_register(parent, name, br_ops, priv, THIS_MODULE)
>  struct fpga_bridge *
> -fpga_bridge_register(struct device *parent, const char *name,
> -		     const struct fpga_bridge_ops *br_ops,
> -		     void *priv);
> +__fpga_bridge_register(struct device *parent, const char *name,
> +		       const struct fpga_bridge_ops *br_ops, void *priv,
> +		       struct module *owner);
>  void fpga_bridge_unregister(struct fpga_bridge *br);
>  
>  #endif /* _LINUX_FPGA_BRIDGE_H */
> 
> base-commit: b1a91ca25f15b6d7b311de4465854a5981dee3d3
> -- 
> 2.44.0
>
diff mbox series

Patch

diff --git a/Documentation/driver-api/fpga/fpga-bridge.rst b/Documentation/driver-api/fpga/fpga-bridge.rst
index 604208534095..d831d5ab6b0d 100644
--- a/Documentation/driver-api/fpga/fpga-bridge.rst
+++ b/Documentation/driver-api/fpga/fpga-bridge.rst
@@ -6,9 +6,12 @@  API to implement a new FPGA bridge
 
 * struct fpga_bridge - The FPGA Bridge structure
 * struct fpga_bridge_ops - Low level Bridge driver ops
-* fpga_bridge_register() - Create and register a bridge
+* __fpga_bridge_register() - Create and register a bridge
 * fpga_bridge_unregister() - Unregister a bridge
 
+The helper macro ``fpga_bridge_register()`` automatically sets
+the module that registers the bridge as the owner.
+
 .. kernel-doc:: include/linux/fpga/fpga-bridge.h
    :functions: fpga_bridge
 
@@ -16,7 +19,7 @@  API to implement a new FPGA bridge
    :functions: fpga_bridge_ops
 
 .. kernel-doc:: drivers/fpga/fpga-bridge.c
-   :functions: fpga_bridge_register
+   :functions: __fpga_bridge_register
 
 .. kernel-doc:: drivers/fpga/fpga-bridge.c
    :functions: fpga_bridge_unregister
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
index 79c473b3c7c3..8ef395b49bf8 100644
--- a/drivers/fpga/fpga-bridge.c
+++ b/drivers/fpga/fpga-bridge.c
@@ -55,33 +55,26 @@  int fpga_bridge_disable(struct fpga_bridge *bridge)
 }
 EXPORT_SYMBOL_GPL(fpga_bridge_disable);
 
-static struct fpga_bridge *__fpga_bridge_get(struct device *dev,
+static struct fpga_bridge *__fpga_bridge_get(struct device *bridge_dev,
 					     struct fpga_image_info *info)
 {
 	struct fpga_bridge *bridge;
-	int ret = -ENODEV;
 
-	bridge = to_fpga_bridge(dev);
+	bridge = to_fpga_bridge(bridge_dev);
 
 	bridge->info = info;
 
-	if (!mutex_trylock(&bridge->mutex)) {
-		ret = -EBUSY;
-		goto err_dev;
-	}
+	if (!mutex_trylock(&bridge->mutex))
+		return ERR_PTR(-EBUSY);
 
-	if (!try_module_get(dev->parent->driver->owner))
-		goto err_ll_mod;
+	if (!try_module_get(bridge->br_ops_owner)) {
+		mutex_unlock(&bridge->mutex);
+		return ERR_PTR(-ENODEV);
+	}
 
 	dev_dbg(&bridge->dev, "get\n");
 
 	return bridge;
-
-err_ll_mod:
-	mutex_unlock(&bridge->mutex);
-err_dev:
-	put_device(dev);
-	return ERR_PTR(ret);
 }
 
 /**
@@ -98,13 +91,18 @@  static struct fpga_bridge *__fpga_bridge_get(struct device *dev,
 struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
 				       struct fpga_image_info *info)
 {
-	struct device *dev;
+	struct fpga_bridge *bridge;
+	struct device *bridge_dev;
 
-	dev = class_find_device_by_of_node(&fpga_bridge_class, np);
-	if (!dev)
+	bridge_dev = class_find_device_by_of_node(&fpga_bridge_class, np);
+	if (!bridge_dev)
 		return ERR_PTR(-ENODEV);
 
-	return __fpga_bridge_get(dev, info);
+	bridge = __fpga_bridge_get(bridge_dev, info);
+	if (IS_ERR(bridge))
+		put_device(bridge_dev);
+
+	return bridge;
 }
 EXPORT_SYMBOL_GPL(of_fpga_bridge_get);
 
@@ -125,6 +123,7 @@  static int fpga_bridge_dev_match(struct device *dev, const void *data)
 struct fpga_bridge *fpga_bridge_get(struct device *dev,
 				    struct fpga_image_info *info)
 {
+	struct fpga_bridge *bridge;
 	struct device *bridge_dev;
 
 	bridge_dev = class_find_device(&fpga_bridge_class, NULL, dev,
@@ -132,7 +131,11 @@  struct fpga_bridge *fpga_bridge_get(struct device *dev,
 	if (!bridge_dev)
 		return ERR_PTR(-ENODEV);
 
-	return __fpga_bridge_get(bridge_dev, info);
+	bridge = __fpga_bridge_get(bridge_dev, info);
+	if (IS_ERR(bridge))
+		put_device(bridge_dev);
+
+	return bridge;
 }
 EXPORT_SYMBOL_GPL(fpga_bridge_get);
 
@@ -146,7 +149,7 @@  void fpga_bridge_put(struct fpga_bridge *bridge)
 	dev_dbg(&bridge->dev, "put\n");
 
 	bridge->info = NULL;
-	module_put(bridge->dev.parent->driver->owner);
+	module_put(bridge->br_ops_owner);
 	mutex_unlock(&bridge->mutex);
 	put_device(&bridge->dev);
 }
@@ -316,18 +319,19 @@  static struct attribute *fpga_bridge_attrs[] = {
 ATTRIBUTE_GROUPS(fpga_bridge);
 
 /**
- * fpga_bridge_register - create and register an FPGA Bridge device
+ * __fpga_bridge_register - create and register an FPGA Bridge device
  * @parent:	FPGA bridge device from pdev
  * @name:	FPGA bridge name
  * @br_ops:	pointer to structure of fpga bridge ops
  * @priv:	FPGA bridge private data
+ * @owner:	owner module containing the br_ops
  *
  * Return: struct fpga_bridge pointer or ERR_PTR()
  */
 struct fpga_bridge *
-fpga_bridge_register(struct device *parent, const char *name,
-		     const struct fpga_bridge_ops *br_ops,
-		     void *priv)
+__fpga_bridge_register(struct device *parent, const char *name,
+		       const struct fpga_bridge_ops *br_ops,
+		       void *priv, struct module *owner)
 {
 	struct fpga_bridge *bridge;
 	int id, ret;
@@ -357,6 +361,7 @@  fpga_bridge_register(struct device *parent, const char *name,
 
 	bridge->name = name;
 	bridge->br_ops = br_ops;
+	bridge->br_ops_owner = owner;
 	bridge->priv = priv;
 
 	bridge->dev.groups = br_ops->groups;
@@ -386,7 +391,7 @@  fpga_bridge_register(struct device *parent, const char *name,
 
 	return ERR_PTR(ret);
 }
-EXPORT_SYMBOL_GPL(fpga_bridge_register);
+EXPORT_SYMBOL_GPL(__fpga_bridge_register);
 
 /**
  * fpga_bridge_unregister - unregister an FPGA bridge
diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
index 223da48a6d18..94c4edd047e5 100644
--- a/include/linux/fpga/fpga-bridge.h
+++ b/include/linux/fpga/fpga-bridge.h
@@ -45,6 +45,7 @@  struct fpga_bridge_info {
  * @dev: FPGA bridge device
  * @mutex: enforces exclusive reference to bridge
  * @br_ops: pointer to struct of FPGA bridge ops
+ * @br_ops_owner: module containing the br_ops
  * @info: fpga image specific information
  * @node: FPGA bridge list node
  * @priv: low level driver private date
@@ -54,6 +55,7 @@  struct fpga_bridge {
 	struct device dev;
 	struct mutex mutex; /* for exclusive reference to bridge */
 	const struct fpga_bridge_ops *br_ops;
+	struct module *br_ops_owner;
 	struct fpga_image_info *info;
 	struct list_head node;
 	void *priv;
@@ -79,10 +81,12 @@  int of_fpga_bridge_get_to_list(struct device_node *np,
 			       struct fpga_image_info *info,
 			       struct list_head *bridge_list);
 
+#define fpga_bridge_register(parent, name, br_ops, priv) \
+	__fpga_bridge_register(parent, name, br_ops, priv, THIS_MODULE)
 struct fpga_bridge *
-fpga_bridge_register(struct device *parent, const char *name,
-		     const struct fpga_bridge_ops *br_ops,
-		     void *priv);
+__fpga_bridge_register(struct device *parent, const char *name,
+		       const struct fpga_bridge_ops *br_ops, void *priv,
+		       struct module *owner);
 void fpga_bridge_unregister(struct fpga_bridge *br);
 
 #endif /* _LINUX_FPGA_BRIDGE_H */