fpga: region: add owner module and take its refcount

[ Upstream commit b7c0e1ecee ]

The current implementation of the fpga region 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 region
during programming if the parent device does not have a driver.

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

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

Fixes: 0fa20cdfcc ("fpga: fpga-region: device tree control for FPGA")
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/20240419083601.77403-1-marpagan@redhat.com
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Marco Pagani 2024-04-19 10:35:59 +02:00 committed by Greg Kroah-Hartman
parent 0bd22a4966
commit 4d7d12b643
3 changed files with 32 additions and 18 deletions

View File

@ -46,13 +46,16 @@ API to add a new FPGA region
---------------------------- ----------------------------
* struct fpga_region - The FPGA region struct * struct fpga_region - The FPGA region struct
* struct fpga_region_info - Parameter structure for fpga_region_register_full() * struct fpga_region_info - Parameter structure for __fpga_region_register_full()
* fpga_region_register_full() - Create and register an FPGA region using the * __fpga_region_register_full() - Create and register an FPGA region using the
fpga_region_info structure to provide the full flexibility of options fpga_region_info structure to provide the full flexibility of options
* fpga_region_register() - Create and register an FPGA region using standard * __fpga_region_register() - Create and register an FPGA region using standard
arguments arguments
* fpga_region_unregister() - Unregister an FPGA region * fpga_region_unregister() - Unregister an FPGA region
Helper macros ``fpga_region_register()`` and ``fpga_region_register_full()``
automatically set the module that registers the FPGA region as the owner.
The FPGA region's probe function will need to get a reference to the FPGA The FPGA region's probe function will need to get a reference to the FPGA
Manager it will be using to do the programming. This usually would happen Manager it will be using to do the programming. This usually would happen
during the region's probe function. during the region's probe function.
@ -82,10 +85,10 @@ following APIs to handle building or tearing down that list.
:functions: fpga_region_info :functions: fpga_region_info
.. kernel-doc:: drivers/fpga/fpga-region.c .. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_register_full :functions: __fpga_region_register_full
.. kernel-doc:: drivers/fpga/fpga-region.c .. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_register :functions: __fpga_region_register
.. kernel-doc:: drivers/fpga/fpga-region.c .. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_unregister :functions: fpga_region_unregister

View File

