From: Ma Wupeng mawupeng1@huawei.com
backport patch to fix CVE-2024-36479.
Alexis Lothoré (1): fpga: bridge: properly initialize bridge device before populating children
Marco Pagani (2): fpga: remove redundant checks for bridge ops fpga: bridge: add owner module and take its refcount
Russ Weight (2): fpga: bridge: Rename dev to parent for parent device fpga: bridge: Use standard dev_release for class driver
Documentation/driver-api/fpga/fpga-bridge.rst | 11 +- drivers/fpga/altera-fpga2sdram.c | 12 +- drivers/fpga/altera-freeze-bridge.c | 10 +- drivers/fpga/altera-hps2fpga.c | 12 +- drivers/fpga/dfl-fme-br.c | 10 +- drivers/fpga/fpga-bridge.c | 188 ++++++------------ drivers/fpga/xilinx-pr-decoupler.c | 13 +- include/linux/fpga/fpga-bridge.h | 34 +++- 8 files changed, 116 insertions(+), 174 deletions(-)
From: Russ Weight russell.h.weight@intel.com
mainline inclusion from mainline-v5.14-rc1 commit ceb8ab3c07db02d6a9bee68414e5f69a1c991182 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA7YMH
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Rename variable "dev" to "parent" in cases where it represents the parent device.
Signed-off-by: Russ Weight russell.h.weight@intel.com Reviewed-by: Xu Yilun yilun.xu@intel.com Signed-off-by: Moritz Fischer mdf@kernel.org Link: https://lore.kernel.org/r/20210614170909.232415-5-mdf@kernel.org Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Ma Wupeng mawupeng1@huawei.com --- drivers/fpga/fpga-bridge.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 851debe32bf0..85bbeb2d7f3f 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -313,7 +313,7 @@ ATTRIBUTE_GROUPS(fpga_bridge);
/** * fpga_bridge_create - create and initialize a struct fpga_bridge - * @dev: FPGA bridge device from pdev + * @parent: FPGA bridge device from pdev * @name: FPGA bridge name * @br_ops: pointer to structure of fpga bridge ops * @priv: FPGA bridge private data @@ -323,7 +323,7 @@ ATTRIBUTE_GROUPS(fpga_bridge); * * Return: struct fpga_bridge or NULL */ -struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, +struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name, const struct fpga_bridge_ops *br_ops, void *priv) { @@ -331,7 +331,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, int id, ret;
if (!name || !strlen(name)) { - dev_err(dev, "Attempt to register with no name!\n"); + dev_err(parent, "Attempt to register with no name!\n"); return NULL; }
@@ -353,8 +353,8 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, device_initialize(&bridge->dev); bridge->dev.groups = br_ops->groups; bridge->dev.class = fpga_bridge_class; - bridge->dev.parent = dev; - bridge->dev.of_node = dev->of_node; + bridge->dev.parent = parent; + bridge->dev.of_node = parent->of_node; bridge->dev.id = id;
ret = dev_set_name(&bridge->dev, "br%d", id); @@ -392,7 +392,7 @@ static void devm_fpga_bridge_release(struct device *dev, void *res)
/** * devm_fpga_bridge_create - create and init a managed struct fpga_bridge - * @dev: FPGA bridge device from pdev + * @parent: FPGA bridge device from pdev * @name: FPGA bridge name * @br_ops: pointer to structure of fpga bridge ops * @priv: FPGA bridge private data @@ -408,7 +408,7 @@ static void devm_fpga_bridge_release(struct device *dev, void *res) * Return: struct fpga_bridge or NULL */ struct fpga_bridge -*devm_fpga_bridge_create(struct device *dev, const char *name, +*devm_fpga_bridge_create(struct device *parent, const char *name, const struct fpga_bridge_ops *br_ops, void *priv) { struct fpga_bridge **ptr, *bridge; @@ -417,12 +417,12 @@ struct fpga_bridge if (!ptr) return NULL;
- bridge = fpga_bridge_create(dev, name, br_ops, priv); + bridge = fpga_bridge_create(parent, name, br_ops, priv); if (!bridge) { devres_free(ptr); } else { *ptr = bridge; - devres_add(dev, ptr); + devres_add(parent, ptr); }
return bridge;
From: Russ Weight russell.h.weight@intel.com
mainline inclusion from mainline-v5.17-rc1 commit 0d70af3c2530a70f1b2c197feaa63fbd3548ce34 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA7YMH
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
The FPGA bridge class driver data structure is being treated as a managed resource instead of using the standard dev_release call-back function to release the class data structure. This change removes the managed resource code and combines the create() and register() functions into a single register() function.
Signed-off-by: Russ Weight russell.h.weight@intel.com Reviewed-by: Xu Yilun yilun.xu@intel.com Acked-by: Xu Yilun yilun.xu@intel.com Signed-off-by: Moritz Fischer mdf@kernel.org
Conflicts: Documentation/driver-api/fpga/fpga-bridge.rst drivers/fpga/fpga-bridge.c drivers/fpga/xilinx-pr-decoupler.c Signed-off-by: Ma Wupeng mawupeng1@huawei.com --- Documentation/driver-api/fpga/fpga-bridge.rst | 6 +- drivers/fpga/altera-fpga2sdram.c | 12 +- drivers/fpga/altera-freeze-bridge.c | 10 +- drivers/fpga/altera-hps2fpga.c | 12 +- drivers/fpga/dfl-fme-br.c | 10 +- drivers/fpga/fpga-bridge.c | 122 ++++-------------- drivers/fpga/xilinx-pr-decoupler.c | 13 +- include/linux/fpga/fpga-bridge.h | 30 +++-- 8 files changed, 72 insertions(+), 143 deletions(-)
diff --git a/Documentation/driver-api/fpga/fpga-bridge.rst b/Documentation/driver-api/fpga/fpga-bridge.rst index 198aadafd3e7..627cf372f412 100644 --- a/Documentation/driver-api/fpga/fpga-bridge.rst +++ b/Documentation/driver-api/fpga/fpga-bridge.rst @@ -6,8 +6,7 @@ API to implement a new FPGA bridge
* struct fpga_bridge — The FPGA Bridge structure * struct fpga_bridge_ops — Low level Bridge driver ops -* devm_fpga_bridge_create() — Allocate and init a bridge struct -* fpga_bridge_register() — Register a bridge +* fpga_bridge_register() — Create and register a bridge * fpga_bridge_unregister() — Unregister a bridge
.. kernel-doc:: include/linux/fpga/fpga-bridge.h @@ -16,9 +15,6 @@ API to implement a new FPGA bridge .. kernel-doc:: include/linux/fpga/fpga-bridge.h :functions: fpga_bridge_ops
-.. kernel-doc:: drivers/fpga/fpga-bridge.c - :functions: devm_fpga_bridge_create - .. kernel-doc:: drivers/fpga/fpga-bridge.c :functions: fpga_bridge_register
diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c index a78e49c63c64..ff3a646fd9e3 100644 --- a/drivers/fpga/altera-fpga2sdram.c +++ b/drivers/fpga/altera-fpga2sdram.c @@ -121,17 +121,13 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) /* Get f2s bridge configuration saved in handoff register */ regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
- br = devm_fpga_bridge_create(dev, F2S_BRIDGE_NAME, - &altera_fpga2sdram_br_ops, priv); - if (!br) - return -ENOMEM; + br = fpga_bridge_register(dev, F2S_BRIDGE_NAME, + &altera_fpga2sdram_br_ops, priv); + if (IS_ERR(br)) + return PTR_ERR(br);
platform_set_drvdata(pdev, br);
- ret = fpga_bridge_register(br); - if (ret) - return ret; - dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) { diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c index dd58c4aea92e..bfbfa43cd05b 100644 --- a/drivers/fpga/altera-freeze-bridge.c +++ b/drivers/fpga/altera-freeze-bridge.c @@ -244,14 +244,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
priv->base_addr = base_addr;
- br = devm_fpga_bridge_create(dev, FREEZE_BRIDGE_NAME, - &altera_freeze_br_br_ops, priv); - if (!br) - return -ENOMEM; + br = fpga_bridge_register(dev, FREEZE_BRIDGE_NAME, + &altera_freeze_br_br_ops, priv); + if (IS_ERR(br)) + return PTR_ERR(br);
platform_set_drvdata(pdev, br);
- return fpga_bridge_register(br); + return 0; }
static int altera_freeze_br_remove(struct platform_device *pdev) diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c index 77b95f251821..aa758426c22b 100644 --- a/drivers/fpga/altera-hps2fpga.c +++ b/drivers/fpga/altera-hps2fpga.c @@ -180,19 +180,15 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) } }
- br = devm_fpga_bridge_create(dev, priv->name, - &altera_hps2fpga_br_ops, priv); - if (!br) { - ret = -ENOMEM; + br = fpga_bridge_register(dev, priv->name, + &altera_hps2fpga_br_ops, priv); + if (IS_ERR(br)) { + ret = PTR_ERR(br); goto err; }
platform_set_drvdata(pdev, br);
- ret = fpga_bridge_register(br); - if (ret) - goto err; - return 0;
err: diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c index 3ff9f3a687ce..808d1f4d76df 100644 --- a/drivers/fpga/dfl-fme-br.c +++ b/drivers/fpga/dfl-fme-br.c @@ -68,14 +68,14 @@ static int fme_br_probe(struct platform_device *pdev)
priv->pdata = dev_get_platdata(dev);
- br = devm_fpga_bridge_create(dev, "DFL FPGA FME Bridge", - &fme_bridge_ops, priv); - if (!br) - return -ENOMEM; + br = fpga_bridge_register(dev, "DFL FPGA FME Bridge", + &fme_bridge_ops, priv); + if (IS_ERR(br)) + return PTR_ERR(br);
platform_set_drvdata(pdev, br);
- return fpga_bridge_register(br); + return 0; }
static int fme_br_remove(struct platform_device *pdev) diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 85bbeb2d7f3f..1a7ca0489d91 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -312,36 +312,41 @@ static struct attribute *fpga_bridge_attrs[] = { ATTRIBUTE_GROUPS(fpga_bridge);
/** - * fpga_bridge_create - create and initialize a struct fpga_bridge + * 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 * - * The caller of this function is responsible for freeing the bridge with - * fpga_bridge_free(). Using devm_fpga_bridge_create() instead is recommended. - * - * Return: struct fpga_bridge or NULL + * Return: struct fpga_bridge pointer or ERR_PTR() */ -struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name, - const struct fpga_bridge_ops *br_ops, - void *priv) +struct fpga_bridge * +fpga_bridge_register(struct device *parent, const char *name, + const struct fpga_bridge_ops *br_ops, + void *priv) { struct fpga_bridge *bridge; int id, ret;
+ if (!br_ops) { + dev_err(parent, "Attempt to register without fpga_bridge_ops\n"); + return ERR_PTR(-EINVAL); + } + if (!name || !strlen(name)) { dev_err(parent, "Attempt to register with no name!\n"); - return NULL; + return ERR_PTR(-EINVAL); }
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); if (!bridge) - return NULL; + return ERR_PTR(-ENOMEM);
id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL); - if (id < 0) + if (id < 0) { + ret = id; goto error_kfree; + }
mutex_init(&bridge->mutex); INIT_LIST_HEAD(&bridge->node); @@ -350,17 +355,23 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name, bridge->br_ops = br_ops; bridge->priv = priv;
- device_initialize(&bridge->dev); bridge->dev.groups = br_ops->groups; bridge->dev.class = fpga_bridge_class; bridge->dev.parent = parent; bridge->dev.of_node = parent->of_node; bridge->dev.id = id; + of_platform_populate(bridge->dev.of_node, NULL, NULL, &bridge->dev);
ret = dev_set_name(&bridge->dev, "br%d", id); if (ret) goto error_device;
+ ret = device_register(&bridge->dev); + if (ret) { + put_device(&bridge->dev); + return ERR_PTR(ret); + } + return bridge;
error_device: @@ -368,88 +379,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name, error_kfree: kfree(bridge);
- return NULL; -} -EXPORT_SYMBOL_GPL(fpga_bridge_create); - -/** - * fpga_bridge_free - free a fpga bridge created by fpga_bridge_create() - * @bridge: FPGA bridge struct - */ -void fpga_bridge_free(struct fpga_bridge *bridge) -{ - ida_simple_remove(&fpga_bridge_ida, bridge->dev.id); - kfree(bridge); -} -EXPORT_SYMBOL_GPL(fpga_bridge_free); - -static void devm_fpga_bridge_release(struct device *dev, void *res) -{ - struct fpga_bridge *bridge = *(struct fpga_bridge **)res; - - fpga_bridge_free(bridge); -} - -/** - * devm_fpga_bridge_create - create and init a managed struct fpga_bridge - * @parent: FPGA bridge device from pdev - * @name: FPGA bridge name - * @br_ops: pointer to structure of fpga bridge ops - * @priv: FPGA bridge private data - * - * This function is intended for use in a FPGA bridge driver's probe function. - * After the bridge driver creates the struct with devm_fpga_bridge_create(), it - * should register the bridge with fpga_bridge_register(). The bridge driver's - * remove function should call fpga_bridge_unregister(). The bridge struct - * allocated with this function will be freed automatically on driver detach. - * This includes the case of a probe function returning error before calling - * fpga_bridge_register(), the struct will still get cleaned up. - * - * Return: struct fpga_bridge or NULL - */ -struct fpga_bridge -*devm_fpga_bridge_create(struct device *parent, const char *name, - const struct fpga_bridge_ops *br_ops, void *priv) -{ - struct fpga_bridge **ptr, *bridge; - - ptr = devres_alloc(devm_fpga_bridge_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - bridge = fpga_bridge_create(parent, name, br_ops, priv); - if (!bridge) { - devres_free(ptr); - } else { - *ptr = bridge; - devres_add(parent, ptr); - } - - return bridge; -} -EXPORT_SYMBOL_GPL(devm_fpga_bridge_create); - -/** - * fpga_bridge_register - register a FPGA bridge - * - * @bridge: FPGA bridge struct - * - * Return: 0 for success, error code otherwise. - */ -int fpga_bridge_register(struct fpga_bridge *bridge) -{ - struct device *dev = &bridge->dev; - int ret; - - ret = device_add(dev); - if (ret) - return ret; - - of_platform_populate(dev->of_node, NULL, NULL, dev); - - dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name); - - return 0; + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(fpga_bridge_register);
@@ -475,6 +405,10 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister);
static void fpga_bridge_dev_release(struct device *dev) { + struct fpga_bridge *bridge = to_fpga_bridge(dev); + + ida_simple_remove(&fpga_bridge_ida, bridge->dev.id); + kfree(bridge); }
static int __init fpga_bridge_dev_init(void) diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index 7d69af230567..4a2bcb148d5d 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -114,21 +114,16 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
clk_disable(priv->clk);
- br = devm_fpga_bridge_create(&pdev->dev, "Xilinx PR Decoupler", + br = fpga_bridge_register(&pdev->dev, "Xilinx PR Decoupler", &xlnx_pr_decoupler_br_ops, priv); - if (!br) { - err = -ENOMEM; + if (IS_ERR(br)) { + err = PTR_ERR(br); + dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler"); goto err_clk; }
platform_set_drvdata(pdev, br);
- err = fpga_bridge_register(br); - if (err) { - dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler"); - goto err_clk; - } - return 0;
err_clk: diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h index 817600a32c93..1c517b12300f 100644 --- a/include/linux/fpga/fpga-bridge.h +++ b/include/linux/fpga/fpga-bridge.h @@ -22,6 +22,23 @@ struct fpga_bridge_ops { const struct attribute_group **groups; };
+/** + * struct fpga_bridge_info - collection of parameters an FPGA Bridge + * @name: fpga bridge name + * @br_ops: pointer to structure of fpga bridge ops + * @priv: fpga bridge private data + * + * fpga_bridge_info contains parameters for the register function. These + * are separated into an info structure because they some are optional + * others could be added to in the future. The info structure facilitates + * maintaining a stable API. + */ +struct fpga_bridge_info { + const char *name; + const struct fpga_bridge_ops *br_ops; + void *priv; +}; + /** * struct fpga_bridge - FPGA bridge structure * @name: name of low level FPGA bridge @@ -62,15 +79,10 @@ int of_fpga_bridge_get_to_list(struct device_node *np, struct fpga_image_info *info, struct list_head *bridge_list);
-struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, - const struct fpga_bridge_ops *br_ops, - void *priv); -void fpga_bridge_free(struct fpga_bridge *br); -int fpga_bridge_register(struct fpga_bridge *br); +struct fpga_bridge * +fpga_bridge_register(struct device *parent, const char *name, + const struct fpga_bridge_ops *br_ops, + void *priv); void fpga_bridge_unregister(struct fpga_bridge *br);
-struct fpga_bridge -*devm_fpga_bridge_create(struct device *dev, const char *name, - const struct fpga_bridge_ops *br_ops, void *priv); - #endif /* _LINUX_FPGA_BRIDGE_H */
From: Alexis Lothoré alexis.lothore@bootlin.com
mainline inclusion from mainline-v6.3 commit dc70eb868b9cd2ca01313e5a394e6ea001d513e9 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA7YMH
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
The current code path can lead to warnings because of uninitialized device, which contains, as a consequence, uninitialized kobject. The uninitialized device is passed to of_platform_populate, which will at some point, while creating child device, try to get a reference on uninitialized parent, resulting in the following warning:
kobject: '(null)' ((ptrval)): is not initialized, yet kobject_get() is being called.
The warning is observed after migrating a kernel 5.10.x to 6.1.x. Reverting commit 0d70af3c2530 ("fpga: bridge: Use standard dev_release for class driver") seems to remove the warning. This commit aggregates device_initialize() and device_add() into device_register() but this new call is done AFTER of_platform_populate
Fixes: 0d70af3c2530 ("fpga: bridge: Use standard dev_release for class driver") Signed-off-by: Alexis Lothoré alexis.lothore@bootlin.com Acked-by: Xu Yilun yilun.xu@intel.com Link: https://lore.kernel.org/r/20230404133102.2837535-2-alexis.lothore@bootlin.co... Signed-off-by: Xu Yilun yilun.xu@intel.com Signed-off-by: Ma Wupeng mawupeng1@huawei.com --- drivers/fpga/fpga-bridge.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 1a7ca0489d91..27750b18803a 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -360,7 +360,6 @@ fpga_bridge_register(struct device *parent, const char *name, bridge->dev.parent = parent; bridge->dev.of_node = parent->of_node; bridge->dev.id = id; - of_platform_populate(bridge->dev.of_node, NULL, NULL, &bridge->dev);
ret = dev_set_name(&bridge->dev, "br%d", id); if (ret) @@ -372,6 +371,8 @@ fpga_bridge_register(struct device *parent, const char *name, return ERR_PTR(ret); }
+ of_platform_populate(bridge->dev.of_node, NULL, NULL, &bridge->dev); + return bridge;
error_device:
From: Marco Pagani marpagan@redhat.com
mainline inclusion from mainline-v6.9-rc1 commit b1a91ca25f15b6d7b311de4465854a5981dee3d3 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA7YMH
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Commit 0d70af3c2530 ("fpga: bridge: Use standard dev_release for class driver") introduced a check in fpga_bridge_register() that prevents registering a bridge without ops, making checking on every call redundant.
Signed-off-by: Marco Pagani marpagan@redhat.com Acked-by: Xu Yilun yilun.xu@intel.com Link: https://lore.kernel.org/r/20240201155713.82898-1-marpagan@redhat.com Signed-off-by: Xu Yilun yilun.xu@linux.intel.com
Conflicts: drivers/fpga/fpga-bridge.c [Ma Wupeng: context conflict] Signed-off-by: Ma Wupeng mawupeng1@huawei.com --- drivers/fpga/fpga-bridge.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 27750b18803a..d10dbeb79a3c 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -30,7 +30,7 @@ int fpga_bridge_enable(struct fpga_bridge *bridge) { dev_dbg(&bridge->dev, "enable\n");
- if (bridge->br_ops && bridge->br_ops->enable_set) + if (bridge->br_ops->enable_set) return bridge->br_ops->enable_set(bridge, 1);
return 0; @@ -48,7 +48,7 @@ int fpga_bridge_disable(struct fpga_bridge *bridge) { dev_dbg(&bridge->dev, "disable\n");
- if (bridge->br_ops && bridge->br_ops->enable_set) + if (bridge->br_ops->enable_set) return bridge->br_ops->enable_set(bridge, 0);
return 0; @@ -295,7 +295,7 @@ static ssize_t state_show(struct device *dev, struct fpga_bridge *bridge = to_fpga_bridge(dev); int enable = 1;
- if (bridge->br_ops && bridge->br_ops->enable_show) + if (bridge->br_ops->enable_show) enable = bridge->br_ops->enable_show(bridge);
return sprintf(buf, "%s\n", enable ? "enabled" : "disabled"); @@ -397,7 +397,7 @@ void fpga_bridge_unregister(struct fpga_bridge *bridge) * If the low level driver provides a method for putting bridge into * a desired state upon unregister, do it. */ - if (bridge->br_ops && bridge->br_ops->fpga_bridge_remove) + if (bridge->br_ops->fpga_bridge_remove) bridge->br_ops->fpga_bridge_remove(bridge);
device_unregister(&bridge->dev);
From: Marco Pagani marpagan@redhat.com
stable inclusion from stable-v6.6.33 commit d7c4081c54a1d4068de9440957303a76f9e5c95b category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA7YMH CVE: CVE-2024-36479
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
[ Upstream commit 1da11f822042eb6ef4b6064dc048f157a7852529 ]
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 Acked-by: Xu Yilun yilun.xu@intel.com Link: https://lore.kernel.org/r/20240322171839.233864-1-marpagan@redhat.com Signed-off-by: Xu Yilun yilun.xu@linux.intel.com Signed-off-by: Sasha Levin sashal@kernel.org Conflicts: Documentation/driver-api/fpga/fpga-bridge.rst drivers/fpga/fpga-bridge.c [Ma Wupeng: class_find_device_by_of_node need a pointer] Signed-off-by: Ma Wupeng mawupeng1@huawei.com --- 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 627cf372f412..da08605a3a11 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 FPGA 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 d10dbeb79a3c..0896db7f9ab3 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); }
/** @@ -97,13 +90,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);
@@ -124,6 +122,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, @@ -131,7 +130,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);
@@ -145,7 +148,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); } @@ -312,18 +315,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; @@ -353,6 +357,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; @@ -382,7 +387,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 a FPGA bridge diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h index 1c517b12300f..36363207d9f1 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 */
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/10334 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/M...
FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/10334 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/M...