@ -53,7 +53,7 @@ static struct fpga_region *fpga_region_get(struct fpga_region *region)
} }
get_device(dev); get_device(dev);
if (!try_module_get(dev->parent->driver->owner)) { if (!try_module_get(region->ops_owner)) {
put_device(dev); put_device(dev);
mutex_unlock(&region->mutex); mutex_unlock(&region->mutex);
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
@ -75,7 +75,7 @@ static void fpga_region_put(struct fpga_region *region)
dev_dbg(dev, "put\n"); dev_dbg(dev, "put\n");
module_put(dev->parent->driver->owner); module_put(region->ops_owner);
put_device(dev); put_device(dev);
mutex_unlock(&region->mutex); mutex_unlock(&region->mutex);
} }
@ -181,14 +181,16 @@ static struct attribute *fpga_region_attrs[] = {
ATTRIBUTE_GROUPS(fpga_region); ATTRIBUTE_GROUPS(fpga_region);
/** /**
* fpga_region_register_full - create and register an FPGA Region device * __fpga_region_register_full - create and register an FPGA Region device
* @parent: device parent * @parent: device parent
* @info: parameters for FPGA Region * @info: parameters for FPGA Region
* @owner: module containing the get_bridges function
* *
* Return: struct fpga_region or ERR_PTR() * Return: struct fpga_region or ERR_PTR()
*/ */
struct fpga_region * struct fpga_region *
fpga_region_register_full(struct device *parent, const struct fpga_region_info *info) __fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
struct module *owner)
{ {
struct fpga_region *region; struct fpga_region *region;
int id, ret = 0; int id, ret = 0;
@ -213,6 +215,7 @@ fpga_region_register_full(struct device *parent, const struct fpga_region_info *
region->compat_id = info->compat_id; region->compat_id = info->compat_id;
region->priv = info->priv; region->priv = info->priv;
region->get_bridges = info->get_bridges; region->get_bridges = info->get_bridges;
region->ops_owner = owner;
mutex_init(&region->mutex); mutex_init(&region->mutex);
INIT_LIST_HEAD(&region->bridge_list); INIT_LIST_HEAD(&region->bridge_list);
@ -241,13 +244,14 @@ err_free:
return ERR_PTR(ret); return ERR_PTR(ret);
} }
EXPORT_SYMBOL_GPL(fpga_region_register_full); EXPORT_SYMBOL_GPL(__fpga_region_register_full);
/** /**
* fpga_region_register - create and register an FPGA Region device * __fpga_region_register - create and register an FPGA Region device
* @parent: device parent * @parent: device parent
* @mgr: manager that programs this region * @mgr: manager that programs this region
* @get_bridges: optional function to get bridges to a list * @get_bridges: optional function to get bridges to a list
* @owner: module containing the get_bridges function
* *
* This simple version of the register function should be sufficient for most users. * This simple version of the register function should be sufficient for most users.
* The fpga_region_register_full() function is available for users that need to * The fpga_region_register_full() function is available for users that need to
@ -256,17 +260,17 @@ EXPORT_SYMBOL_GPL(fpga_region_register_full);
* Return: struct fpga_region or ERR_PTR() * Return: struct fpga_region or ERR_PTR()
*/ */
struct fpga_region * struct fpga_region *
fpga_region_register(struct device *parent, struct fpga_manager *mgr, __fpga_region_register(struct device *parent, struct fpga_manager *mgr,
int (*get_bridges)(struct fpga_region *)) int (*get_bridges)(struct fpga_region *), struct module *owner)
{ {
struct fpga_region_info info = { 0 }; struct fpga_region_info info = { 0 };
info.mgr = mgr; info.mgr = mgr;
info.get_bridges = get_bridges; info.get_bridges = get_bridges;
return fpga_region_register_full(parent, &info); return __fpga_region_register_full(parent, &info, owner);
} }
EXPORT_SYMBOL_GPL(fpga_region_register); EXPORT_SYMBOL_GPL(__fpga_region_register);
/** /**
* fpga_region_unregister - unregister an FPGA region * fpga_region_unregister - unregister an FPGA region

View File

@ -36,6 +36,7 @@ struct fpga_region_info {
* @mgr: FPGA manager * @mgr: FPGA manager
* @info: FPGA image info * @info: FPGA image info
* @compat_id: FPGA region id for compatibility check. * @compat_id: FPGA region id for compatibility check.
* @ops_owner: module containing the get_bridges function
* @priv: private data * @priv: private data
* @get_bridges: optional function to get bridges to a list * @get_bridges: optional function to get bridges to a list
*/ */
@ -46,6 +47,7 @@ struct fpga_region {
struct fpga_manager *mgr; struct fpga_manager *mgr;
struct fpga_image_info *info; struct fpga_image_info *info;
struct fpga_compat_id *compat_id; struct fpga_compat_id *compat_id;
struct module *ops_owner;
void *priv; void *priv;
int (*get_bridges)(struct fpga_region *region); int (*get_bridges)(struct fpga_region *region);
}; };
@ -58,12 +60,17 @@ fpga_region_class_find(struct device *start, const void *data,
int fpga_region_program_fpga(struct fpga_region *region); int fpga_region_program_fpga(struct fpga_region *region);
#define fpga_region_register_full(parent, info) \
__fpga_region_register_full(parent, info, THIS_MODULE)
struct fpga_region * struct fpga_region *
fpga_region_register_full(struct device *parent, const struct fpga_region_info *info); __fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
struct module *owner);
#define fpga_region_register(parent, mgr, get_bridges) \
__fpga_region_register(parent, mgr, get_bridges, THIS_MODULE)
struct fpga_region * struct fpga_region *
fpga_region_register(struct device *parent, struct fpga_manager *mgr, __fpga_region_register(struct device *parent, struct fpga_manager *mgr,
int (*get_bridges)(struct fpga_region *)); int (*get_bridges)(struct fpga_region *), struct module *owner);
void fpga_region_unregister(struct fpga_region *region); void fpga_region_unregister(struct fpga_region *region);
#endif /* _FPGA_REGION_H */ #endif /* _FPGA_REGION_H */