linux-fpga.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/3] fpga: Use standard class dev_release function
@ 2021-05-28 21:57 Russ Weight
  2021-05-28 21:57 ` [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
                   ` (3 more replies)
  0 siblings, 4 replies; 19+ messages in thread
From: Russ Weight @ 2021-05-28 21:57 UTC (permalink / raw)
  To: mdf, linux-fpga
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong,
	Russ Weight

The FPGA framework has a convention of using managed resource
functions to allow parent drivers to manage the data structures
allocated by the class drivers. They use an empty *_dev_release()
function to satisfy the class driver.

This is inconsistent with linux driver model.

These changes remove the managed resource functions and populate
the class dev_release callback functions. They also merge the
create and register functions into a single register function for
each of the fpga-mgr, fpga-region, and fpga-bridge class drivers.

For more context, refer to this email thread:

https://marc.info/?l=linux-fpga&m=162127412218557&w=2

I turned on the configs assocated with each of the modified files,
but I must have been missing some dependencies, because not all
of them compiled. I did a run-time test specifically with the
dfl-fme infrastructure. This would have exercised the region,
bridge, and fpga-mgr frameworks.

Changelog v3 -> v4:
  - Added the compat_id parameter to fpga_mgr_register() and
    devm_fpga_mgr_register() to ensure that the compat_id is set before
    the device_register() call.
  - Added the compat_id parameter to fpga_bridge_register() to ensure
    that the compat_id is set before the device_register() call.
  - Modified the dfl_fpga_feature_devs_enumerate() function to restore
    the fpga_region_register() call to the correct location.

Changelog v2 -> v3:
  - Cleaned up comment headers for fpga_mgr_register(), fpga_bridge_register(),
    and fpga_region_register().
  - Fixed error return on ida_simple_get() failure for fpga_mgr_register(),
    fpga_bridge_register(), and fpga_region_register().
  - Fixed error return value for fpga_bridge_register(): ERR_PTR(ret) instead
    of NULL.

Changelog v1 -> v2:
  - Restored devm_fpga_mgr_register() functionality to the fpga-mgr
    class driver, adapted for the combined create/register functionality.
  - All previous callers of devm_fpga_mgr_register() will continue to call
    devm_fpga_mgr_register().
  - replaced unnecessary ternary operators in return statements with
    standard if conditions.

Russ Weight (3):
  fpga: mgr: Use standard dev_release for class driver
  fpga: bridge: Use standard dev_release for class driver
  fpga: region: Use standard dev_release for class driver

 drivers/fpga/altera-cvp.c           |  12 +-
 drivers/fpga/altera-fpga2sdram.c    |  12 +-
 drivers/fpga/altera-freeze-bridge.c |  10 +-
 drivers/fpga/altera-hps2fpga.c      |  12 +-
 drivers/fpga/altera-pr-ip-core.c    |   9 +-
 drivers/fpga/altera-ps-spi.c        |  10 +-
 drivers/fpga/dfl-fme-br.c           |  10 +-
 drivers/fpga/dfl-fme-mgr.c          |  12 +-
 drivers/fpga/dfl-fme-region.c       |  12 +-
 drivers/fpga/dfl.c                  |  12 +-
 drivers/fpga/fpga-bridge.c          | 119 +++++---------------
 drivers/fpga/fpga-mgr.c             | 168 ++++++++--------------------
 drivers/fpga/fpga-region.c          | 106 ++++--------------
 drivers/fpga/ice40-spi.c            |  10 +-
 drivers/fpga/machxo2-spi.c          |  10 +-
 drivers/fpga/of-fpga-region.c       |  10 +-
 drivers/fpga/socfpga-a10.c          |  16 +--
 drivers/fpga/socfpga.c              |  10 +-
 drivers/fpga/stratix10-soc.c        |  15 +--
 drivers/fpga/ts73xx-fpga.c          |  10 +-
 drivers/fpga/xilinx-pr-decoupler.c  |  17 +--
 drivers/fpga/xilinx-spi.c           |  12 +-
 drivers/fpga/zynq-fpga.c            |  16 +--
 drivers/fpga/zynqmp-fpga.c          |  10 +-
 include/linux/fpga/fpga-bridge.h    |  12 +-
 include/linux/fpga/fpga-mgr.h       |  19 ++--
 include/linux/fpga/fpga-region.h    |  13 +--
 27 files changed, 210 insertions(+), 474 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-05-28 21:57 [PATCH v4 0/3] fpga: Use standard class dev_release function Russ Weight
@ 2021-05-28 21:57 ` Russ Weight
  2021-05-31  8:54   ` Xu Yilun
  2021-06-01 23:24   ` Tom Rix
  2021-05-28 21:57 ` [PATCH v4 2/3] fpga: bridge: " Russ Weight
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 19+ messages in thread
From: Russ Weight @ 2021-05-28 21:57 UTC (permalink / raw)
  To: mdf, linux-fpga
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong,
	Russ Weight

The FPGA manager class driver data structure is being treated as a
managed resource instead of using standard dev_release call-back
to release the class data structure. This change removes the
managed resource code for the freeing of the class data structure
and combines the create() and register() functions into a single
register() function.

The devm_fpga_mgr_register() function is retained.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v4:
  - Added the compat_id parameter to fpga_mgr_register() and
    devm_fpga_mgr_register() to ensure that the compat_id is set before
    the device_register() call.
v3:
  - Cleaned up comment header for fpga_mgr_register()
  - Fix error return on ida_simple_get() failure
v2:
  - Restored devm_fpga_mgr_register() functionality, adapted for the combined
    create/register functionality.
  - All previous callers of devm_fpga_mgr_register() will continue to call
    devm_fpga_mgr_register().
  - replaced unnecessary ternary operators in return statements with standard
    if conditions.
---
 drivers/fpga/altera-cvp.c        |  12 +--
 drivers/fpga/altera-pr-ip-core.c |   9 +-
 drivers/fpga/altera-ps-spi.c     |  10 +-
 drivers/fpga/dfl-fme-mgr.c       |  12 +--
 drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
 drivers/fpga/ice40-spi.c         |  10 +-
 drivers/fpga/machxo2-spi.c       |  10 +-
 drivers/fpga/socfpga-a10.c       |  16 ++-
 drivers/fpga/socfpga.c           |  10 +-
 drivers/fpga/stratix10-soc.c     |  15 +--
 drivers/fpga/ts73xx-fpga.c       |  10 +-
 drivers/fpga/xilinx-spi.c        |  12 +--
 drivers/fpga/zynq-fpga.c         |  16 ++-
 drivers/fpga/zynqmp-fpga.c       |  10 +-
 include/linux/fpga/fpga-mgr.h    |  19 ++--
 15 files changed, 120 insertions(+), 219 deletions(-)

diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 4e0edb60bfba..25eaa97ae7aa 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
 	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
 		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
 
-	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
-				   &altera_cvp_ops, conf);
-	if (!mgr) {
-		ret = -ENOMEM;
+	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
+				&altera_cvp_ops, conf);
+	if (IS_ERR(mgr)) {
+		ret = PTR_ERR(mgr);
 		goto err_unmap;
 	}
 
 	pci_set_drvdata(pdev, mgr);
 
-	ret = fpga_mgr_register(mgr);
-	if (ret)
-		goto err_unmap;
-
 	return 0;
 
 err_unmap:
diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
index 5b130c4d9882..bfd4de1e7ac5 100644
--- a/drivers/fpga/altera-pr-ip-core.c
+++ b/drivers/fpga/altera-pr-ip-core.c
@@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
 		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
 		(int)(val & ALT_PR_CSR_PR_START));
 
-	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
+				     &alt_pr_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 EXPORT_SYMBOL_GPL(alt_pr_register);
 
diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
index 23bfd4d1ad0f..e6fc7208213c 100644
--- a/drivers/fpga/altera-ps-spi.c
+++ b/drivers/fpga/altera-ps-spi.c
@@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
 	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
 		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
 
-	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
-				   &altera_ps_ops, conf);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
+				     &altera_ps_ops, conf);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(&spi->dev, mgr);
+	return 0;
 }
 
 static const struct spi_device_id altera_ps_spi_ids[] = {
diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
index d5861d13b306..c9b98afa7d50 100644
--- a/drivers/fpga/dfl-fme-mgr.c
+++ b/drivers/fpga/dfl-fme-mgr.c
@@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
 
 	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
 
-	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
-				   &fme_mgr_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
-
-	mgr->compat_id = compat_id;
+	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
+				     &fme_mgr_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 static struct platform_driver fme_mgr_driver = {
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index b85bc47c91a9..d6d64ce34417 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
 EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
 
 /**
- * fpga_mgr_create - create and initialize a FPGA manager struct
+ * fpga_mgr_register - create and register a FPGA Manager device
  * @dev:	fpga manager device from pdev
  * @name:	fpga manager name
+ * @compat_id:	FPGA manager id for compatibility check.
  * @mops:	pointer to structure of fpga manager ops
  * @priv:	fpga manager private data
  *
- * The caller of this function is responsible for freeing the struct with
- * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
+ * The caller of this function is responsible for calling fpga_mgr_unregister().
+ * Using devm_fpga_mgr_register instead is recommended.
  *
- * Return: pointer to struct fpga_manager or NULL
+ * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
  */
-struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
-				     const struct fpga_manager_ops *mops,
-				     void *priv)
+struct fpga_manager *
+fpga_mgr_register(struct device *dev, const char *name,
+		  struct fpga_compat_id *compat_id,
+		  const struct fpga_manager_ops *mops, void *priv)
 {
 	struct fpga_manager *mgr;
 	int id, ret;
@@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
 	    !mops->write_init || (!mops->write && !mops->write_sg) ||
 	    (mops->write && mops->write_sg)) {
 		dev_err(dev, "Attempt to register without fpga_manager_ops\n");
-		return NULL;
+		return ERR_PTR(-EINVAL);
 	}
 
 	if (!name || !strlen(name)) {
 		dev_err(dev, "Attempt to register with no name!\n");
-		return NULL;
+		return ERR_PTR(-EINVAL);
 	}
 
 	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
 	if (!mgr)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
-	if (id < 0)
+	if (id < 0) {
+		ret = id;
 		goto error_kfree;
+	}
 
 	mutex_init(&mgr->ref_mutex);
 
 	mgr->name = name;
+	mgr->compat_id = compat_id;
 	mgr->mops = mops;
 	mgr->priv = priv;
 
-	device_initialize(&mgr->dev);
 	mgr->dev.class = fpga_mgr_class;
 	mgr->dev.groups = mops->groups;
 	mgr->dev.parent = dev;
@@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
 	if (ret)
 		goto error_device;
 
-	return mgr;
-
-error_device:
-	ida_simple_remove(&fpga_mgr_ida, id);
-error_kfree:
-	kfree(mgr);
-
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(fpga_mgr_create);
-
-/**
- * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
- * @mgr:	fpga manager struct
- */
-void fpga_mgr_free(struct fpga_manager *mgr)
-{
-	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
-	kfree(mgr);
-}
-EXPORT_SYMBOL_GPL(fpga_mgr_free);
-
-static void devm_fpga_mgr_release(struct device *dev, void *res)
-{
-	struct fpga_mgr_devres *dr = res;
-
-	fpga_mgr_free(dr->mgr);
-}
-
-/**
- * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
- * @dev:	fpga manager device from pdev
- * @name:	fpga manager name
- * @mops:	pointer to structure of fpga manager ops
- * @priv:	fpga manager private data
- *
- * This function is intended for use in a FPGA manager driver's probe function.
- * After the manager driver creates the manager struct with
- * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
- * manager driver's remove function should call fpga_mgr_unregister().  The
- * manager 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_mgr_register(), the struct will still get cleaned up.
- *
- * Return: pointer to struct fpga_manager or NULL
- */
-struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
-					  const struct fpga_manager_ops *mops,
-					  void *priv)
-{
-	struct fpga_mgr_devres *dr;
-
-	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
-	if (!dr)
-		return NULL;
-
-	dr->mgr = fpga_mgr_create(dev, name, mops, priv);
-	if (!dr->mgr) {
-		devres_free(dr);
-		return NULL;
-	}
-
-	devres_add(dev, dr);
-
-	return dr->mgr;
-}
-EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
-
-/**
- * fpga_mgr_register - register a FPGA manager
- * @mgr: fpga manager struct
- *
- * Return: 0 on success, negative error code otherwise.
- */
-int fpga_mgr_register(struct fpga_manager *mgr)
-{
-	int ret;
-
 	/*
 	 * Initialize framework state by requesting low level driver read state
 	 * from device.  FPGA may be in reset mode or may have been programmed
@@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
 	 */
 	mgr->state = mgr->mops->state(mgr);
 
-	ret = device_add(&mgr->dev);
-	if (ret)
-		goto error_device;
-
-	dev_info(&mgr->dev, "%s registered\n", mgr->name);
+	ret = device_register(&mgr->dev);
+	if (ret) {
+		put_device(&mgr->dev);
+		return ERR_PTR(ret);
+	}
 
-	return 0;
+	return mgr;
 
 error_device:
-	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
+	ida_simple_remove(&fpga_mgr_ida, id);
+error_kfree:
+	kfree(mgr);
 
-	return ret;
+	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(fpga_mgr_register);
 
@@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
 }
 EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
 
-static int fpga_mgr_devres_match(struct device *dev, void *res,
-				 void *match_data)
-{
-	struct fpga_mgr_devres *dr = res;
-
-	return match_data == dr->mgr;
-}
-
 static void devm_fpga_mgr_unregister(struct device *dev, void *res)
 {
 	struct fpga_mgr_devres *dr = res;
@@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
 
 /**
  * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
- * @dev: managing device for this FPGA manager
- * @mgr: fpga manager struct
+ * @dev:	fpga manager device from pdev
+ * @name:	fpga manager name
+ * @compat_id:	FPGA manager id for compatibility check.
+ * @mops:	pointer to structure of fpga manager ops
+ * @priv:	fpga manager private data
  *
  * This is the devres variant of fpga_mgr_register() for which the unregister
  * function will be called automatically when the managing device is detached.
  */
-int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
+struct fpga_manager *
+devm_fpga_mgr_register(struct device *dev, const char *name,
+		       struct fpga_compat_id *compat_id,
+		       const struct fpga_manager_ops *mops, void *priv)
 {
 	struct fpga_mgr_devres *dr;
-	int ret;
-
-	/*
-	 * Make sure that the struct fpga_manager * that is passed in is
-	 * managed itself.
-	 */
-	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
-				 fpga_mgr_devres_match, mgr)))
-		return -EINVAL;
+	struct fpga_manager *mgr;
 
 	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
 	if (!dr)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
-	ret = fpga_mgr_register(mgr);
-	if (ret) {
+	mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
+	if (IS_ERR(mgr)) {
 		devres_free(dr);
-		return ret;
+		return mgr;
 	}
 
 	dr->mgr = mgr;
 	devres_add(dev, dr);
 
-	return 0;
+	return mgr;
 }
 EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
 
 static void fpga_mgr_dev_release(struct device *dev)
 {
+	struct fpga_manager *mgr = to_fpga_manager(dev);
+
+	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
+	kfree(mgr);
 }
 
 static int __init fpga_mgr_class_init(void)
diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
index 69dec5af23c3..b1c7c0713e11 100644
--- a/drivers/fpga/ice40-spi.c
+++ b/drivers/fpga/ice40-spi.c
@@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
 		return ret;
 	}
 
-	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
-				   &ice40_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
+				     NULL, &ice40_fpga_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 static const struct of_device_id ice40_fpga_of_match[] = {
diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
index 114a64d2b7a4..75cc6efaa1b6 100644
--- a/drivers/fpga/machxo2-spi.c
+++ b/drivers/fpga/machxo2-spi.c
@@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
 		return -EINVAL;
 	}
 
-	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
-				   &machxo2_ops, spi);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
+				     NULL, &machxo2_ops, spi);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 static const struct of_device_id of_match[] = {
diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
index 573d88bdf730..80b8891fb302 100644
--- a/drivers/fpga/socfpga-a10.c
+++ b/drivers/fpga/socfpga-a10.c
@@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
 		return -EBUSY;
 	}
 
-	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
-				   &socfpga_a10_fpga_mgr_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, mgr);
-
-	ret = fpga_mgr_register(mgr);
-	if (ret) {
+	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
+				NULL, &socfpga_a10_fpga_mgr_ops, priv);
+	if (IS_ERR(mgr)) {
 		clk_disable_unprepare(priv->clk);
-		return ret;
+		return PTR_ERR(mgr);
 	}
 
+	platform_set_drvdata(pdev, mgr);
+
 	return 0;
 }
 
diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
index 1f467173fc1f..06edfab8a496 100644
--- a/drivers/fpga/socfpga.c
+++ b/drivers/fpga/socfpga.c
@@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
-				   &socfpga_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
+				     NULL, &socfpga_fpga_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 #ifdef CONFIG_OF
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index 657a70c5fc99..434094c3ac69 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
 
 	init_completion(&priv->status_return_completion);
 
-	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
-			      &s10_ops, priv);
-	if (!mgr) {
-		dev_err(dev, "unable to create FPGA manager\n");
-		ret = -ENOMEM;
-		goto probe_err;
-	}
-
-	ret = fpga_mgr_register(mgr);
-	if (ret) {
+	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
+				NULL, &s10_ops, priv);
+	if (IS_ERR(mgr)) {
 		dev_err(dev, "unable to register FPGA manager\n");
-		fpga_mgr_free(mgr);
+		ret = PTR_ERR(mgr);
 		goto probe_err;
 	}
 
diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
index 101f016c6ed8..5efbec2b9f9f 100644
--- a/drivers/fpga/ts73xx-fpga.c
+++ b/drivers/fpga/ts73xx-fpga.c
@@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
 	if (IS_ERR(priv->io_base))
 		return PTR_ERR(priv->io_base);
 
-	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
-				   &ts73xx_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
+				     &ts73xx_fpga_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(kdev, mgr);
+	return 0;
 }
 
 static struct platform_driver ts73xx_fpga_driver = {
diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
index fee4d0abf6bf..6f1894929687 100644
--- a/drivers/fpga/xilinx-spi.c
+++ b/drivers/fpga/xilinx-spi.c
@@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
 		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
 				     "Failed to get DONE gpio\n");
 
-	mgr = devm_fpga_mgr_create(&spi->dev,
-				   "Xilinx Slave Serial FPGA Manager",
-				   &xilinx_spi_ops, conf);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(&spi->dev,
+				     "Xilinx Slave Serial FPGA Manager",
+				     NULL, &xilinx_spi_ops, conf);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(&spi->dev, mgr);
+	return 0;
 }
 
 static const struct of_device_id xlnx_spi_of_match[] = {
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index 07fa8d9ec675..74bbb9710f4e 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
 
 	clk_disable(priv->clk);
 
-	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
-				   &zynq_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, mgr);
-
-	err = fpga_mgr_register(mgr);
-	if (err) {
+	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
+				&zynq_fpga_ops, priv);
+	if (IS_ERR(mgr)) {
 		dev_err(dev, "unable to register FPGA manager\n");
 		clk_unprepare(priv->clk);
-		return err;
+		return PTR_ERR(mgr);
 	}
 
+	platform_set_drvdata(pdev, mgr);
+
 	return 0;
 }
 
diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
index 125743c9797f..1a6d520985be 100644
--- a/drivers/fpga/zynqmp-fpga.c
+++ b/drivers/fpga/zynqmp-fpga.c
@@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
 
 	priv->dev = dev;
 
-	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
-				   &zynqmp_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
+				     &zynqmp_fpga_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 static const struct of_device_id zynqmp_fpga_of_match[] = {
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 2bc3030a69e5..3e1859cc0640 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
 
 void fpga_mgr_put(struct fpga_manager *mgr);
 
-struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
-				     const struct fpga_manager_ops *mops,
-				     void *priv);
-void fpga_mgr_free(struct fpga_manager *mgr);
-int fpga_mgr_register(struct fpga_manager *mgr);
+struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
+				       struct fpga_compat_id *compat_id,
+				       const struct fpga_manager_ops *mops,
+				       void *priv);
 void fpga_mgr_unregister(struct fpga_manager *mgr);
 
-int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
-
-struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
-					  const struct fpga_manager_ops *mops,
-					  void *priv);
+struct fpga_manager *
+devm_fpga_mgr_register(struct device *dev, const char *name,
+		       struct fpga_compat_id *compat_id,
+		       const struct fpga_manager_ops *mops,
+		       void *priv);
 
 #endif /*_LINUX_FPGA_MGR_H */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v4 2/3] fpga: bridge: Use standard dev_release for class driver
  2021-05-28 21:57 [PATCH v4 0/3] fpga: Use standard class dev_release function Russ Weight
  2021-05-28 21:57 ` [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
@ 2021-05-28 21:57 ` Russ Weight
  2021-05-28 21:57 ` [PATCH v4 3/3] fpga: region: " Russ Weight
  2021-05-31 15:24 ` [PATCH v4 0/3] fpga: Use standard class dev_release function Xu Yilun
  3 siblings, 0 replies; 19+ messages in thread
From: Russ Weight @ 2021-05-28 21:57 UTC (permalink / raw)
  To: mdf, linux-fpga
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong,
	Russ Weight

The FPGA bridge class driver data structure is being treated as a
managed resource instead of using standard dev_release call-back
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>
---
v4:
  - Restore the previous format for the Return value in the comment header
    for fpga_bridge_register()
v3:
  - Cleaned up comment header for fpga_bridge_register()
  - Fix error return values for fpga_bridge_register()
v2:
  - No changes
---
 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          | 119 ++++++----------------------
 drivers/fpga/xilinx-pr-decoupler.c  |  17 ++--
 include/linux/fpga/fpga-bridge.h    |  12 +--
 7 files changed, 52 insertions(+), 140 deletions(-)

diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c
index a78e49c63c64..66063507116b 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(mgr);
 
 	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 e9266b2a357f..3d5026244c7c 100644
--- a/drivers/fpga/fpga-bridge.c
+++ b/drivers/fpga/fpga-bridge.c
@@ -312,36 +312,35 @@ 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 a FPGA Bridge device
  * @dev:	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 on success, or ERR_PTR() on error.
  */
-struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
-				       const struct fpga_bridge_ops *br_ops,
-				       void *priv)
+struct fpga_bridge *fpga_bridge_register(struct device *dev, const char *name,
+					 const struct fpga_bridge_ops *br_ops,
+					 void *priv)
 {
 	struct fpga_bridge *bridge;
 	int id, ret;
 
 	if (!name || !strlen(name)) {
 		dev_err(dev, "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,7 +349,6 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, 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 = dev;
@@ -361,6 +359,16 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
 	if (ret)
 		goto error_device;
 
+	ret = device_register(&bridge->dev);
+	if (ret) {
+		put_device(&bridge->dev);
+		return ERR_PTR(ret);
+	}
+
+	of_platform_populate(dev->of_node, NULL, NULL, dev);
+
+	dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name);
+
 	return bridge;
 
 error_device:
@@ -368,88 +376,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, 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
- * @dev:	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 *dev, 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(dev, name, br_ops, priv);
-	if (!bridge) {
-		devres_free(ptr);
-	} else {
-		*ptr = bridge;
-		devres_add(dev, 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 +402,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 ea2bde6e5bc4..c004e52b9464 100644
--- a/drivers/fpga/xilinx-pr-decoupler.c
+++ b/drivers/fpga/xilinx-pr-decoupler.c
@@ -138,22 +138,17 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
 
 	clk_disable(priv->clk);
 
-	br = devm_fpga_bridge_create(&pdev->dev, priv->ipconfig->name,
-				     &xlnx_pr_decoupler_br_ops, priv);
-	if (!br) {
-		err = -ENOMEM;
-		goto err_clk;
-	}
-
-	platform_set_drvdata(pdev, br);
-
-	err = fpga_bridge_register(br);
-	if (err) {
+	br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
+				  &xlnx_pr_decoupler_br_ops, priv);
+	if (IS_ERR(br)) {
+		err = PTR_ERR(br);
 		dev_err(&pdev->dev, "unable to register %s",
 			priv->ipconfig->name);
 		goto err_clk;
 	}
 
+	platform_set_drvdata(pdev, br);
+
 	return 0;
 
 err_clk:
diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
index 817600a32c93..86f306c99fa5 100644
--- a/include/linux/fpga/fpga-bridge.h
+++ b/include/linux/fpga/fpga-bridge.h
@@ -62,15 +62,9 @@ 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 *dev, 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 */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v4 3/3] fpga: region: Use standard dev_release for class driver
  2021-05-28 21:57 [PATCH v4 0/3] fpga: Use standard class dev_release function Russ Weight
  2021-05-28 21:57 ` [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
  2021-05-28 21:57 ` [PATCH v4 2/3] fpga: bridge: " Russ Weight
@ 2021-05-28 21:57 ` Russ Weight
  2021-05-31 15:23   ` Xu Yilun
  2021-05-31 15:24 ` [PATCH v4 0/3] fpga: Use standard class dev_release function Xu Yilun
  3 siblings, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-05-28 21:57 UTC (permalink / raw)
  To: mdf, linux-fpga
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong,
	Russ Weight

The FPGA region class driver data structure is being treated as a
managed resource instead of using standard dev_release call-back
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>
---
v4:
  - Added the compat_id parameter to fpga_bridge_register() to ensure
    that the compat_id is set before the device_register() call.
  - Modified the dfl_fpga_feature_devs_enumerate() function to restore
    the fpga_region_register() call to the correct location.
v3:
  - Cleaned up comment header for fpga_region_register()
  - Fix fpga_region_register() error return on ida_simple_get() failure
v2:
  - No changes
---
 drivers/fpga/dfl-fme-region.c    |  12 ++--
 drivers/fpga/dfl.c               |  12 ++--
 drivers/fpga/fpga-region.c       | 106 +++++++------------------------
 drivers/fpga/of-fpga-region.c    |  10 +--
 include/linux/fpga/fpga-region.h |  13 ++--
 5 files changed, 38 insertions(+), 115 deletions(-)

diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
index 1eeb42af1012..9e44c623a5e9 100644
--- a/drivers/fpga/dfl-fme-region.c
+++ b/drivers/fpga/dfl-fme-region.c
@@ -39,20 +39,16 @@ static int fme_region_probe(struct platform_device *pdev)
 	if (IS_ERR(mgr))
 		return -EPROBE_DEFER;
 
-	region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges);
-	if (!region) {
-		ret = -ENOMEM;
+	region = fpga_region_register(dev, mgr, mgr->compat_id,
+				      fme_region_get_bridges);
+	if (IS_ERR(region)) {
+		ret = PTR_ERR(region);
 		goto eprobe_mgr_put;
 	}
 
 	region->priv = pdata;
-	region->compat_id = mgr->compat_id;
 	platform_set_drvdata(pdev, region);
 
-	ret = fpga_region_register(region);
-	if (ret)
-		goto eprobe_mgr_put;
-
 	dev_dbg(dev, "DFL FME FPGA Region probed\n");
 
 	return 0;
diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index 511b20ff35a3..6e628c07c1dc 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -1400,19 +1400,15 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
 	if (!cdev)
 		return ERR_PTR(-ENOMEM);
 
-	cdev->region = devm_fpga_region_create(info->dev, NULL, NULL);
-	if (!cdev->region) {
-		ret = -ENOMEM;
-		goto free_cdev_exit;
-	}
-
 	cdev->parent = info->dev;
 	mutex_init(&cdev->lock);
 	INIT_LIST_HEAD(&cdev->port_dev_list);
 
-	ret = fpga_region_register(cdev->region);
-	if (ret)
+	cdev->region = fpga_region_register(info->dev, NULL, NULL, NULL);
+	if (IS_ERR(cdev->region)) {
+		ret = PTR_ERR(cdev->region);
 		goto free_cdev_exit;
+	}
 
 	/* create and init build info for enumeration */
 	binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index c3134b89c3fe..3d28880bf1cc 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -180,39 +180,38 @@ static struct attribute *fpga_region_attrs[] = {
 ATTRIBUTE_GROUPS(fpga_region);
 
 /**
- * fpga_region_create - alloc and init a struct fpga_region
+ * fpga_region_register - create and register a FPGA Region device
  * @dev: device parent
  * @mgr: manager that programs this region
+ * @compat_id: FPGA manager id for compatibility check.
  * @get_bridges: optional function to get bridges to a list
  *
- * The caller of this function is responsible for freeing the resulting region
- * struct with fpga_region_free().  Using devm_fpga_region_create() instead is
- * recommended.
- *
- * Return: struct fpga_region or NULL
+ * Return struct fpga_region pointer on success, or ERR_PTR() on error.
  */
-struct fpga_region
-*fpga_region_create(struct device *dev,
-		    struct fpga_manager *mgr,
-		    int (*get_bridges)(struct fpga_region *))
+struct fpga_region *
+fpga_region_register(struct device *dev, struct fpga_manager *mgr,
+		     struct fpga_compat_id *compat_id,
+		     int (*get_bridges)(struct fpga_region *))
 {
 	struct fpga_region *region;
 	int id, ret = 0;
 
 	region = kzalloc(sizeof(*region), GFP_KERNEL);
 	if (!region)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
-	if (id < 0)
+	if (id < 0) {
+		ret = id;
 		goto err_free;
+	}
 
 	region->mgr = mgr;
+	region->compat_id = compat_id;
 	region->get_bridges = get_bridges;
 	mutex_init(&region->mutex);
 	INIT_LIST_HEAD(&region->bridge_list);
 
-	device_initialize(&region->dev);
 	region->dev.class = fpga_region_class;
 	region->dev.parent = dev;
 	region->dev.of_node = dev->of_node;
@@ -222,6 +221,12 @@ struct fpga_region
 	if (ret)
 		goto err_remove;
 
+	ret = device_register(&region->dev);
+	if (ret) {
+		put_device(&region->dev);
+		return ERR_PTR(ret);
+	}
+
 	return region;
 
 err_remove:
@@ -229,76 +234,7 @@ struct fpga_region
 err_free:
 	kfree(region);
 
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(fpga_region_create);
-
-/**
- * fpga_region_free - free a FPGA region created by fpga_region_create()
- * @region: FPGA region
- */
-void fpga_region_free(struct fpga_region *region)
-{
-	ida_simple_remove(&fpga_region_ida, region->dev.id);
-	kfree(region);
-}
-EXPORT_SYMBOL_GPL(fpga_region_free);
-
-static void devm_fpga_region_release(struct device *dev, void *res)
-{
-	struct fpga_region *region = *(struct fpga_region **)res;
-
-	fpga_region_free(region);
-}
-
-/**
- * devm_fpga_region_create - create and initialize a managed FPGA region struct
- * @dev: device parent
- * @mgr: manager that programs this region
- * @get_bridges: optional function to get bridges to a list
- *
- * This function is intended for use in a FPGA region driver's probe function.
- * After the region driver creates the region struct with
- * devm_fpga_region_create(), it should register it with fpga_region_register().
- * The region driver's remove function should call fpga_region_unregister().
- * The region 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_region_register(), the struct will still get cleaned up.
- *
- * Return: struct fpga_region or NULL
- */
-struct fpga_region
-*devm_fpga_region_create(struct device *dev,
-			 struct fpga_manager *mgr,
-			 int (*get_bridges)(struct fpga_region *))
-{
-	struct fpga_region **ptr, *region;
-
-	ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL);
-	if (!ptr)
-		return NULL;
-
-	region = fpga_region_create(dev, mgr, get_bridges);
-	if (!region) {
-		devres_free(ptr);
-	} else {
-		*ptr = region;
-		devres_add(dev, ptr);
-	}
-
-	return region;
-}
-EXPORT_SYMBOL_GPL(devm_fpga_region_create);
-
-/**
- * fpga_region_register - register a FPGA region
- * @region: FPGA region
- *
- * Return: 0 or -errno
- */
-int fpga_region_register(struct fpga_region *region)
-{
-	return device_add(&region->dev);
+	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(fpga_region_register);
 
@@ -316,6 +252,10 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister);
 
 static void fpga_region_dev_release(struct device *dev)
 {
+	struct fpga_region *region = to_fpga_region(dev);
+
+	ida_simple_remove(&fpga_region_ida, region->dev.id);
+	kfree(region);
 }
 
 /**
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
index e405309baadc..c115e5fc0c62 100644
--- a/drivers/fpga/of-fpga-region.c
+++ b/drivers/fpga/of-fpga-region.c
@@ -405,16 +405,12 @@ static int of_fpga_region_probe(struct platform_device *pdev)
 	if (IS_ERR(mgr))
 		return -EPROBE_DEFER;
 
-	region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges);
-	if (!region) {
-		ret = -ENOMEM;
+	region = fpga_region_register(dev, mgr, NULL, of_fpga_region_get_bridges);
+	if (IS_ERR(region)) {
+		ret = PTR_ERR(region);
 		goto eprobe_mgr_put;
 	}
 
-	ret = fpga_region_register(region);
-	if (ret)
-		goto eprobe_mgr_put;
-
 	of_platform_populate(np, fpga_region_of_match, NULL, &region->dev);
 	platform_set_drvdata(pdev, region);
 
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
index 27cb706275db..1e4c2dc7a63b 100644
--- a/include/linux/fpga/fpga-region.h
+++ b/include/linux/fpga/fpga-region.h
@@ -37,15 +37,10 @@ struct fpga_region *fpga_region_class_find(
 
 int fpga_region_program_fpga(struct fpga_region *region);
 
-struct fpga_region
-*fpga_region_create(struct device *dev, struct fpga_manager *mgr,
-		    int (*get_bridges)(struct fpga_region *));
-void fpga_region_free(struct fpga_region *region);
-int fpga_region_register(struct fpga_region *region);
+struct fpga_region *
+fpga_region_register(struct device *dev, struct fpga_manager *mgr,
+		     struct fpga_compat_id *compat_id,
+		     int (*get_bridges)(struct fpga_region *));
 void fpga_region_unregister(struct fpga_region *region);
 
-struct fpga_region
-*devm_fpga_region_create(struct device *dev, struct fpga_manager *mgr,
-			int (*get_bridges)(struct fpga_region *));
-
 #endif /* _FPGA_REGION_H */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-05-28 21:57 ` [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
@ 2021-05-31  8:54   ` Xu Yilun
  2021-06-03 21:21     ` Russ Weight
  2021-06-01 23:24   ` Tom Rix
  1 sibling, 1 reply; 19+ messages in thread
From: Xu Yilun @ 2021-05-31  8:54 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong

A quick comment, may come back later. See inline:

On Fri, May 28, 2021 at 02:57:53PM -0700, Russ Weight wrote:
> The FPGA manager class driver data structure is being treated as a
> managed resource instead of using standard dev_release call-back
> to release the class data structure. This change removes the
> managed resource code for the freeing of the class data structure
> and combines the create() and register() functions into a single
> register() function.
> 
> The devm_fpga_mgr_register() function is retained.
> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v4:
>   - Added the compat_id parameter to fpga_mgr_register() and
>     devm_fpga_mgr_register() to ensure that the compat_id is set before
>     the device_register() call.
> v3:
>   - Cleaned up comment header for fpga_mgr_register()
>   - Fix error return on ida_simple_get() failure
> v2:
>   - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>     create/register functionality.
>   - All previous callers of devm_fpga_mgr_register() will continue to call
>     devm_fpga_mgr_register().
>   - replaced unnecessary ternary operators in return statements with standard
>     if conditions.
> ---
>  drivers/fpga/altera-cvp.c        |  12 +--
>  drivers/fpga/altera-pr-ip-core.c |   9 +-
>  drivers/fpga/altera-ps-spi.c     |  10 +-
>  drivers/fpga/dfl-fme-mgr.c       |  12 +--
>  drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>  drivers/fpga/ice40-spi.c         |  10 +-
>  drivers/fpga/machxo2-spi.c       |  10 +-
>  drivers/fpga/socfpga-a10.c       |  16 ++-
>  drivers/fpga/socfpga.c           |  10 +-
>  drivers/fpga/stratix10-soc.c     |  15 +--
>  drivers/fpga/ts73xx-fpga.c       |  10 +-
>  drivers/fpga/xilinx-spi.c        |  12 +--
>  drivers/fpga/zynq-fpga.c         |  16 ++-
>  drivers/fpga/zynqmp-fpga.c       |  10 +-
>  include/linux/fpga/fpga-mgr.h    |  19 ++--
>  15 files changed, 120 insertions(+), 219 deletions(-)
> 
> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> index 4e0edb60bfba..25eaa97ae7aa 100644
> --- a/drivers/fpga/altera-cvp.c
> +++ b/drivers/fpga/altera-cvp.c
> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>  		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
>  
> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> -				   &altera_cvp_ops, conf);
> -	if (!mgr) {
> -		ret = -ENOMEM;
> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
> +				&altera_cvp_ops, conf);
> +	if (IS_ERR(mgr)) {
> +		ret = PTR_ERR(mgr);
>  		goto err_unmap;
>  	}
>  
>  	pci_set_drvdata(pdev, mgr);
>  
> -	ret = fpga_mgr_register(mgr);
> -	if (ret)
> -		goto err_unmap;
> -
>  	return 0;
>  
>  err_unmap:
> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
> index 5b130c4d9882..bfd4de1e7ac5 100644
> --- a/drivers/fpga/altera-pr-ip-core.c
> +++ b/drivers/fpga/altera-pr-ip-core.c
> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>  		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>  		(int)(val & ALT_PR_CSR_PR_START));
>  
> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
> +				     &alt_pr_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  EXPORT_SYMBOL_GPL(alt_pr_register);
>  
> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> index 23bfd4d1ad0f..e6fc7208213c 100644
> --- a/drivers/fpga/altera-ps-spi.c
> +++ b/drivers/fpga/altera-ps-spi.c
> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>  		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
>  
> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> -				   &altera_ps_ops, conf);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
> +				     &altera_ps_ops, conf);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> +	return 0;
>  }
>  
>  static const struct spi_device_id altera_ps_spi_ids[] = {
> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> index d5861d13b306..c9b98afa7d50 100644
> --- a/drivers/fpga/dfl-fme-mgr.c
> +++ b/drivers/fpga/dfl-fme-mgr.c
> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>  
>  	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>  
> -	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
> -				   &fme_mgr_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> -
> -	mgr->compat_id = compat_id;
> +	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
> +				     &fme_mgr_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  static struct platform_driver fme_mgr_driver = {
> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> index b85bc47c91a9..d6d64ce34417 100644
> --- a/drivers/fpga/fpga-mgr.c
> +++ b/drivers/fpga/fpga-mgr.c
> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>  EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>  
>  /**
> - * fpga_mgr_create - create and initialize a FPGA manager struct
> + * fpga_mgr_register - create and register a FPGA Manager device
>   * @dev:	fpga manager device from pdev
>   * @name:	fpga manager name
> + * @compat_id:	FPGA manager id for compatibility check.
>   * @mops:	pointer to structure of fpga manager ops
>   * @priv:	fpga manager private data
>   *
> - * The caller of this function is responsible for freeing the struct with
> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
> + * The caller of this function is responsible for calling fpga_mgr_unregister().
> + * Using devm_fpga_mgr_register instead is recommended.
>   *
> - * Return: pointer to struct fpga_manager or NULL
> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>   */
> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> -				     const struct fpga_manager_ops *mops,
> -				     void *priv)
> +struct fpga_manager *
> +fpga_mgr_register(struct device *dev, const char *name,
> +		  struct fpga_compat_id *compat_id,
> +		  const struct fpga_manager_ops *mops, void *priv)
>  {
>  	struct fpga_manager *mgr;
>  	int id, ret;
> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>  	    !mops->write_init || (!mops->write && !mops->write_sg) ||
>  	    (mops->write && mops->write_sg)) {
>  		dev_err(dev, "Attempt to register without fpga_manager_ops\n");
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>  	}
>  
>  	if (!name || !strlen(name)) {
>  		dev_err(dev, "Attempt to register with no name!\n");
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>  	}
>  
>  	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>  	if (!mgr)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>  
>  	id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
> -	if (id < 0)
> +	if (id < 0) {
> +		ret = id;
>  		goto error_kfree;
> +	}
>  
>  	mutex_init(&mgr->ref_mutex);
>  
>  	mgr->name = name;
> +	mgr->compat_id = compat_id;
>  	mgr->mops = mops;
>  	mgr->priv = priv;
>  
> -	device_initialize(&mgr->dev);
>  	mgr->dev.class = fpga_mgr_class;
>  	mgr->dev.groups = mops->groups;
>  	mgr->dev.parent = dev;
> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>  	if (ret)
>  		goto error_device;
>  
> -	return mgr;
> -
> -error_device:
> -	ida_simple_remove(&fpga_mgr_ida, id);
> -error_kfree:
> -	kfree(mgr);
> -
> -	return NULL;
> -}
> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
> -
> -/**
> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
> - * @mgr:	fpga manager struct
> - */
> -void fpga_mgr_free(struct fpga_manager *mgr)
> -{
> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> -	kfree(mgr);
> -}
> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
> -
> -static void devm_fpga_mgr_release(struct device *dev, void *res)
> -{
> -	struct fpga_mgr_devres *dr = res;
> -
> -	fpga_mgr_free(dr->mgr);
> -}
> -
> -/**
> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
> - * @dev:	fpga manager device from pdev
> - * @name:	fpga manager name
> - * @mops:	pointer to structure of fpga manager ops
> - * @priv:	fpga manager private data
> - *
> - * This function is intended for use in a FPGA manager driver's probe function.
> - * After the manager driver creates the manager struct with
> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
> - * manager driver's remove function should call fpga_mgr_unregister().  The
> - * manager 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_mgr_register(), the struct will still get cleaned up.
> - *
> - * Return: pointer to struct fpga_manager or NULL
> - */
> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> -					  const struct fpga_manager_ops *mops,
> -					  void *priv)
> -{
> -	struct fpga_mgr_devres *dr;
> -
> -	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
> -	if (!dr)
> -		return NULL;
> -
> -	dr->mgr = fpga_mgr_create(dev, name, mops, priv);
> -	if (!dr->mgr) {
> -		devres_free(dr);
> -		return NULL;
> -	}
> -
> -	devres_add(dev, dr);
> -
> -	return dr->mgr;
> -}
> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
> -
> -/**
> - * fpga_mgr_register - register a FPGA manager
> - * @mgr: fpga manager struct
> - *
> - * Return: 0 on success, negative error code otherwise.
> - */
> -int fpga_mgr_register(struct fpga_manager *mgr)
> -{
> -	int ret;
> -
>  	/*
>  	 * Initialize framework state by requesting low level driver read state
>  	 * from device.  FPGA may be in reset mode or may have been programmed
> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>  	 */
>  	mgr->state = mgr->mops->state(mgr);
>  
> -	ret = device_add(&mgr->dev);
> -	if (ret)
> -		goto error_device;
> -
> -	dev_info(&mgr->dev, "%s registered\n", mgr->name);
> +	ret = device_register(&mgr->dev);
> +	if (ret) {
> +		put_device(&mgr->dev);
> +		return ERR_PTR(ret);
> +	}
>  
> -	return 0;
> +	return mgr;
>  
>  error_device:
> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> +	ida_simple_remove(&fpga_mgr_ida, id);
> +error_kfree:
> +	kfree(mgr);
>  
> -	return ret;
> +	return ERR_PTR(ret);
>  }
>  EXPORT_SYMBOL_GPL(fpga_mgr_register);
>  
> @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>  }
>  EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>  
> -static int fpga_mgr_devres_match(struct device *dev, void *res,
> -				 void *match_data)
> -{
> -	struct fpga_mgr_devres *dr = res;
> -
> -	return match_data == dr->mgr;
> -}
> -
>  static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>  {
>  	struct fpga_mgr_devres *dr = res;
> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>  
>  /**
>   * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
> - * @dev: managing device for this FPGA manager
> - * @mgr: fpga manager struct
> + * @dev:	fpga manager device from pdev
> + * @name:	fpga manager name
> + * @compat_id:	FPGA manager id for compatibility check.
> + * @mops:	pointer to structure of fpga manager ops
> + * @priv:	fpga manager private data
>   *
>   * This is the devres variant of fpga_mgr_register() for which the unregister
>   * function will be called automatically when the managing device is detached.
>   */
> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
> +struct fpga_manager *
> +devm_fpga_mgr_register(struct device *dev, const char *name,
> +		       struct fpga_compat_id *compat_id,
> +		       const struct fpga_manager_ops *mops, void *priv)
>  {
>  	struct fpga_mgr_devres *dr;
> -	int ret;
> -
> -	/*
> -	 * Make sure that the struct fpga_manager * that is passed in is
> -	 * managed itself.
> -	 */
> -	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
> -				 fpga_mgr_devres_match, mgr)))
> -		return -EINVAL;
> +	struct fpga_manager *mgr;
>  
>  	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>  	if (!dr)
> -		return -ENOMEM;
> +		return ERR_PTR(-ENOMEM);
>  
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
> +	if (IS_ERR(mgr)) {
>  		devres_free(dr);
> -		return ret;
> +		return mgr;
>  	}
>  
>  	dr->mgr = mgr;
>  	devres_add(dev, dr);
>  
> -	return 0;
> +	return mgr;
>  }
>  EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>  
>  static void fpga_mgr_dev_release(struct device *dev)
>  {
> +	struct fpga_manager *mgr = to_fpga_manager(dev);
> +
> +	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> +	kfree(mgr);
>  }
>  
>  static int __init fpga_mgr_class_init(void)
> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> index 69dec5af23c3..b1c7c0713e11 100644
> --- a/drivers/fpga/ice40-spi.c
> +++ b/drivers/fpga/ice40-spi.c
> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>  		return ret;
>  	}
>  
> -	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
> -				   &ice40_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> +				     NULL, &ice40_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  static const struct of_device_id ice40_fpga_of_match[] = {
> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
> index 114a64d2b7a4..75cc6efaa1b6 100644
> --- a/drivers/fpga/machxo2-spi.c
> +++ b/drivers/fpga/machxo2-spi.c
> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>  		return -EINVAL;
>  	}
>  
> -	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
> -				   &machxo2_ops, spi);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
> +				     NULL, &machxo2_ops, spi);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  static const struct of_device_id of_match[] = {
> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
> index 573d88bdf730..80b8891fb302 100644
> --- a/drivers/fpga/socfpga-a10.c
> +++ b/drivers/fpga/socfpga-a10.c
> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>  		return -EBUSY;
>  	}
>  
> -	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
> -				   &socfpga_a10_fpga_mgr_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> -
> -	platform_set_drvdata(pdev, mgr);
> -
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
> +				NULL, &socfpga_a10_fpga_mgr_ops, priv);
> +	if (IS_ERR(mgr)) {
>  		clk_disable_unprepare(priv->clk);
> -		return ret;
> +		return PTR_ERR(mgr);
>  	}
>  
> +	platform_set_drvdata(pdev, mgr);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
> index 1f467173fc1f..06edfab8a496 100644
> --- a/drivers/fpga/socfpga.c
> +++ b/drivers/fpga/socfpga.c
> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> -	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
> -				   &socfpga_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
> +				     NULL, &socfpga_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  #ifdef CONFIG_OF
> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> index 657a70c5fc99..434094c3ac69 100644
> --- a/drivers/fpga/stratix10-soc.c
> +++ b/drivers/fpga/stratix10-soc.c
> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>  
>  	init_completion(&priv->status_return_completion);
>  
> -	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
> -			      &s10_ops, priv);
> -	if (!mgr) {
> -		dev_err(dev, "unable to create FPGA manager\n");
> -		ret = -ENOMEM;
> -		goto probe_err;
> -	}
> -
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
> +				NULL, &s10_ops, priv);
> +	if (IS_ERR(mgr)) {
>  		dev_err(dev, "unable to register FPGA manager\n");
> -		fpga_mgr_free(mgr);
> +		ret = PTR_ERR(mgr);
>  		goto probe_err;
>  	}
>  
> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
> index 101f016c6ed8..5efbec2b9f9f 100644
> --- a/drivers/fpga/ts73xx-fpga.c
> +++ b/drivers/fpga/ts73xx-fpga.c
> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>  	if (IS_ERR(priv->io_base))
>  		return PTR_ERR(priv->io_base);
>  
> -	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
> -				   &ts73xx_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
> +				     &ts73xx_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(kdev, mgr);
> +	return 0;
>  }
>  
>  static struct platform_driver ts73xx_fpga_driver = {
> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
> index fee4d0abf6bf..6f1894929687 100644
> --- a/drivers/fpga/xilinx-spi.c
> +++ b/drivers/fpga/xilinx-spi.c
> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>  		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>  				     "Failed to get DONE gpio\n");
>  
> -	mgr = devm_fpga_mgr_create(&spi->dev,
> -				   "Xilinx Slave Serial FPGA Manager",
> -				   &xilinx_spi_ops, conf);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(&spi->dev,
> +				     "Xilinx Slave Serial FPGA Manager",
> +				     NULL, &xilinx_spi_ops, conf);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> +	return 0;
>  }
>  
>  static const struct of_device_id xlnx_spi_of_match[] = {
> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
> index 07fa8d9ec675..74bbb9710f4e 100644
> --- a/drivers/fpga/zynq-fpga.c
> +++ b/drivers/fpga/zynq-fpga.c
> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>  
>  	clk_disable(priv->clk);
>  
> -	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
> -				   &zynq_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> -
> -	platform_set_drvdata(pdev, mgr);
> -
> -	err = fpga_mgr_register(mgr);
> -	if (err) {
> +	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
> +				&zynq_fpga_ops, priv);
> +	if (IS_ERR(mgr)) {
>  		dev_err(dev, "unable to register FPGA manager\n");
>  		clk_unprepare(priv->clk);
> -		return err;
> +		return PTR_ERR(mgr);
>  	}
>  
> +	platform_set_drvdata(pdev, mgr);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
> index 125743c9797f..1a6d520985be 100644
> --- a/drivers/fpga/zynqmp-fpga.c
> +++ b/drivers/fpga/zynqmp-fpga.c
> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>  
>  	priv->dev = dev;
>  
> -	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
> -				   &zynqmp_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
> +				     &zynqmp_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  static const struct of_device_id zynqmp_fpga_of_match[] = {
> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> index 2bc3030a69e5..3e1859cc0640 100644
> --- a/include/linux/fpga/fpga-mgr.h
> +++ b/include/linux/fpga/fpga-mgr.h
> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>  
>  void fpga_mgr_put(struct fpga_manager *mgr);
>  
> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> -				     const struct fpga_manager_ops *mops,
> -				     void *priv);
> -void fpga_mgr_free(struct fpga_manager *mgr);
> -int fpga_mgr_register(struct fpga_manager *mgr);
> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
> +				       struct fpga_compat_id *compat_id,
> +				       const struct fpga_manager_ops *mops,
> +				       void *priv);
>  void fpga_mgr_unregister(struct fpga_manager *mgr);
>  
> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
> -
> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> -					  const struct fpga_manager_ops *mops,
> -					  void *priv);
> +struct fpga_manager *
> +devm_fpga_mgr_register(struct device *dev, const char *name,
> +		       struct fpga_compat_id *compat_id,

Could we integrate the compat_id in fpga_manager_ops? Actually not much
driver cares about the compat_id.
In this way we don't force everyone fill a NULL on registration.

Expecting more opinions.

Thanks,
Yilun

> +		       const struct fpga_manager_ops *mops,
> +		       void *priv);
>  
>  #endif /*_LINUX_FPGA_MGR_H */
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 3/3] fpga: region: Use standard dev_release for class driver
  2021-05-28 21:57 ` [PATCH v4 3/3] fpga: region: " Russ Weight
@ 2021-05-31 15:23   ` Xu Yilun
  2021-06-03 22:55     ` Russ Weight
  0 siblings, 1 reply; 19+ messages in thread
From: Xu Yilun @ 2021-05-31 15:23 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong

On Fri, May 28, 2021 at 02:57:55PM -0700, Russ Weight wrote:
> The FPGA region class driver data structure is being treated as a
> managed resource instead of using standard dev_release call-back
> 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>
> ---
> v4:
>   - Added the compat_id parameter to fpga_bridge_register() to ensure

A typo                           ...   fpga_region_register() ...

>     that the compat_id is set before the device_register() call.
>   - Modified the dfl_fpga_feature_devs_enumerate() function to restore
>     the fpga_region_register() call to the correct location.
> v3:
>   - Cleaned up comment header for fpga_region_register()
>   - Fix fpga_region_register() error return on ida_simple_get() failure
> v2:
>   - No changes
> ---
>  drivers/fpga/dfl-fme-region.c    |  12 ++--
>  drivers/fpga/dfl.c               |  12 ++--
>  drivers/fpga/fpga-region.c       | 106 +++++++------------------------
>  drivers/fpga/of-fpga-region.c    |  10 +--
>  include/linux/fpga/fpga-region.h |  13 ++--
>  5 files changed, 38 insertions(+), 115 deletions(-)
> 
> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
> index 1eeb42af1012..9e44c623a5e9 100644
> --- a/drivers/fpga/dfl-fme-region.c
> +++ b/drivers/fpga/dfl-fme-region.c
> @@ -39,20 +39,16 @@ static int fme_region_probe(struct platform_device *pdev)
>  	if (IS_ERR(mgr))
>  		return -EPROBE_DEFER;
>  
> -	region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges);
> -	if (!region) {
> -		ret = -ENOMEM;
> +	region = fpga_region_register(dev, mgr, mgr->compat_id,
> +				      fme_region_get_bridges);
> +	if (IS_ERR(region)) {
> +		ret = PTR_ERR(region);
>  		goto eprobe_mgr_put;
>  	}
>  
>  	region->priv = pdata;
> -	region->compat_id = mgr->compat_id;

I think the region->priv has the same issue as compat_id, could we
introduce a fpga_region_ops for fpga_region_register, wrapping all
input parameters, such as mgr, (*get_bridges)(), priv, compat_id?

>  	platform_set_drvdata(pdev, region);
>  
> -	ret = fpga_region_register(region);
> -	if (ret)
> -		goto eprobe_mgr_put;
> -
>  	dev_dbg(dev, "DFL FME FPGA Region probed\n");
>  
>  	return 0;
> diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> index 511b20ff35a3..6e628c07c1dc 100644
> --- a/drivers/fpga/dfl.c
> +++ b/drivers/fpga/dfl.c
> @@ -1400,19 +1400,15 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
>  	if (!cdev)
>  		return ERR_PTR(-ENOMEM);
>  
> -	cdev->region = devm_fpga_region_create(info->dev, NULL, NULL);
> -	if (!cdev->region) {
> -		ret = -ENOMEM;
> -		goto free_cdev_exit;
> -	}
> -
>  	cdev->parent = info->dev;
>  	mutex_init(&cdev->lock);
>  	INIT_LIST_HEAD(&cdev->port_dev_list);
>  
> -	ret = fpga_region_register(cdev->region);
> -	if (ret)
> +	cdev->region = fpga_region_register(info->dev, NULL, NULL, NULL);
> +	if (IS_ERR(cdev->region)) {
> +		ret = PTR_ERR(cdev->region);
>  		goto free_cdev_exit;
> +	}
>  
>  	/* create and init build info for enumeration */
>  	binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
> diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
> index c3134b89c3fe..3d28880bf1cc 100644
> --- a/drivers/fpga/fpga-region.c
> +++ b/drivers/fpga/fpga-region.c
> @@ -180,39 +180,38 @@ static struct attribute *fpga_region_attrs[] = {
>  ATTRIBUTE_GROUPS(fpga_region);
>  
>  /**
> - * fpga_region_create - alloc and init a struct fpga_region
> + * fpga_region_register - create and register a FPGA Region device
>   * @dev: device parent
>   * @mgr: manager that programs this region
> + * @compat_id: FPGA manager id for compatibility check.
>   * @get_bridges: optional function to get bridges to a list
>   *
> - * The caller of this function is responsible for freeing the resulting region
> - * struct with fpga_region_free().  Using devm_fpga_region_create() instead is
> - * recommended.
> - *
> - * Return: struct fpga_region or NULL
> + * Return struct fpga_region pointer on success, or ERR_PTR() on error.

Please also restore the doc format.

>   */
> -struct fpga_region
> -*fpga_region_create(struct device *dev,
> -		    struct fpga_manager *mgr,
> -		    int (*get_bridges)(struct fpga_region *))
> +struct fpga_region *
> +fpga_region_register(struct device *dev, struct fpga_manager *mgr,
> +		     struct fpga_compat_id *compat_id,
> +		     int (*get_bridges)(struct fpga_region *))
>  {
>  	struct fpga_region *region;
>  	int id, ret = 0;
>  
>  	region = kzalloc(sizeof(*region), GFP_KERNEL);
>  	if (!region)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>  
>  	id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
> -	if (id < 0)
> +	if (id < 0) {
> +		ret = id;
>  		goto err_free;
> +	}
>  
>  	region->mgr = mgr;
> +	region->compat_id = compat_id;
>  	region->get_bridges = get_bridges;
>  	mutex_init(&region->mutex);
>  	INIT_LIST_HEAD(&region->bridge_list);
>  
> -	device_initialize(&region->dev);
>  	region->dev.class = fpga_region_class;
>  	region->dev.parent = dev;
>  	region->dev.of_node = dev->of_node;
> @@ -222,6 +221,12 @@ struct fpga_region
>  	if (ret)
>  		goto err_remove;
>  
> +	ret = device_register(&region->dev);
> +	if (ret) {
> +		put_device(&region->dev);
> +		return ERR_PTR(ret);
> +	}
> +
>  	return region;
>  
>  err_remove:
> @@ -229,76 +234,7 @@ struct fpga_region
>  err_free:
>  	kfree(region);
>  
> -	return NULL;
> -}
> -EXPORT_SYMBOL_GPL(fpga_region_create);
> -
> -/**
> - * fpga_region_free - free a FPGA region created by fpga_region_create()
> - * @region: FPGA region
> - */
> -void fpga_region_free(struct fpga_region *region)
> -{
> -	ida_simple_remove(&fpga_region_ida, region->dev.id);
> -	kfree(region);
> -}
> -EXPORT_SYMBOL_GPL(fpga_region_free);
> -
> -static void devm_fpga_region_release(struct device *dev, void *res)
> -{
> -	struct fpga_region *region = *(struct fpga_region **)res;
> -
> -	fpga_region_free(region);
> -}
> -
> -/**
> - * devm_fpga_region_create - create and initialize a managed FPGA region struct
> - * @dev: device parent
> - * @mgr: manager that programs this region
> - * @get_bridges: optional function to get bridges to a list
> - *
> - * This function is intended for use in a FPGA region driver's probe function.
> - * After the region driver creates the region struct with
> - * devm_fpga_region_create(), it should register it with fpga_region_register().
> - * The region driver's remove function should call fpga_region_unregister().
> - * The region 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_region_register(), the struct will still get cleaned up.
> - *
> - * Return: struct fpga_region or NULL
> - */
> -struct fpga_region
> -*devm_fpga_region_create(struct device *dev,
> -			 struct fpga_manager *mgr,
> -			 int (*get_bridges)(struct fpga_region *))
> -{
> -	struct fpga_region **ptr, *region;
> -
> -	ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL);
> -	if (!ptr)
> -		return NULL;
> -
> -	region = fpga_region_create(dev, mgr, get_bridges);
> -	if (!region) {
> -		devres_free(ptr);
> -	} else {
> -		*ptr = region;
> -		devres_add(dev, ptr);
> -	}
> -
> -	return region;
> -}
> -EXPORT_SYMBOL_GPL(devm_fpga_region_create);
> -
> -/**
> - * fpga_region_register - register a FPGA region
> - * @region: FPGA region
> - *
> - * Return: 0 or -errno
> - */
> -int fpga_region_register(struct fpga_region *region)
> -{
> -	return device_add(&region->dev);
> +	return ERR_PTR(ret);
>  }
>  EXPORT_SYMBOL_GPL(fpga_region_register);
>  
> @@ -316,6 +252,10 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister);
>  
>  static void fpga_region_dev_release(struct device *dev)
>  {
> +	struct fpga_region *region = to_fpga_region(dev);
> +
> +	ida_simple_remove(&fpga_region_ida, region->dev.id);
> +	kfree(region);
>  }
>  
>  /**
> diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
> index e405309baadc..c115e5fc0c62 100644
> --- a/drivers/fpga/of-fpga-region.c
> +++ b/drivers/fpga/of-fpga-region.c
> @@ -405,16 +405,12 @@ static int of_fpga_region_probe(struct platform_device *pdev)
>  	if (IS_ERR(mgr))
>  		return -EPROBE_DEFER;
>  
> -	region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges);
> -	if (!region) {
> -		ret = -ENOMEM;
> +	region = fpga_region_register(dev, mgr, NULL, of_fpga_region_get_bridges);
> +	if (IS_ERR(region)) {
> +		ret = PTR_ERR(region);
>  		goto eprobe_mgr_put;
>  	}
>  
> -	ret = fpga_region_register(region);
> -	if (ret)
> -		goto eprobe_mgr_put;
> -
>  	of_platform_populate(np, fpga_region_of_match, NULL, &region->dev);
>  	platform_set_drvdata(pdev, region);
>  
> diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
> index 27cb706275db..1e4c2dc7a63b 100644
> --- a/include/linux/fpga/fpga-region.h
> +++ b/include/linux/fpga/fpga-region.h
> @@ -37,15 +37,10 @@ struct fpga_region *fpga_region_class_find(
>  
>  int fpga_region_program_fpga(struct fpga_region *region);
>  
> -struct fpga_region
> -*fpga_region_create(struct device *dev, struct fpga_manager *mgr,
> -		    int (*get_bridges)(struct fpga_region *));
> -void fpga_region_free(struct fpga_region *region);
> -int fpga_region_register(struct fpga_region *region);
> +struct fpga_region *
> +fpga_region_register(struct device *dev, struct fpga_manager *mgr,
> +		     struct fpga_compat_id *compat_id,
> +		     int (*get_bridges)(struct fpga_region *));

Same concern as fpga_mgr_register, shall we introduce a fpga_region_ops
to wrap mgr, (*get_bridges), compat_id and priv? So we don't have to change
all the callers everytime we need a new input for fpga_region.

Thanks,
Yilun

>  void fpga_region_unregister(struct fpga_region *region);
>  
> -struct fpga_region
> -*devm_fpga_region_create(struct device *dev, struct fpga_manager *mgr,
> -			int (*get_bridges)(struct fpga_region *));
> -
>  #endif /* _FPGA_REGION_H */
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 0/3] fpga: Use standard class dev_release function
  2021-05-28 21:57 [PATCH v4 0/3] fpga: Use standard class dev_release function Russ Weight
                   ` (2 preceding siblings ...)
  2021-05-28 21:57 ` [PATCH v4 3/3] fpga: region: " Russ Weight
@ 2021-05-31 15:24 ` Xu Yilun
  2021-06-03 22:49   ` Russ Weight
  3 siblings, 1 reply; 19+ messages in thread
From: Xu Yilun @ 2021-05-31 15:24 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong

On Fri, May 28, 2021 at 02:57:52PM -0700, Russ Weight wrote:
> The FPGA framework has a convention of using managed resource
> functions to allow parent drivers to manage the data structures
> allocated by the class drivers. They use an empty *_dev_release()
> function to satisfy the class driver.
> 
> This is inconsistent with linux driver model.
> 
> These changes remove the managed resource functions and populate
> the class dev_release callback functions. They also merge the
> create and register functions into a single register function for
> each of the fpga-mgr, fpga-region, and fpga-bridge class drivers.
> 
> For more context, refer to this email thread:
> 
> https://marc.info/?l=linux-fpga&m=162127412218557&w=2
> 
> I turned on the configs assocated with each of the modified files,
> but I must have been missing some dependencies, because not all
> of them compiled. I did a run-time test specifically with the
> dfl-fme infrastructure. This would have exercised the region,
> bridge, and fpga-mgr frameworks.
> 
> Changelog v3 -> v4:
>   - Added the compat_id parameter to fpga_mgr_register() and
>     devm_fpga_mgr_register() to ensure that the compat_id is set before
>     the device_register() call.
>   - Added the compat_id parameter to fpga_bridge_register() to ensure

A typo                             ... fpga_region_register() ...

Thanks,
Yilun

>     that the compat_id is set before the device_register() call.
>   - Modified the dfl_fpga_feature_devs_enumerate() function to restore
>     the fpga_region_register() call to the correct location.
> 
> Changelog v2 -> v3:
>   - Cleaned up comment headers for fpga_mgr_register(), fpga_bridge_register(),
>     and fpga_region_register().
>   - Fixed error return on ida_simple_get() failure for fpga_mgr_register(),
>     fpga_bridge_register(), and fpga_region_register().
>   - Fixed error return value for fpga_bridge_register(): ERR_PTR(ret) instead
>     of NULL.
> 
> Changelog v1 -> v2:
>   - Restored devm_fpga_mgr_register() functionality to the fpga-mgr
>     class driver, adapted for the combined create/register functionality.
>   - All previous callers of devm_fpga_mgr_register() will continue to call
>     devm_fpga_mgr_register().
>   - replaced unnecessary ternary operators in return statements with
>     standard if conditions.
> 
> Russ Weight (3):
>   fpga: mgr: Use standard dev_release for class driver
>   fpga: bridge: Use standard dev_release for class driver
>   fpga: region: Use standard dev_release for class driver
> 
>  drivers/fpga/altera-cvp.c           |  12 +-
>  drivers/fpga/altera-fpga2sdram.c    |  12 +-
>  drivers/fpga/altera-freeze-bridge.c |  10 +-
>  drivers/fpga/altera-hps2fpga.c      |  12 +-
>  drivers/fpga/altera-pr-ip-core.c    |   9 +-
>  drivers/fpga/altera-ps-spi.c        |  10 +-
>  drivers/fpga/dfl-fme-br.c           |  10 +-
>  drivers/fpga/dfl-fme-mgr.c          |  12 +-
>  drivers/fpga/dfl-fme-region.c       |  12 +-
>  drivers/fpga/dfl.c                  |  12 +-
>  drivers/fpga/fpga-bridge.c          | 119 +++++---------------
>  drivers/fpga/fpga-mgr.c             | 168 ++++++++--------------------
>  drivers/fpga/fpga-region.c          | 106 ++++--------------
>  drivers/fpga/ice40-spi.c            |  10 +-
>  drivers/fpga/machxo2-spi.c          |  10 +-
>  drivers/fpga/of-fpga-region.c       |  10 +-
>  drivers/fpga/socfpga-a10.c          |  16 +--
>  drivers/fpga/socfpga.c              |  10 +-
>  drivers/fpga/stratix10-soc.c        |  15 +--
>  drivers/fpga/ts73xx-fpga.c          |  10 +-
>  drivers/fpga/xilinx-pr-decoupler.c  |  17 +--
>  drivers/fpga/xilinx-spi.c           |  12 +-
>  drivers/fpga/zynq-fpga.c            |  16 +--
>  drivers/fpga/zynqmp-fpga.c          |  10 +-
>  include/linux/fpga/fpga-bridge.h    |  12 +-
>  include/linux/fpga/fpga-mgr.h       |  19 ++--
>  include/linux/fpga/fpga-region.h    |  13 +--
>  27 files changed, 210 insertions(+), 474 deletions(-)
> 
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-05-28 21:57 ` [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
  2021-05-31  8:54   ` Xu Yilun
@ 2021-06-01 23:24   ` Tom Rix
  2021-06-02  0:38     ` Russ Weight
  1 sibling, 1 reply; 19+ messages in thread
From: Tom Rix @ 2021-06-01 23:24 UTC (permalink / raw)
  To: Russ Weight, mdf, linux-fpga
  Cc: lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong


On 5/28/21 2:57 PM, Russ Weight wrote:
> The FPGA manager class driver data structure is being treated as a
> managed resource instead of using standard dev_release call-back
> to release the class data structure. This change removes the
> managed resource code for the freeing of the class data structure
> and combines the create() and register() functions into a single
> register() function.
>
> The devm_fpga_mgr_register() function is retained.
>
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v4:
>    - Added the compat_id parameter to fpga_mgr_register() and

On a refactor, it is best to do the refactoring as mechanically as possible.

While compat_id may be useful, it should be done in a separate patch.

Unless it is really needed for your next security update patch consider 
resubmitting later

>      devm_fpga_mgr_register() to ensure that the compat_id is set before
>      the device_register() call.
> v3:
>    - Cleaned up comment header for fpga_mgr_register()
>    - Fix error return on ida_simple_get() failure
> v2:
>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>      create/register functionality.
>    - All previous callers of devm_fpga_mgr_register() will continue to call
>      devm_fpga_mgr_register().
>    - replaced unnecessary ternary operators in return statements with standard
>      if conditions.
> ---
>   drivers/fpga/altera-cvp.c        |  12 +--
>   drivers/fpga/altera-pr-ip-core.c |   9 +-
>   drivers/fpga/altera-ps-spi.c     |  10 +-
>   drivers/fpga/dfl-fme-mgr.c       |  12 +--
>   drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>   drivers/fpga/ice40-spi.c         |  10 +-
>   drivers/fpga/machxo2-spi.c       |  10 +-
>   drivers/fpga/socfpga-a10.c       |  16 ++-
>   drivers/fpga/socfpga.c           |  10 +-
>   drivers/fpga/stratix10-soc.c     |  15 +--
>   drivers/fpga/ts73xx-fpga.c       |  10 +-
>   drivers/fpga/xilinx-spi.c        |  12 +--
>   drivers/fpga/zynq-fpga.c         |  16 ++-
>   drivers/fpga/zynqmp-fpga.c       |  10 +-
>   include/linux/fpga/fpga-mgr.h    |  19 ++--
>   15 files changed, 120 insertions(+), 219 deletions(-)

./Documentation/driver-api/fpga/fpga-mgr.rst

still refers to fpga_mgr_create.

>
> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> index 4e0edb60bfba..25eaa97ae7aa 100644
> --- a/drivers/fpga/altera-cvp.c
> +++ b/drivers/fpga/altera-cvp.c
> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>   	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>   		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
>   
> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> -				   &altera_cvp_ops, conf);
> -	if (!mgr) {
> -		ret = -ENOMEM;
> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
> +				&altera_cvp_ops, conf);
> +	if (IS_ERR(mgr)) {
> +		ret = PTR_ERR(mgr);
>   		goto err_unmap;
>   	}
>   
>   	pci_set_drvdata(pdev, mgr);
>   
> -	ret = fpga_mgr_register(mgr);
> -	if (ret)
> -		goto err_unmap;
> -
>   	return 0;
>   
>   err_unmap:
> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
> index 5b130c4d9882..bfd4de1e7ac5 100644
> --- a/drivers/fpga/altera-pr-ip-core.c
> +++ b/drivers/fpga/altera-pr-ip-core.c
> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>   		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>   		(int)(val & ALT_PR_CSR_PR_START));
>   
> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
> +				     &alt_pr_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>   }
>   EXPORT_SYMBOL_GPL(alt_pr_register);
>   
> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> index 23bfd4d1ad0f..e6fc7208213c 100644
> --- a/drivers/fpga/altera-ps-spi.c
> +++ b/drivers/fpga/altera-ps-spi.c
> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>   	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>   		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
>   
> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> -				   &altera_ps_ops, conf);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
> +				     &altera_ps_ops, conf);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> +	return 0;
>   }
>   
>   static const struct spi_device_id altera_ps_spi_ids[] = {
> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> index d5861d13b306..c9b98afa7d50 100644
> --- a/drivers/fpga/dfl-fme-mgr.c
> +++ b/drivers/fpga/dfl-fme-mgr.c
> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>   
>   	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>   
> -	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
> -				   &fme_mgr_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> -
> -	mgr->compat_id = compat_id;
> +	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
> +				     &fme_mgr_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>   }
>   
>   static struct platform_driver fme_mgr_driver = {
> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> index b85bc47c91a9..d6d64ce34417 100644
> --- a/drivers/fpga/fpga-mgr.c
> +++ b/drivers/fpga/fpga-mgr.c
> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>   
>   /**
> - * fpga_mgr_create - create and initialize a FPGA manager struct
> + * fpga_mgr_register - create and register a FPGA Manager device
>    * @dev:	fpga manager device from pdev
>    * @name:	fpga manager name
> + * @compat_id:	FPGA manager id for compatibility check.

for consistency, use lowercase fpga

similar issue below.

>    * @mops:	pointer to structure of fpga manager ops
>    * @priv:	fpga manager private data
>    *
> - * The caller of this function is responsible for freeing the struct with
> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
> + * The caller of this function is responsible for calling fpga_mgr_unregister().
> + * Using devm_fpga_mgr_register instead is recommended.
>    *
> - * Return: pointer to struct fpga_manager or NULL
> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>    */
> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> -				     const struct fpga_manager_ops *mops,
> -				     void *priv)
> +struct fpga_manager *
> +fpga_mgr_register(struct device *dev, const char *name,
> +		  struct fpga_compat_id *compat_id,
> +		  const struct fpga_manager_ops *mops, void *priv)
>   {
>   	struct fpga_manager *mgr;
>   	int id, ret;
> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>   	    !mops->write_init || (!mops->write && !mops->write_sg) ||
>   	    (mops->write && mops->write_sg)) {
>   		dev_err(dev, "Attempt to register without fpga_manager_ops\n");
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>   	}
>   
>   	if (!name || !strlen(name)) {
>   		dev_err(dev, "Attempt to register with no name!\n");
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>   	}
>   
>   	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>   	if (!mgr)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>   
>   	id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
> -	if (id < 0)
> +	if (id < 0) {
> +		ret = id;

ida_* returns a normal error, so needs a wrapping in

ret = ERR_PTR(id)

>   		goto error_kfree;
> +	}
>   
>   	mutex_init(&mgr->ref_mutex);
>   
>   	mgr->name = name;
> +	mgr->compat_id = compat_id;
>   	mgr->mops = mops;
>   	mgr->priv = priv;
>   
> -	device_initialize(&mgr->dev);
>   	mgr->dev.class = fpga_mgr_class;
>   	mgr->dev.groups = mops->groups;
>   	mgr->dev.parent = dev;
> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>   	if (ret)
>   		goto error_device;
>   
> -	return mgr;
> -
> -error_device:
> -	ida_simple_remove(&fpga_mgr_ida, id);
> -error_kfree:
> -	kfree(mgr);
> -
> -	return NULL;
> -}
> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
> -
> -/**
> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
> - * @mgr:	fpga manager struct
> - */
> -void fpga_mgr_free(struct fpga_manager *mgr)
> -{
> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> -	kfree(mgr);
> -}
> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
> -
> -static void devm_fpga_mgr_release(struct device *dev, void *res)
> -{
> -	struct fpga_mgr_devres *dr = res;
> -
> -	fpga_mgr_free(dr->mgr);
> -}
> -
> -/**
> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
> - * @dev:	fpga manager device from pdev
> - * @name:	fpga manager name
> - * @mops:	pointer to structure of fpga manager ops
> - * @priv:	fpga manager private data
> - *
> - * This function is intended for use in a FPGA manager driver's probe function.
> - * After the manager driver creates the manager struct with
> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
> - * manager driver's remove function should call fpga_mgr_unregister().  The
> - * manager 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_mgr_register(), the struct will still get cleaned up.
> - *
> - * Return: pointer to struct fpga_manager or NULL
> - */
> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> -					  const struct fpga_manager_ops *mops,
> -					  void *priv)
> -{
> -	struct fpga_mgr_devres *dr;
> -
> -	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
> -	if (!dr)
> -		return NULL;
> -
> -	dr->mgr = fpga_mgr_create(dev, name, mops, priv);
> -	if (!dr->mgr) {
> -		devres_free(dr);
> -		return NULL;
> -	}
> -
> -	devres_add(dev, dr);
> -
> -	return dr->mgr;
> -}
> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
> -
> -/**
> - * fpga_mgr_register - register a FPGA manager
> - * @mgr: fpga manager struct
> - *
> - * Return: 0 on success, negative error code otherwise.
> - */
> -int fpga_mgr_register(struct fpga_manager *mgr)
> -{
> -	int ret;
> -
>   	/*
>   	 * Initialize framework state by requesting low level driver read state
>   	 * from device.  FPGA may be in reset mode or may have been programmed
> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>   	 */
>   	mgr->state = mgr->mops->state(mgr);
>   
> -	ret = device_add(&mgr->dev);
> -	if (ret)
> -		goto error_device;
> -
> -	dev_info(&mgr->dev, "%s registered\n", mgr->name);
can we keep this dev_info() line and move it below device_register() ?
> +	ret = device_register(&mgr->dev);
> +	if (ret) {
> +		put_device(&mgr->dev);
pr_err() here ?
> +		return ERR_PTR(ret);
> +	}
>   
> -	return 0;
> +	return mgr;
>   
>   error_device:
> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> +	ida_simple_remove(&fpga_mgr_ida, id);
> +error_kfree:
> +	kfree(mgr);
>   
> -	return ret;
> +	return ERR_PTR(ret);
>   }
>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
>   
> @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>   }
>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>   
> -static int fpga_mgr_devres_match(struct device *dev, void *res,
> -				 void *match_data)
> -{
> -	struct fpga_mgr_devres *dr = res;
> -
> -	return match_data == dr->mgr;
> -}
> -
>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>   {
>   	struct fpga_mgr_devres *dr = res;
> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>   
>   /**
>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
> - * @dev: managing device for this FPGA manager
> - * @mgr: fpga manager struct
> + * @dev:	fpga manager device from pdev
> + * @name:	fpga manager name
> + * @compat_id:	FPGA manager id for compatibility check.
> + * @mops:	pointer to structure of fpga manager ops
> + * @priv:	fpga manager private data
>    *
>    * This is the devres variant of fpga_mgr_register() for which the unregister
>    * function will be called automatically when the managing device is detached.
>    */
> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
> +struct fpga_manager *
> +devm_fpga_mgr_register(struct device *dev, const char *name,
> +		       struct fpga_compat_id *compat_id,
> +		       const struct fpga_manager_ops *mops, void *priv)
>   {
>   	struct fpga_mgr_devres *dr;
> -	int ret;
> -
> -	/*
> -	 * Make sure that the struct fpga_manager * that is passed in is
> -	 * managed itself.
> -	 */
> -	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
> -				 fpga_mgr_devres_match, mgr)))
> -		return -EINVAL;
> +	struct fpga_manager *mgr;
>   
>   	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>   	if (!dr)
> -		return -ENOMEM;
> +		return ERR_PTR(-ENOMEM);
>   
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
> +	if (IS_ERR(mgr)) {
>   		devres_free(dr);
> -		return ret;
> +		return mgr;
>   	}
>   
>   	dr->mgr = mgr;
>   	devres_add(dev, dr);
>   
> -	return 0;
> +	return mgr;
>   }
>   EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>   
>   static void fpga_mgr_dev_release(struct device *dev)
>   {
> +	struct fpga_manager *mgr = to_fpga_manager(dev);
> +
> +	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> +	kfree(mgr);
>   }
>   
>   static int __init fpga_mgr_class_init(void)
> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> index 69dec5af23c3..b1c7c0713e11 100644
> --- a/drivers/fpga/ice40-spi.c
> +++ b/drivers/fpga/ice40-spi.c
> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>   		return ret;
>   	}
>   
> -	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
> -				   &ice40_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> +				     NULL, &ice40_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>   }
>   
>   static const struct of_device_id ice40_fpga_of_match[] = {
> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
> index 114a64d2b7a4..75cc6efaa1b6 100644
> --- a/drivers/fpga/machxo2-spi.c
> +++ b/drivers/fpga/machxo2-spi.c
> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>   		return -EINVAL;
>   	}
>   
> -	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
> -				   &machxo2_ops, spi);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
> +				     NULL, &machxo2_ops, spi);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>   }
>   
>   static const struct of_device_id of_match[] = {
> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
> index 573d88bdf730..80b8891fb302 100644
> --- a/drivers/fpga/socfpga-a10.c
> +++ b/drivers/fpga/socfpga-a10.c
> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>   		return -EBUSY;
>   	}
>   
> -	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
> -				   &socfpga_a10_fpga_mgr_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> -
> -	platform_set_drvdata(pdev, mgr);
> -
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
> +				NULL, &socfpga_a10_fpga_mgr_ops, priv);
> +	if (IS_ERR(mgr)) {
>   		clk_disable_unprepare(priv->clk);
> -		return ret;
> +		return PTR_ERR(mgr);
>   	}
>   
> +	platform_set_drvdata(pdev, mgr);
> +
>   	return 0;
>   }
>   
> diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
> index 1f467173fc1f..06edfab8a496 100644
> --- a/drivers/fpga/socfpga.c
> +++ b/drivers/fpga/socfpga.c
> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>   	if (ret)
>   		return ret;
>   
> -	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
> -				   &socfpga_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
> +				     NULL, &socfpga_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>   }
>   
>   #ifdef CONFIG_OF
> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> index 657a70c5fc99..434094c3ac69 100644
> --- a/drivers/fpga/stratix10-soc.c
> +++ b/drivers/fpga/stratix10-soc.c
> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>   
>   	init_completion(&priv->status_return_completion);
>   
> -	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
> -			      &s10_ops, priv);
> -	if (!mgr) {
> -		dev_err(dev, "unable to create FPGA manager\n");
> -		ret = -ENOMEM;
> -		goto probe_err;
> -	}
> -
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
> +				NULL, &s10_ops, priv);
> +	if (IS_ERR(mgr)) {
>   		dev_err(dev, "unable to register FPGA manager\n");
> -		fpga_mgr_free(mgr);
> +		ret = PTR_ERR(mgr);
>   		goto probe_err;
>   	}
>   
> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
> index 101f016c6ed8..5efbec2b9f9f 100644
> --- a/drivers/fpga/ts73xx-fpga.c
> +++ b/drivers/fpga/ts73xx-fpga.c
> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>   	if (IS_ERR(priv->io_base))
>   		return PTR_ERR(priv->io_base);
>   
> -	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
> -				   &ts73xx_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",

This is missing compat_id param.

Similar for others.

> +				     &ts73xx_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(kdev, mgr);
> +	return 0;
>   }
>   
>   static struct platform_driver ts73xx_fpga_driver = {
> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
> index fee4d0abf6bf..6f1894929687 100644
> --- a/drivers/fpga/xilinx-spi.c
> +++ b/drivers/fpga/xilinx-spi.c
> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>   		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>   				     "Failed to get DONE gpio\n");
>   
> -	mgr = devm_fpga_mgr_create(&spi->dev,
> -				   "Xilinx Slave Serial FPGA Manager",
> -				   &xilinx_spi_ops, conf);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(&spi->dev,
> +				     "Xilinx Slave Serial FPGA Manager",
> +				     NULL, &xilinx_spi_ops, conf);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> +	return 0;
>   }
>   
>   static const struct of_device_id xlnx_spi_of_match[] = {
> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
> index 07fa8d9ec675..74bbb9710f4e 100644
> --- a/drivers/fpga/zynq-fpga.c
> +++ b/drivers/fpga/zynq-fpga.c
> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>   
>   	clk_disable(priv->clk);
>   
> -	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
> -				   &zynq_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> -
> -	platform_set_drvdata(pdev, mgr);
> -
> -	err = fpga_mgr_register(mgr);
> -	if (err) {
> +	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
This is missing compat_id param as well.
> +				&zynq_fpga_ops, priv);
> +	if (IS_ERR(mgr)) {
>   		dev_err(dev, "unable to register FPGA manager\n");
>   		clk_unprepare(priv->clk);
> -		return err;
> +		return PTR_ERR(mgr);
>   	}
>   
> +	platform_set_drvdata(pdev, mgr);
> +
>   	return 0;
>   }
>   
> diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
> index 125743c9797f..1a6d520985be 100644
> --- a/drivers/fpga/zynqmp-fpga.c
> +++ b/drivers/fpga/zynqmp-fpga.c
> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>   
>   	priv->dev = dev;
>   
> -	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
> -				   &zynqmp_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
> +				     &zynqmp_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>   
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>   }
>   
>   static const struct of_device_id zynqmp_fpga_of_match[] = {
> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> index 2bc3030a69e5..3e1859cc0640 100644
> --- a/include/linux/fpga/fpga-mgr.h
> +++ b/include/linux/fpga/fpga-mgr.h
> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>   
>   void fpga_mgr_put(struct fpga_manager *mgr);
>   
> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> -				     const struct fpga_manager_ops *mops,
> -				     void *priv);

My biggest concern is that removing this will break all oot fpga drivers.

The other patches have similar api changes

Having a stable api is very important.

My recommendation is to keep the public api and hide the change with an 
exported but not public function.

Tom

> -void fpga_mgr_free(struct fpga_manager *mgr);
> -int fpga_mgr_register(struct fpga_manager *mgr);
> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
> +				       struct fpga_compat_id *compat_id,
> +				       const struct fpga_manager_ops *mops,
> +				       void *priv);
>   void fpga_mgr_unregister(struct fpga_manager *mgr);
>   
> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
> -
> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> -					  const struct fpga_manager_ops *mops,
> -					  void *priv);
> +struct fpga_manager *
> +devm_fpga_mgr_register(struct device *dev, const char *name,
> +		       struct fpga_compat_id *compat_id,
> +		       const struct fpga_manager_ops *mops,
> +		       void *priv);
>   
>   #endif /*_LINUX_FPGA_MGR_H */


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-01 23:24   ` Tom Rix
@ 2021-06-02  0:38     ` Russ Weight
  2021-06-03 22:45       ` Russ Weight
  0 siblings, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-06-02  0:38 UTC (permalink / raw)
  To: Tom Rix, mdf, linux-fpga
  Cc: lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong



On 6/1/21 4:24 PM, Tom Rix wrote:
>
> On 5/28/21 2:57 PM, Russ Weight wrote:
>> The FPGA manager class driver data structure is being treated as a
>> managed resource instead of using standard dev_release call-back
>> to release the class data structure. This change removes the
>> managed resource code for the freeing of the class data structure
>> and combines the create() and register() functions into a single
>> register() function.
>>
>> The devm_fpga_mgr_register() function is retained.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v4:
>>    - Added the compat_id parameter to fpga_mgr_register() and
>
> On a refactor, it is best to do the refactoring as mechanically as possible.
>
> While compat_id may be useful, it should be done in a separate patch.

These would have to be included with the changes merge the create and register
functions into a single function.

>
>
> Unless it is really needed for your next security update patch consider resubmitting later
>
>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
>>      the device_register() call.
>> v3:
>>    - Cleaned up comment header for fpga_mgr_register()
>>    - Fix error return on ida_simple_get() failure
>> v2:
>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>      create/register functionality.
>>    - All previous callers of devm_fpga_mgr_register() will continue to call
>>      devm_fpga_mgr_register().
>>    - replaced unnecessary ternary operators in return statements with standard
>>      if conditions.
>> ---
>>   drivers/fpga/altera-cvp.c        |  12 +--
>>   drivers/fpga/altera-pr-ip-core.c |   9 +-
>>   drivers/fpga/altera-ps-spi.c     |  10 +-
>>   drivers/fpga/dfl-fme-mgr.c       |  12 +--
>>   drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>>   drivers/fpga/ice40-spi.c         |  10 +-
>>   drivers/fpga/machxo2-spi.c       |  10 +-
>>   drivers/fpga/socfpga-a10.c       |  16 ++-
>>   drivers/fpga/socfpga.c           |  10 +-
>>   drivers/fpga/stratix10-soc.c     |  15 +--
>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
>>   drivers/fpga/xilinx-spi.c        |  12 +--
>>   drivers/fpga/zynq-fpga.c         |  16 ++-
>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
>>   include/linux/fpga/fpga-mgr.h    |  19 ++--
>>   15 files changed, 120 insertions(+), 219 deletions(-)
>
> ./Documentation/driver-api/fpga/fpga-mgr.rst
>
> still refers to fpga_mgr_create.
Thanks for catching that.

>
>>
>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>> index 4e0edb60bfba..25eaa97ae7aa 100644
>> --- a/drivers/fpga/altera-cvp.c
>> +++ b/drivers/fpga/altera-cvp.c
>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>            ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>   -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>> -                   &altera_cvp_ops, conf);
>> -    if (!mgr) {
>> -        ret = -ENOMEM;
>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
>> +                &altera_cvp_ops, conf);
>> +    if (IS_ERR(mgr)) {
>> +        ret = PTR_ERR(mgr);
>>           goto err_unmap;
>>       }
>>         pci_set_drvdata(pdev, mgr);
>>   -    ret = fpga_mgr_register(mgr);
>> -    if (ret)
>> -        goto err_unmap;
>> -
>>       return 0;
>>     err_unmap:
>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>> index 5b130c4d9882..bfd4de1e7ac5 100644
>> --- a/drivers/fpga/altera-pr-ip-core.c
>> +++ b/drivers/fpga/altera-pr-ip-core.c
>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>           (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>           (int)(val & ALT_PR_CSR_PR_START));
>>   -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
>> +                     &alt_pr_ops, priv);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(dev, mgr);
>> +    return 0;
>>   }
>>   EXPORT_SYMBOL_GPL(alt_pr_register);
>>   diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>> index 23bfd4d1ad0f..e6fc7208213c 100644
>> --- a/drivers/fpga/altera-ps-spi.c
>> +++ b/drivers/fpga/altera-ps-spi.c
>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>            dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>   -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>> -                   &altera_ps_ops, conf);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
>> +                     &altera_ps_ops, conf);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>> +    return 0;
>>   }
>>     static const struct spi_device_id altera_ps_spi_ids[] = {
>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>> index d5861d13b306..c9b98afa7d50 100644
>> --- a/drivers/fpga/dfl-fme-mgr.c
>> +++ b/drivers/fpga/dfl-fme-mgr.c
>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>         fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>   -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>> -                   &fme_mgr_ops, priv);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> -
>> -    mgr->compat_id = compat_id;
>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
>> +                     &fme_mgr_ops, priv);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(dev, mgr);
>> +    return 0;
>>   }
>>     static struct platform_driver fme_mgr_driver = {
>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>> index b85bc47c91a9..d6d64ce34417 100644
>> --- a/drivers/fpga/fpga-mgr.c
>> +++ b/drivers/fpga/fpga-mgr.c
>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>     /**
>> - * fpga_mgr_create - create and initialize a FPGA manager struct
>> + * fpga_mgr_register - create and register a FPGA Manager device
>>    * @dev:    fpga manager device from pdev
>>    * @name:    fpga manager name
>> + * @compat_id:    FPGA manager id for compatibility check.
>
> for consistency, use lowercase fpga
>
> similar issue below.

OK

>
>>    * @mops:    pointer to structure of fpga manager ops
>>    * @priv:    fpga manager private data
>>    *
>> - * The caller of this function is responsible for freeing the struct with
>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
>> + * Using devm_fpga_mgr_register instead is recommended.
>>    *
>> - * Return: pointer to struct fpga_manager or NULL
>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>>    */
>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>> -                     const struct fpga_manager_ops *mops,
>> -                     void *priv)
>> +struct fpga_manager *
>> +fpga_mgr_register(struct device *dev, const char *name,
>> +          struct fpga_compat_id *compat_id,
>> +          const struct fpga_manager_ops *mops, void *priv)
>>   {
>>       struct fpga_manager *mgr;
>>       int id, ret;
>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>           !mops->write_init || (!mops->write && !mops->write_sg) ||
>>           (mops->write && mops->write_sg)) {
>>           dev_err(dev, "Attempt to register without fpga_manager_ops\n");
>> -        return NULL;
>> +        return ERR_PTR(-EINVAL);
>>       }
>>         if (!name || !strlen(name)) {
>>           dev_err(dev, "Attempt to register with no name!\n");
>> -        return NULL;
>> +        return ERR_PTR(-EINVAL);
>>       }
>>         mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>>       if (!mgr)
>> -        return NULL;
>> +        return ERR_PTR(-ENOMEM);
>>         id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
>> -    if (id < 0)
>> +    if (id < 0) {
>> +        ret = id;
>
> ida_* returns a normal error, so needs a wrapping in
>
> ret = ERR_PTR(id)

Thanks - I'll fix that

>
>>           goto error_kfree;
>> +    }
>>         mutex_init(&mgr->ref_mutex);
>>         mgr->name = name;
>> +    mgr->compat_id = compat_id;
>>       mgr->mops = mops;
>>       mgr->priv = priv;
>>   -    device_initialize(&mgr->dev);
>>       mgr->dev.class = fpga_mgr_class;
>>       mgr->dev.groups = mops->groups;
>>       mgr->dev.parent = dev;
>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>       if (ret)
>>           goto error_device;
>>   -    return mgr;
>> -
>> -error_device:
>> -    ida_simple_remove(&fpga_mgr_ida, id);
>> -error_kfree:
>> -    kfree(mgr);
>> -
>> -    return NULL;
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>> -
>> -/**
>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
>> - * @mgr:    fpga manager struct
>> - */
>> -void fpga_mgr_free(struct fpga_manager *mgr)
>> -{
>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>> -    kfree(mgr);
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
>> -
>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
>> -{
>> -    struct fpga_mgr_devres *dr = res;
>> -
>> -    fpga_mgr_free(dr->mgr);
>> -}
>> -
>> -/**
>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>> - * @dev:    fpga manager device from pdev
>> - * @name:    fpga manager name
>> - * @mops:    pointer to structure of fpga manager ops
>> - * @priv:    fpga manager private data
>> - *
>> - * This function is intended for use in a FPGA manager driver's probe function.
>> - * After the manager driver creates the manager struct with
>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
>> - * manager driver's remove function should call fpga_mgr_unregister().  The
>> - * manager 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_mgr_register(), the struct will still get cleaned up.
>> - *
>> - * Return: pointer to struct fpga_manager or NULL
>> - */
>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>> -                      const struct fpga_manager_ops *mops,
>> -                      void *priv)
>> -{
>> -    struct fpga_mgr_devres *dr;
>> -
>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>> -    if (!dr)
>> -        return NULL;
>> -
>> -    dr->mgr = fpga_mgr_create(dev, name, mops, priv);
>> -    if (!dr->mgr) {
>> -        devres_free(dr);
>> -        return NULL;
>> -    }
>> -
>> -    devres_add(dev, dr);
>> -
>> -    return dr->mgr;
>> -}
>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>> -
>> -/**
>> - * fpga_mgr_register - register a FPGA manager
>> - * @mgr: fpga manager struct
>> - *
>> - * Return: 0 on success, negative error code otherwise.
>> - */
>> -int fpga_mgr_register(struct fpga_manager *mgr)
>> -{
>> -    int ret;
>> -
>>       /*
>>        * Initialize framework state by requesting low level driver read state
>>        * from device.  FPGA may be in reset mode or may have been programmed
>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>        */
>>       mgr->state = mgr->mops->state(mgr);
>>   -    ret = device_add(&mgr->dev);
>> -    if (ret)
>> -        goto error_device;
>> -
>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
> can we keep this dev_info() line and move it below device_register() ?

In Greg's response to my driver, he said he said that a well-behaved driver does
not need dev_info() calls...
>> +    ret = device_register(&mgr->dev);
>> +    if (ret) {
>> +        put_device(&mgr->dev);
> pr_err() here ?

OK - I can add a message

>> +        return ERR_PTR(ret);
>> +    }
>>   -    return 0;
>> +    return mgr;
>>     error_device:
>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>> +    ida_simple_remove(&fpga_mgr_ida, id);
>> +error_kfree:
>> +    kfree(mgr);
>>   -    return ret;
>> +    return ERR_PTR(ret);
>>   }
>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>   @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>   }
>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>   -static int fpga_mgr_devres_match(struct device *dev, void *res,
>> -                 void *match_data)
>> -{
>> -    struct fpga_mgr_devres *dr = res;
>> -
>> -    return match_data == dr->mgr;
>> -}
>> -
>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>   {
>>       struct fpga_mgr_devres *dr = res;
>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>     /**
>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>> - * @dev: managing device for this FPGA manager
>> - * @mgr: fpga manager struct
>> + * @dev:    fpga manager device from pdev
>> + * @name:    fpga manager name
>> + * @compat_id:    FPGA manager id for compatibility check.
>> + * @mops:    pointer to structure of fpga manager ops
>> + * @priv:    fpga manager private data
>>    *
>>    * This is the devres variant of fpga_mgr_register() for which the unregister
>>    * function will be called automatically when the managing device is detached.
>>    */
>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>> +struct fpga_manager *
>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>> +               struct fpga_compat_id *compat_id,
>> +               const struct fpga_manager_ops *mops, void *priv)
>>   {
>>       struct fpga_mgr_devres *dr;
>> -    int ret;
>> -
>> -    /*
>> -     * Make sure that the struct fpga_manager * that is passed in is
>> -     * managed itself.
>> -     */
>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>> -                 fpga_mgr_devres_match, mgr)))
>> -        return -EINVAL;
>> +    struct fpga_manager *mgr;
>>         dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>       if (!dr)
>> -        return -ENOMEM;
>> +        return ERR_PTR(-ENOMEM);
>>   -    ret = fpga_mgr_register(mgr);
>> -    if (ret) {
>> +    mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
>> +    if (IS_ERR(mgr)) {
>>           devres_free(dr);
>> -        return ret;
>> +        return mgr;
>>       }
>>         dr->mgr = mgr;
>>       devres_add(dev, dr);
>>   -    return 0;
>> +    return mgr;
>>   }
>>   EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>>     static void fpga_mgr_dev_release(struct device *dev)
>>   {
>> +    struct fpga_manager *mgr = to_fpga_manager(dev);
>> +
>> +    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>> +    kfree(mgr);
>>   }
>>     static int __init fpga_mgr_class_init(void)
>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>> index 69dec5af23c3..b1c7c0713e11 100644
>> --- a/drivers/fpga/ice40-spi.c
>> +++ b/drivers/fpga/ice40-spi.c
>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>           return ret;
>>       }
>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>> -                   &ice40_fpga_ops, priv);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>> +                     NULL, &ice40_fpga_ops, priv);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(dev, mgr);
>> +    return 0;
>>   }
>>     static const struct of_device_id ice40_fpga_of_match[] = {
>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>> index 114a64d2b7a4..75cc6efaa1b6 100644
>> --- a/drivers/fpga/machxo2-spi.c
>> +++ b/drivers/fpga/machxo2-spi.c
>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>           return -EINVAL;
>>       }
>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>> -                   &machxo2_ops, spi);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>> +                     NULL, &machxo2_ops, spi);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(dev, mgr);
>> +    return 0;
>>   }
>>     static const struct of_device_id of_match[] = {
>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>> index 573d88bdf730..80b8891fb302 100644
>> --- a/drivers/fpga/socfpga-a10.c
>> +++ b/drivers/fpga/socfpga-a10.c
>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>           return -EBUSY;
>>       }
>>   -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>> -                   &socfpga_a10_fpga_mgr_ops, priv);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> -
>> -    platform_set_drvdata(pdev, mgr);
>> -
>> -    ret = fpga_mgr_register(mgr);
>> -    if (ret) {
>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>> +                NULL, &socfpga_a10_fpga_mgr_ops, priv);
>> +    if (IS_ERR(mgr)) {
>>           clk_disable_unprepare(priv->clk);
>> -        return ret;
>> +        return PTR_ERR(mgr);
>>       }
>>   +    platform_set_drvdata(pdev, mgr);
>> +
>>       return 0;
>>   }
>>   diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>> index 1f467173fc1f..06edfab8a496 100644
>> --- a/drivers/fpga/socfpga.c
>> +++ b/drivers/fpga/socfpga.c
>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>       if (ret)
>>           return ret;
>>   -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>> -                   &socfpga_fpga_ops, priv);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>> +                     NULL, &socfpga_fpga_ops, priv);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(dev, mgr);
>> +    return 0;
>>   }
>>     #ifdef CONFIG_OF
>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>> index 657a70c5fc99..434094c3ac69 100644
>> --- a/drivers/fpga/stratix10-soc.c
>> +++ b/drivers/fpga/stratix10-soc.c
>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>         init_completion(&priv->status_return_completion);
>>   -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>> -                  &s10_ops, priv);
>> -    if (!mgr) {
>> -        dev_err(dev, "unable to create FPGA manager\n");
>> -        ret = -ENOMEM;
>> -        goto probe_err;
>> -    }
>> -
>> -    ret = fpga_mgr_register(mgr);
>> -    if (ret) {
>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>> +                NULL, &s10_ops, priv);
>> +    if (IS_ERR(mgr)) {
>>           dev_err(dev, "unable to register FPGA manager\n");
>> -        fpga_mgr_free(mgr);
>> +        ret = PTR_ERR(mgr);
>>           goto probe_err;
>>       }
>>   diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>> index 101f016c6ed8..5efbec2b9f9f 100644
>> --- a/drivers/fpga/ts73xx-fpga.c
>> +++ b/drivers/fpga/ts73xx-fpga.c
>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>       if (IS_ERR(priv->io_base))
>>           return PTR_ERR(priv->io_base);
>>   -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>> -                   &ts73xx_fpga_ops, priv);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>
> This is missing compat_id param.

Thanks - I thought I'll do another pass and make sure I catch them all.

>
> Similar for others.
>
>> +                     &ts73xx_fpga_ops, priv);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(kdev, mgr);
>> +    return 0;
>>   }
>>     static struct platform_driver ts73xx_fpga_driver = {
>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>> index fee4d0abf6bf..6f1894929687 100644
>> --- a/drivers/fpga/xilinx-spi.c
>> +++ b/drivers/fpga/xilinx-spi.c
>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>           return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>                        "Failed to get DONE gpio\n");
>>   -    mgr = devm_fpga_mgr_create(&spi->dev,
>> -                   "Xilinx Slave Serial FPGA Manager",
>> -                   &xilinx_spi_ops, conf);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> +    mgr = devm_fpga_mgr_register(&spi->dev,
>> +                     "Xilinx Slave Serial FPGA Manager",
>> +                     NULL, &xilinx_spi_ops, conf);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>> +    return 0;
>>   }
>>     static const struct of_device_id xlnx_spi_of_match[] = {
>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>> index 07fa8d9ec675..74bbb9710f4e 100644
>> --- a/drivers/fpga/zynq-fpga.c
>> +++ b/drivers/fpga/zynq-fpga.c
>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>         clk_disable(priv->clk);
>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>> -                   &zynq_fpga_ops, priv);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> -
>> -    platform_set_drvdata(pdev, mgr);
>> -
>> -    err = fpga_mgr_register(mgr);
>> -    if (err) {
>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
> This is missing compat_id param as well.
>> +                &zynq_fpga_ops, priv);
>> +    if (IS_ERR(mgr)) {
>>           dev_err(dev, "unable to register FPGA manager\n");
>>           clk_unprepare(priv->clk);
>> -        return err;
>> +        return PTR_ERR(mgr);
>>       }
>>   +    platform_set_drvdata(pdev, mgr);
>> +
>>       return 0;
>>   }
>>   diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>> index 125743c9797f..1a6d520985be 100644
>> --- a/drivers/fpga/zynqmp-fpga.c
>> +++ b/drivers/fpga/zynqmp-fpga.c
>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>         priv->dev = dev;
>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>> -                   &zynqmp_fpga_ops, priv);
>> -    if (!mgr)
>> -        return -ENOMEM;
>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>> +                     &zynqmp_fpga_ops, priv);
>> +    if (IS_ERR(mgr))
>> +        return PTR_ERR(mgr);
>>   -    return devm_fpga_mgr_register(dev, mgr);
>> +    return 0;
>>   }
>>     static const struct of_device_id zynqmp_fpga_of_match[] = {
>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>> index 2bc3030a69e5..3e1859cc0640 100644
>> --- a/include/linux/fpga/fpga-mgr.h
>> +++ b/include/linux/fpga/fpga-mgr.h
>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>     void fpga_mgr_put(struct fpga_manager *mgr);
>>   -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>> -                     const struct fpga_manager_ops *mops,
>> -                     void *priv);
>
> My biggest concern is that removing this will break all oot fpga drivers.
>
> The other patches have similar api changes
>
> Having a stable api is very important.

That is a good point. While Greg did call out the separation of the
device_initialize() and device_add() calls, his biggest concern was with
the empty dev_release() functions. Maybe it would be best to first submit
a patch that only addresses the dev_release() functions? Maybe it would be
OK to leave the create and register functions as separate functions in order
to maintain a stable API...

- Russ

>
> My recommendation is to keep the public api and hide the change with an exported but not public function.
>
> Tom
>
>> -void fpga_mgr_free(struct fpga_manager *mgr);
>> -int fpga_mgr_register(struct fpga_manager *mgr);
>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>> +                       struct fpga_compat_id *compat_id,
>> +                       const struct fpga_manager_ops *mops,
>> +                       void *priv);
>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
>>   -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>> -
>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>> -                      const struct fpga_manager_ops *mops,
>> -                      void *priv);
>> +struct fpga_manager *
>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>> +               struct fpga_compat_id *compat_id,
>> +               const struct fpga_manager_ops *mops,
>> +               void *priv);
>>     #endif /*_LINUX_FPGA_MGR_H */
>


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-05-31  8:54   ` Xu Yilun
@ 2021-06-03 21:21     ` Russ Weight
  0 siblings, 0 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-03 21:21 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong



On 5/31/21 1:54 AM, Xu Yilun wrote:
> A quick comment, may come back later. See inline:
>
> On Fri, May 28, 2021 at 02:57:53PM -0700, Russ Weight wrote:
>> The FPGA manager class driver data structure is being treated as a
>> managed resource instead of using standard dev_release call-back
>> to release the class data structure. This change removes the
>> managed resource code for the freeing of the class data structure
>> and combines the create() and register() functions into a single
>> register() function.
>>
>> The devm_fpga_mgr_register() function is retained.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v4:
>>   - Added the compat_id parameter to fpga_mgr_register() and
>>     devm_fpga_mgr_register() to ensure that the compat_id is set before
>>     the device_register() call.
>> v3:
>>   - Cleaned up comment header for fpga_mgr_register()
>>   - Fix error return on ida_simple_get() failure
>> v2:
>>   - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>     create/register functionality.
>>   - All previous callers of devm_fpga_mgr_register() will continue to call
>>     devm_fpga_mgr_register().
>>   - replaced unnecessary ternary operators in return statements with standard
>>     if conditions.
>> ---
>>  drivers/fpga/altera-cvp.c        |  12 +--
>>  drivers/fpga/altera-pr-ip-core.c |   9 +-
>>  drivers/fpga/altera-ps-spi.c     |  10 +-
>>  drivers/fpga/dfl-fme-mgr.c       |  12 +--
>>  drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>>  drivers/fpga/ice40-spi.c         |  10 +-
>>  drivers/fpga/machxo2-spi.c       |  10 +-
>>  drivers/fpga/socfpga-a10.c       |  16 ++-
>>  drivers/fpga/socfpga.c           |  10 +-
>>  drivers/fpga/stratix10-soc.c     |  15 +--
>>  drivers/fpga/ts73xx-fpga.c       |  10 +-
>>  drivers/fpga/xilinx-spi.c        |  12 +--
>>  drivers/fpga/zynq-fpga.c         |  16 ++-
>>  drivers/fpga/zynqmp-fpga.c       |  10 +-
>>  include/linux/fpga/fpga-mgr.h    |  19 ++--
>>  15 files changed, 120 insertions(+), 219 deletions(-)
>>
>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>> index 4e0edb60bfba..25eaa97ae7aa 100644
>> --- a/drivers/fpga/altera-cvp.c
>> +++ b/drivers/fpga/altera-cvp.c
>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>  		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>  
>> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>> -				   &altera_cvp_ops, conf);
>> -	if (!mgr) {
>> -		ret = -ENOMEM;
>> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
>> +				&altera_cvp_ops, conf);
>> +	if (IS_ERR(mgr)) {
>> +		ret = PTR_ERR(mgr);
>>  		goto err_unmap;
>>  	}
>>  
>>  	pci_set_drvdata(pdev, mgr);
>>  
>> -	ret = fpga_mgr_register(mgr);
>> -	if (ret)
>> -		goto err_unmap;
>> -
>>  	return 0;
>>  
>>  err_unmap:
>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>> index 5b130c4d9882..bfd4de1e7ac5 100644
>> --- a/drivers/fpga/altera-pr-ip-core.c
>> +++ b/drivers/fpga/altera-pr-ip-core.c
>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>  		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>  		(int)(val & ALT_PR_CSR_PR_START));
>>  
>> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
>> +				     &alt_pr_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  EXPORT_SYMBOL_GPL(alt_pr_register);
>>  
>> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>> index 23bfd4d1ad0f..e6fc7208213c 100644
>> --- a/drivers/fpga/altera-ps-spi.c
>> +++ b/drivers/fpga/altera-ps-spi.c
>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>  		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>  
>> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>> -				   &altera_ps_ops, conf);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
>> +				     &altera_ps_ops, conf);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct spi_device_id altera_ps_spi_ids[] = {
>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>> index d5861d13b306..c9b98afa7d50 100644
>> --- a/drivers/fpga/dfl-fme-mgr.c
>> +++ b/drivers/fpga/dfl-fme-mgr.c
>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>  
>>  	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>> -				   &fme_mgr_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> -
>> -	mgr->compat_id = compat_id;
>> +	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
>> +				     &fme_mgr_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static struct platform_driver fme_mgr_driver = {
>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>> index b85bc47c91a9..d6d64ce34417 100644
>> --- a/drivers/fpga/fpga-mgr.c
>> +++ b/drivers/fpga/fpga-mgr.c
>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>  EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>  
>>  /**
>> - * fpga_mgr_create - create and initialize a FPGA manager struct
>> + * fpga_mgr_register - create and register a FPGA Manager device
>>   * @dev:	fpga manager device from pdev
>>   * @name:	fpga manager name
>> + * @compat_id:	FPGA manager id for compatibility check.
>>   * @mops:	pointer to structure of fpga manager ops
>>   * @priv:	fpga manager private data
>>   *
>> - * The caller of this function is responsible for freeing the struct with
>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
>> + * Using devm_fpga_mgr_register instead is recommended.
>>   *
>> - * Return: pointer to struct fpga_manager or NULL
>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>>   */
>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>> -				     const struct fpga_manager_ops *mops,
>> -				     void *priv)
>> +struct fpga_manager *
>> +fpga_mgr_register(struct device *dev, const char *name,
>> +		  struct fpga_compat_id *compat_id,
>> +		  const struct fpga_manager_ops *mops, void *priv)
>>  {
>>  	struct fpga_manager *mgr;
>>  	int id, ret;
>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>  	    !mops->write_init || (!mops->write && !mops->write_sg) ||
>>  	    (mops->write && mops->write_sg)) {
>>  		dev_err(dev, "Attempt to register without fpga_manager_ops\n");
>> -		return NULL;
>> +		return ERR_PTR(-EINVAL);
>>  	}
>>  
>>  	if (!name || !strlen(name)) {
>>  		dev_err(dev, "Attempt to register with no name!\n");
>> -		return NULL;
>> +		return ERR_PTR(-EINVAL);
>>  	}
>>  
>>  	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>>  	if (!mgr)
>> -		return NULL;
>> +		return ERR_PTR(-ENOMEM);
>>  
>>  	id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
>> -	if (id < 0)
>> +	if (id < 0) {
>> +		ret = id;
>>  		goto error_kfree;
>> +	}
>>  
>>  	mutex_init(&mgr->ref_mutex);
>>  
>>  	mgr->name = name;
>> +	mgr->compat_id = compat_id;
>>  	mgr->mops = mops;
>>  	mgr->priv = priv;
>>  
>> -	device_initialize(&mgr->dev);
>>  	mgr->dev.class = fpga_mgr_class;
>>  	mgr->dev.groups = mops->groups;
>>  	mgr->dev.parent = dev;
>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>  	if (ret)
>>  		goto error_device;
>>  
>> -	return mgr;
>> -
>> -error_device:
>> -	ida_simple_remove(&fpga_mgr_ida, id);
>> -error_kfree:
>> -	kfree(mgr);
>> -
>> -	return NULL;
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>> -
>> -/**
>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
>> - * @mgr:	fpga manager struct
>> - */
>> -void fpga_mgr_free(struct fpga_manager *mgr)
>> -{
>> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>> -	kfree(mgr);
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
>> -
>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
>> -{
>> -	struct fpga_mgr_devres *dr = res;
>> -
>> -	fpga_mgr_free(dr->mgr);
>> -}
>> -
>> -/**
>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>> - * @dev:	fpga manager device from pdev
>> - * @name:	fpga manager name
>> - * @mops:	pointer to structure of fpga manager ops
>> - * @priv:	fpga manager private data
>> - *
>> - * This function is intended for use in a FPGA manager driver's probe function.
>> - * After the manager driver creates the manager struct with
>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
>> - * manager driver's remove function should call fpga_mgr_unregister().  The
>> - * manager 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_mgr_register(), the struct will still get cleaned up.
>> - *
>> - * Return: pointer to struct fpga_manager or NULL
>> - */
>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>> -					  const struct fpga_manager_ops *mops,
>> -					  void *priv)
>> -{
>> -	struct fpga_mgr_devres *dr;
>> -
>> -	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>> -	if (!dr)
>> -		return NULL;
>> -
>> -	dr->mgr = fpga_mgr_create(dev, name, mops, priv);
>> -	if (!dr->mgr) {
>> -		devres_free(dr);
>> -		return NULL;
>> -	}
>> -
>> -	devres_add(dev, dr);
>> -
>> -	return dr->mgr;
>> -}
>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>> -
>> -/**
>> - * fpga_mgr_register - register a FPGA manager
>> - * @mgr: fpga manager struct
>> - *
>> - * Return: 0 on success, negative error code otherwise.
>> - */
>> -int fpga_mgr_register(struct fpga_manager *mgr)
>> -{
>> -	int ret;
>> -
>>  	/*
>>  	 * Initialize framework state by requesting low level driver read state
>>  	 * from device.  FPGA may be in reset mode or may have been programmed
>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>  	 */
>>  	mgr->state = mgr->mops->state(mgr);
>>  
>> -	ret = device_add(&mgr->dev);
>> -	if (ret)
>> -		goto error_device;
>> -
>> -	dev_info(&mgr->dev, "%s registered\n", mgr->name);
>> +	ret = device_register(&mgr->dev);
>> +	if (ret) {
>> +		put_device(&mgr->dev);
>> +		return ERR_PTR(ret);
>> +	}
>>  
>> -	return 0;
>> +	return mgr;
>>  
>>  error_device:
>> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>> +	ida_simple_remove(&fpga_mgr_ida, id);
>> +error_kfree:
>> +	kfree(mgr);
>>  
>> -	return ret;
>> +	return ERR_PTR(ret);
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>  
>> @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>  
>> -static int fpga_mgr_devres_match(struct device *dev, void *res,
>> -				 void *match_data)
>> -{
>> -	struct fpga_mgr_devres *dr = res;
>> -
>> -	return match_data == dr->mgr;
>> -}
>> -
>>  static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>  {
>>  	struct fpga_mgr_devres *dr = res;
>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>  
>>  /**
>>   * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>> - * @dev: managing device for this FPGA manager
>> - * @mgr: fpga manager struct
>> + * @dev:	fpga manager device from pdev
>> + * @name:	fpga manager name
>> + * @compat_id:	FPGA manager id for compatibility check.
>> + * @mops:	pointer to structure of fpga manager ops
>> + * @priv:	fpga manager private data
>>   *
>>   * This is the devres variant of fpga_mgr_register() for which the unregister
>>   * function will be called automatically when the managing device is detached.
>>   */
>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>> +struct fpga_manager *
>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>> +		       struct fpga_compat_id *compat_id,
>> +		       const struct fpga_manager_ops *mops, void *priv)
>>  {
>>  	struct fpga_mgr_devres *dr;
>> -	int ret;
>> -
>> -	/*
>> -	 * Make sure that the struct fpga_manager * that is passed in is
>> -	 * managed itself.
>> -	 */
>> -	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>> -				 fpga_mgr_devres_match, mgr)))
>> -		return -EINVAL;
>> +	struct fpga_manager *mgr;
>>  
>>  	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>  	if (!dr)
>> -		return -ENOMEM;
>> +		return ERR_PTR(-ENOMEM);
>>  
>> -	ret = fpga_mgr_register(mgr);
>> -	if (ret) {
>> +	mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
>> +	if (IS_ERR(mgr)) {
>>  		devres_free(dr);
>> -		return ret;
>> +		return mgr;
>>  	}
>>  
>>  	dr->mgr = mgr;
>>  	devres_add(dev, dr);
>>  
>> -	return 0;
>> +	return mgr;
>>  }
>>  EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>>  
>>  static void fpga_mgr_dev_release(struct device *dev)
>>  {
>> +	struct fpga_manager *mgr = to_fpga_manager(dev);
>> +
>> +	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>> +	kfree(mgr);
>>  }
>>  
>>  static int __init fpga_mgr_class_init(void)
>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>> index 69dec5af23c3..b1c7c0713e11 100644
>> --- a/drivers/fpga/ice40-spi.c
>> +++ b/drivers/fpga/ice40-spi.c
>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>  		return ret;
>>  	}
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>> -				   &ice40_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>> +				     NULL, &ice40_fpga_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct of_device_id ice40_fpga_of_match[] = {
>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>> index 114a64d2b7a4..75cc6efaa1b6 100644
>> --- a/drivers/fpga/machxo2-spi.c
>> +++ b/drivers/fpga/machxo2-spi.c
>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>  		return -EINVAL;
>>  	}
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>> -				   &machxo2_ops, spi);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>> +				     NULL, &machxo2_ops, spi);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct of_device_id of_match[] = {
>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>> index 573d88bdf730..80b8891fb302 100644
>> --- a/drivers/fpga/socfpga-a10.c
>> +++ b/drivers/fpga/socfpga-a10.c
>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>  		return -EBUSY;
>>  	}
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>> -				   &socfpga_a10_fpga_mgr_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> -
>> -	platform_set_drvdata(pdev, mgr);
>> -
>> -	ret = fpga_mgr_register(mgr);
>> -	if (ret) {
>> +	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>> +				NULL, &socfpga_a10_fpga_mgr_ops, priv);
>> +	if (IS_ERR(mgr)) {
>>  		clk_disable_unprepare(priv->clk);
>> -		return ret;
>> +		return PTR_ERR(mgr);
>>  	}
>>  
>> +	platform_set_drvdata(pdev, mgr);
>> +
>>  	return 0;
>>  }
>>  
>> diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>> index 1f467173fc1f..06edfab8a496 100644
>> --- a/drivers/fpga/socfpga.c
>> +++ b/drivers/fpga/socfpga.c
>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>  	if (ret)
>>  		return ret;
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>> -				   &socfpga_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>> +				     NULL, &socfpga_fpga_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  #ifdef CONFIG_OF
>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>> index 657a70c5fc99..434094c3ac69 100644
>> --- a/drivers/fpga/stratix10-soc.c
>> +++ b/drivers/fpga/stratix10-soc.c
>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>  
>>  	init_completion(&priv->status_return_completion);
>>  
>> -	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>> -			      &s10_ops, priv);
>> -	if (!mgr) {
>> -		dev_err(dev, "unable to create FPGA manager\n");
>> -		ret = -ENOMEM;
>> -		goto probe_err;
>> -	}
>> -
>> -	ret = fpga_mgr_register(mgr);
>> -	if (ret) {
>> +	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>> +				NULL, &s10_ops, priv);
>> +	if (IS_ERR(mgr)) {
>>  		dev_err(dev, "unable to register FPGA manager\n");
>> -		fpga_mgr_free(mgr);
>> +		ret = PTR_ERR(mgr);
>>  		goto probe_err;
>>  	}
>>  
>> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>> index 101f016c6ed8..5efbec2b9f9f 100644
>> --- a/drivers/fpga/ts73xx-fpga.c
>> +++ b/drivers/fpga/ts73xx-fpga.c
>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>  	if (IS_ERR(priv->io_base))
>>  		return PTR_ERR(priv->io_base);
>>  
>> -	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>> -				   &ts73xx_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>> +				     &ts73xx_fpga_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(kdev, mgr);
>> +	return 0;
>>  }
>>  
>>  static struct platform_driver ts73xx_fpga_driver = {
>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>> index fee4d0abf6bf..6f1894929687 100644
>> --- a/drivers/fpga/xilinx-spi.c
>> +++ b/drivers/fpga/xilinx-spi.c
>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>  		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>  				     "Failed to get DONE gpio\n");
>>  
>> -	mgr = devm_fpga_mgr_create(&spi->dev,
>> -				   "Xilinx Slave Serial FPGA Manager",
>> -				   &xilinx_spi_ops, conf);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(&spi->dev,
>> +				     "Xilinx Slave Serial FPGA Manager",
>> +				     NULL, &xilinx_spi_ops, conf);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct of_device_id xlnx_spi_of_match[] = {
>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>> index 07fa8d9ec675..74bbb9710f4e 100644
>> --- a/drivers/fpga/zynq-fpga.c
>> +++ b/drivers/fpga/zynq-fpga.c
>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>  
>>  	clk_disable(priv->clk);
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>> -				   &zynq_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> -
>> -	platform_set_drvdata(pdev, mgr);
>> -
>> -	err = fpga_mgr_register(mgr);
>> -	if (err) {
>> +	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>> +				&zynq_fpga_ops, priv);
>> +	if (IS_ERR(mgr)) {
>>  		dev_err(dev, "unable to register FPGA manager\n");
>>  		clk_unprepare(priv->clk);
>> -		return err;
>> +		return PTR_ERR(mgr);
>>  	}
>>  
>> +	platform_set_drvdata(pdev, mgr);
>> +
>>  	return 0;
>>  }
>>  
>> diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>> index 125743c9797f..1a6d520985be 100644
>> --- a/drivers/fpga/zynqmp-fpga.c
>> +++ b/drivers/fpga/zynqmp-fpga.c
>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>  
>>  	priv->dev = dev;
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>> -				   &zynqmp_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>> +				     &zynqmp_fpga_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct of_device_id zynqmp_fpga_of_match[] = {
>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>> index 2bc3030a69e5..3e1859cc0640 100644
>> --- a/include/linux/fpga/fpga-mgr.h
>> +++ b/include/linux/fpga/fpga-mgr.h
>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>  
>>  void fpga_mgr_put(struct fpga_manager *mgr);
>>  
>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>> -				     const struct fpga_manager_ops *mops,
>> -				     void *priv);
>> -void fpga_mgr_free(struct fpga_manager *mgr);
>> -int fpga_mgr_register(struct fpga_manager *mgr);
>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>> +				       struct fpga_compat_id *compat_id,
>> +				       const struct fpga_manager_ops *mops,
>> +				       void *priv);
>>  void fpga_mgr_unregister(struct fpga_manager *mgr);
>>  
>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>> -
>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>> -					  const struct fpga_manager_ops *mops,
>> -					  void *priv);
>> +struct fpga_manager *
>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>> +		       struct fpga_compat_id *compat_id,
> Could we integrate the compat_id in fpga_manager_ops? Actually not much
> driver cares about the compat_id.
> In this way we don't force everyone fill a NULL on registration.

I'll keep it this way for now. I am also looking for input from others. I think
this suggestion would make it easier for drivers that don't care about compat_id,
and harder for drivers that do - because they would have to dynamically create
the fpga_manager_ops data structure in stead of defining it statically.

- Russ
>
> Expecting more opinions.
>
> Thanks,
> Yilun
>
>> +		       const struct fpga_manager_ops *mops,
>> +		       void *priv);
>>  
>>  #endif /*_LINUX_FPGA_MGR_H */
>> -- 
>> 2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-02  0:38     ` Russ Weight
@ 2021-06-03 22:45       ` Russ Weight
  2021-06-04 20:56         ` Tom Rix
  2021-06-05 15:42         ` Xu Yilun
  0 siblings, 2 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-03 22:45 UTC (permalink / raw)
  To: Tom Rix, mdf, linux-fpga
  Cc: lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong



On 6/1/21 5:38 PM, Russ Weight wrote:
>
> On 6/1/21 4:24 PM, Tom Rix wrote:
>> On 5/28/21 2:57 PM, Russ Weight wrote:
>>> The FPGA manager class driver data structure is being treated as a
>>> managed resource instead of using standard dev_release call-back
>>> to release the class data structure. This change removes the
>>> managed resource code for the freeing of the class data structure
>>> and combines the create() and register() functions into a single
>>> register() function.
>>>
>>> The devm_fpga_mgr_register() function is retained.
>>>
>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>> ---
>>> v4:
>>>    - Added the compat_id parameter to fpga_mgr_register() and
>> On a refactor, it is best to do the refactoring as mechanically as possible.
>>
>> While compat_id may be useful, it should be done in a separate patch.
> These would have to be included with the changes merge the create and register
> functions into a single function.
>
>>
>> Unless it is really needed for your next security update patch consider resubmitting later
>>
>>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
>>>      the device_register() call.
>>> v3:
>>>    - Cleaned up comment header for fpga_mgr_register()
>>>    - Fix error return on ida_simple_get() failure
>>> v2:
>>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>>      create/register functionality.
>>>    - All previous callers of devm_fpga_mgr_register() will continue to call
>>>      devm_fpga_mgr_register().
>>>    - replaced unnecessary ternary operators in return statements with standard
>>>      if conditions.
>>> ---
>>>   drivers/fpga/altera-cvp.c        |  12 +--
>>>   drivers/fpga/altera-pr-ip-core.c |   9 +-
>>>   drivers/fpga/altera-ps-spi.c     |  10 +-
>>>   drivers/fpga/dfl-fme-mgr.c       |  12 +--
>>>   drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>>>   drivers/fpga/ice40-spi.c         |  10 +-
>>>   drivers/fpga/machxo2-spi.c       |  10 +-
>>>   drivers/fpga/socfpga-a10.c       |  16 ++-
>>>   drivers/fpga/socfpga.c           |  10 +-
>>>   drivers/fpga/stratix10-soc.c     |  15 +--
>>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
>>>   drivers/fpga/xilinx-spi.c        |  12 +--
>>>   drivers/fpga/zynq-fpga.c         |  16 ++-
>>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
>>>   include/linux/fpga/fpga-mgr.h    |  19 ++--
>>>   15 files changed, 120 insertions(+), 219 deletions(-)
>> ./Documentation/driver-api/fpga/fpga-mgr.rst
>>
>> still refers to fpga_mgr_create.
> Thanks for catching that.
>
>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>>> index 4e0edb60bfba..25eaa97ae7aa 100644
>>> --- a/drivers/fpga/altera-cvp.c
>>> +++ b/drivers/fpga/altera-cvp.c
>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>>            ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>>   -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>>> -                   &altera_cvp_ops, conf);
>>> -    if (!mgr) {
>>> -        ret = -ENOMEM;
>>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
>>> +                &altera_cvp_ops, conf);
>>> +    if (IS_ERR(mgr)) {
>>> +        ret = PTR_ERR(mgr);
>>>           goto err_unmap;
>>>       }
>>>         pci_set_drvdata(pdev, mgr);
>>>   -    ret = fpga_mgr_register(mgr);
>>> -    if (ret)
>>> -        goto err_unmap;
>>> -
>>>       return 0;
>>>     err_unmap:
>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>>> index 5b130c4d9882..bfd4de1e7ac5 100644
>>> --- a/drivers/fpga/altera-pr-ip-core.c
>>> +++ b/drivers/fpga/altera-pr-ip-core.c
>>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>>           (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>>           (int)(val & ALT_PR_CSR_PR_START));
>>>   -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
>>> +                     &alt_pr_ops, priv);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>> +    return 0;
>>>   }
>>>   EXPORT_SYMBOL_GPL(alt_pr_register);
>>>   diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>>> index 23bfd4d1ad0f..e6fc7208213c 100644
>>> --- a/drivers/fpga/altera-ps-spi.c
>>> +++ b/drivers/fpga/altera-ps-spi.c
>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>>            dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>>   -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>>> -                   &altera_ps_ops, conf);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
>>> +                     &altera_ps_ops, conf);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>> +    return 0;
>>>   }
>>>     static const struct spi_device_id altera_ps_spi_ids[] = {
>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>>> index d5861d13b306..c9b98afa7d50 100644
>>> --- a/drivers/fpga/dfl-fme-mgr.c
>>> +++ b/drivers/fpga/dfl-fme-mgr.c
>>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>>         fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>>   -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>>> -                   &fme_mgr_ops, priv);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> -
>>> -    mgr->compat_id = compat_id;
>>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
>>> +                     &fme_mgr_ops, priv);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>> +    return 0;
>>>   }
>>>     static struct platform_driver fme_mgr_driver = {
>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>>> index b85bc47c91a9..d6d64ce34417 100644
>>> --- a/drivers/fpga/fpga-mgr.c
>>> +++ b/drivers/fpga/fpga-mgr.c
>>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>>     /**
>>> - * fpga_mgr_create - create and initialize a FPGA manager struct
>>> + * fpga_mgr_register - create and register a FPGA Manager device
>>>    * @dev:    fpga manager device from pdev
>>>    * @name:    fpga manager name
>>> + * @compat_id:    FPGA manager id for compatibility check.
>> for consistency, use lowercase fpga
>>
>> similar issue below.
> OK
>
>>>    * @mops:    pointer to structure of fpga manager ops
>>>    * @priv:    fpga manager private data
>>>    *
>>> - * The caller of this function is responsible for freeing the struct with
>>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
>>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
>>> + * Using devm_fpga_mgr_register instead is recommended.
>>>    *
>>> - * Return: pointer to struct fpga_manager or NULL
>>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>>>    */
>>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>> -                     const struct fpga_manager_ops *mops,
>>> -                     void *priv)
>>> +struct fpga_manager *
>>> +fpga_mgr_register(struct device *dev, const char *name,
>>> +          struct fpga_compat_id *compat_id,
>>> +          const struct fpga_manager_ops *mops, void *priv)
>>>   {
>>>       struct fpga_manager *mgr;
>>>       int id, ret;
>>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>           !mops->write_init || (!mops->write && !mops->write_sg) ||
>>>           (mops->write && mops->write_sg)) {
>>>           dev_err(dev, "Attempt to register without fpga_manager_ops\n");
>>> -        return NULL;
>>> +        return ERR_PTR(-EINVAL);
>>>       }
>>>         if (!name || !strlen(name)) {
>>>           dev_err(dev, "Attempt to register with no name!\n");
>>> -        return NULL;
>>> +        return ERR_PTR(-EINVAL);
>>>       }
>>>         mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>>>       if (!mgr)
>>> -        return NULL;
>>> +        return ERR_PTR(-ENOMEM);
>>>         id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
>>> -    if (id < 0)
>>> +    if (id < 0) {
>>> +        ret = id;
>> ida_* returns a normal error, so needs a wrapping in
>>
>> ret = ERR_PTR(id)
> Thanks - I'll fix that
Actually, if you follow the "goto error_kfree", you will see that ret is
wrapped in ERR_PTR() in the return statement, so id does not need to be
wrapped here.

>
>>>           goto error_kfree;
>>> +    }
>>>         mutex_init(&mgr->ref_mutex);
>>>         mgr->name = name;
>>> +    mgr->compat_id = compat_id;
>>>       mgr->mops = mops;
>>>       mgr->priv = priv;
>>>   -    device_initialize(&mgr->dev);
>>>       mgr->dev.class = fpga_mgr_class;
>>>       mgr->dev.groups = mops->groups;
>>>       mgr->dev.parent = dev;
>>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>       if (ret)
>>>           goto error_device;
>>>   -    return mgr;
>>> -
>>> -error_device:
>>> -    ida_simple_remove(&fpga_mgr_ida, id);
>>> -error_kfree:
>>> -    kfree(mgr);
>>> -
>>> -    return NULL;
>>> -}
>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>>> -
>>> -/**
>>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
>>> - * @mgr:    fpga manager struct
>>> - */
>>> -void fpga_mgr_free(struct fpga_manager *mgr)
>>> -{
>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>> -    kfree(mgr);
>>> -}
>>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
>>> -
>>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
>>> -{
>>> -    struct fpga_mgr_devres *dr = res;
>>> -
>>> -    fpga_mgr_free(dr->mgr);
>>> -}
>>> -
>>> -/**
>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>>> - * @dev:    fpga manager device from pdev
>>> - * @name:    fpga manager name
>>> - * @mops:    pointer to structure of fpga manager ops
>>> - * @priv:    fpga manager private data
>>> - *
>>> - * This function is intended for use in a FPGA manager driver's probe function.
>>> - * After the manager driver creates the manager struct with
>>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
>>> - * manager driver's remove function should call fpga_mgr_unregister().  The
>>> - * manager 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_mgr_register(), the struct will still get cleaned up.
>>> - *
>>> - * Return: pointer to struct fpga_manager or NULL
>>> - */
>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>> -                      const struct fpga_manager_ops *mops,
>>> -                      void *priv)
>>> -{
>>> -    struct fpga_mgr_devres *dr;
>>> -
>>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>>> -    if (!dr)
>>> -        return NULL;
>>> -
>>> -    dr->mgr = fpga_mgr_create(dev, name, mops, priv);
>>> -    if (!dr->mgr) {
>>> -        devres_free(dr);
>>> -        return NULL;
>>> -    }
>>> -
>>> -    devres_add(dev, dr);
>>> -
>>> -    return dr->mgr;
>>> -}
>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>>> -
>>> -/**
>>> - * fpga_mgr_register - register a FPGA manager
>>> - * @mgr: fpga manager struct
>>> - *
>>> - * Return: 0 on success, negative error code otherwise.
>>> - */
>>> -int fpga_mgr_register(struct fpga_manager *mgr)
>>> -{
>>> -    int ret;
>>> -
>>>       /*
>>>        * Initialize framework state by requesting low level driver read state
>>>        * from device.  FPGA may be in reset mode or may have been programmed
>>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>>        */
>>>       mgr->state = mgr->mops->state(mgr);
>>>   -    ret = device_add(&mgr->dev);
>>> -    if (ret)
>>> -        goto error_device;
>>> -
>>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
>> can we keep this dev_info() line and move it below device_register() ?
> In Greg's response to my driver, he said he said that a well-behaved driver does
> not need dev_info() calls...
>>> +    ret = device_register(&mgr->dev);
>>> +    if (ret) {
>>> +        put_device(&mgr->dev);
>> pr_err() here ?
> OK - I can add a message
>
>>> +        return ERR_PTR(ret);
>>> +    }
>>>   -    return 0;
>>> +    return mgr;
>>>     error_device:
>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>> +    ida_simple_remove(&fpga_mgr_ida, id);
>>> +error_kfree:
>>> +    kfree(mgr);
>>>   -    return ret;
>>> +    return ERR_PTR(ret);
>>>   }
>>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>>   @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>>   }
>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>>   -static int fpga_mgr_devres_match(struct device *dev, void *res,
>>> -                 void *match_data)
>>> -{
>>> -    struct fpga_mgr_devres *dr = res;
>>> -
>>> -    return match_data == dr->mgr;
>>> -}
>>> -
>>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>   {
>>>       struct fpga_mgr_devres *dr = res;
>>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>     /**
>>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>>> - * @dev: managing device for this FPGA manager
>>> - * @mgr: fpga manager struct
>>> + * @dev:    fpga manager device from pdev
>>> + * @name:    fpga manager name
>>> + * @compat_id:    FPGA manager id for compatibility check.
>>> + * @mops:    pointer to structure of fpga manager ops
>>> + * @priv:    fpga manager private data
>>>    *
>>>    * This is the devres variant of fpga_mgr_register() for which the unregister
>>>    * function will be called automatically when the managing device is detached.
>>>    */
>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>>> +struct fpga_manager *
>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>> +               struct fpga_compat_id *compat_id,
>>> +               const struct fpga_manager_ops *mops, void *priv)
>>>   {
>>>       struct fpga_mgr_devres *dr;
>>> -    int ret;
>>> -
>>> -    /*
>>> -     * Make sure that the struct fpga_manager * that is passed in is
>>> -     * managed itself.
>>> -     */
>>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>>> -                 fpga_mgr_devres_match, mgr)))
>>> -        return -EINVAL;
>>> +    struct fpga_manager *mgr;
>>>         dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>>       if (!dr)
>>> -        return -ENOMEM;
>>> +        return ERR_PTR(-ENOMEM);
>>>   -    ret = fpga_mgr_register(mgr);
>>> -    if (ret) {
>>> +    mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
>>> +    if (IS_ERR(mgr)) {
>>>           devres_free(dr);
>>> -        return ret;
>>> +        return mgr;
>>>       }
>>>         dr->mgr = mgr;
>>>       devres_add(dev, dr);
>>>   -    return 0;
>>> +    return mgr;
>>>   }
>>>   EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>>>     static void fpga_mgr_dev_release(struct device *dev)
>>>   {
>>> +    struct fpga_manager *mgr = to_fpga_manager(dev);
>>> +
>>> +    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>> +    kfree(mgr);
>>>   }
>>>     static int __init fpga_mgr_class_init(void)
>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>>> index 69dec5af23c3..b1c7c0713e11 100644
>>> --- a/drivers/fpga/ice40-spi.c
>>> +++ b/drivers/fpga/ice40-spi.c
>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>>           return ret;
>>>       }
>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>>> -                   &ice40_fpga_ops, priv);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>>> +                     NULL, &ice40_fpga_ops, priv);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>> +    return 0;
>>>   }
>>>     static const struct of_device_id ice40_fpga_of_match[] = {
>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>>> index 114a64d2b7a4..75cc6efaa1b6 100644
>>> --- a/drivers/fpga/machxo2-spi.c
>>> +++ b/drivers/fpga/machxo2-spi.c
>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>>           return -EINVAL;
>>>       }
>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>>> -                   &machxo2_ops, spi);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>>> +                     NULL, &machxo2_ops, spi);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>> +    return 0;
>>>   }
>>>     static const struct of_device_id of_match[] = {
>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>>> index 573d88bdf730..80b8891fb302 100644
>>> --- a/drivers/fpga/socfpga-a10.c
>>> +++ b/drivers/fpga/socfpga-a10.c
>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>>           return -EBUSY;
>>>       }
>>>   -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>>> -                   &socfpga_a10_fpga_mgr_ops, priv);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> -
>>> -    platform_set_drvdata(pdev, mgr);
>>> -
>>> -    ret = fpga_mgr_register(mgr);
>>> -    if (ret) {
>>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>>> +                NULL, &socfpga_a10_fpga_mgr_ops, priv);
>>> +    if (IS_ERR(mgr)) {
>>>           clk_disable_unprepare(priv->clk);
>>> -        return ret;
>>> +        return PTR_ERR(mgr);
>>>       }
>>>   +    platform_set_drvdata(pdev, mgr);
>>> +
>>>       return 0;
>>>   }
>>>   diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>>> index 1f467173fc1f..06edfab8a496 100644
>>> --- a/drivers/fpga/socfpga.c
>>> +++ b/drivers/fpga/socfpga.c
>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>>       if (ret)
>>>           return ret;
>>>   -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>>> -                   &socfpga_fpga_ops, priv);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>>> +                     NULL, &socfpga_fpga_ops, priv);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>> +    return 0;
>>>   }
>>>     #ifdef CONFIG_OF
>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>>> index 657a70c5fc99..434094c3ac69 100644
>>> --- a/drivers/fpga/stratix10-soc.c
>>> +++ b/drivers/fpga/stratix10-soc.c
>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>>         init_completion(&priv->status_return_completion);
>>>   -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>>> -                  &s10_ops, priv);
>>> -    if (!mgr) {
>>> -        dev_err(dev, "unable to create FPGA manager\n");
>>> -        ret = -ENOMEM;
>>> -        goto probe_err;
>>> -    }
>>> -
>>> -    ret = fpga_mgr_register(mgr);
>>> -    if (ret) {
>>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>>> +                NULL, &s10_ops, priv);
>>> +    if (IS_ERR(mgr)) {
>>>           dev_err(dev, "unable to register FPGA manager\n");
>>> -        fpga_mgr_free(mgr);
>>> +        ret = PTR_ERR(mgr);
>>>           goto probe_err;
>>>       }
>>>   diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>>> index 101f016c6ed8..5efbec2b9f9f 100644
>>> --- a/drivers/fpga/ts73xx-fpga.c
>>> +++ b/drivers/fpga/ts73xx-fpga.c
>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>>       if (IS_ERR(priv->io_base))
>>>           return PTR_ERR(priv->io_base);
>>>   -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>>> -                   &ts73xx_fpga_ops, priv);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>> This is missing compat_id param.
> Thanks - I thought I'll do another pass and make sure I catch them all.
>
>> Similar for others.
>>
>>> +                     &ts73xx_fpga_ops, priv);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(kdev, mgr);
>>> +    return 0;
>>>   }
>>>     static struct platform_driver ts73xx_fpga_driver = {
>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>>> index fee4d0abf6bf..6f1894929687 100644
>>> --- a/drivers/fpga/xilinx-spi.c
>>> +++ b/drivers/fpga/xilinx-spi.c
>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>>           return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>>                        "Failed to get DONE gpio\n");
>>>   -    mgr = devm_fpga_mgr_create(&spi->dev,
>>> -                   "Xilinx Slave Serial FPGA Manager",
>>> -                   &xilinx_spi_ops, conf);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> +    mgr = devm_fpga_mgr_register(&spi->dev,
>>> +                     "Xilinx Slave Serial FPGA Manager",
>>> +                     NULL, &xilinx_spi_ops, conf);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>> +    return 0;
>>>   }
>>>     static const struct of_device_id xlnx_spi_of_match[] = {
>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>>> index 07fa8d9ec675..74bbb9710f4e 100644
>>> --- a/drivers/fpga/zynq-fpga.c
>>> +++ b/drivers/fpga/zynq-fpga.c
>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>>         clk_disable(priv->clk);
>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>>> -                   &zynq_fpga_ops, priv);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> -
>>> -    platform_set_drvdata(pdev, mgr);
>>> -
>>> -    err = fpga_mgr_register(mgr);
>>> -    if (err) {
>>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>> This is missing compat_id param as well.
>>> +                &zynq_fpga_ops, priv);
>>> +    if (IS_ERR(mgr)) {
>>>           dev_err(dev, "unable to register FPGA manager\n");
>>>           clk_unprepare(priv->clk);
>>> -        return err;
>>> +        return PTR_ERR(mgr);
>>>       }
>>>   +    platform_set_drvdata(pdev, mgr);
>>> +
>>>       return 0;
>>>   }
>>>   diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>>> index 125743c9797f..1a6d520985be 100644
>>> --- a/drivers/fpga/zynqmp-fpga.c
>>> +++ b/drivers/fpga/zynqmp-fpga.c
>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>>         priv->dev = dev;
>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>>> -                   &zynqmp_fpga_ops, priv);
>>> -    if (!mgr)
>>> -        return -ENOMEM;
>>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>>> +                     &zynqmp_fpga_ops, priv);
>>> +    if (IS_ERR(mgr))
>>> +        return PTR_ERR(mgr);
>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>> +    return 0;
>>>   }
>>>     static const struct of_device_id zynqmp_fpga_of_match[] = {
>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>>> index 2bc3030a69e5..3e1859cc0640 100644
>>> --- a/include/linux/fpga/fpga-mgr.h
>>> +++ b/include/linux/fpga/fpga-mgr.h
>>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>>     void fpga_mgr_put(struct fpga_manager *mgr);
>>>   -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>> -                     const struct fpga_manager_ops *mops,
>>> -                     void *priv);
>> My biggest concern is that removing this will break all oot fpga drivers.
>>
>> The other patches have similar api changes
>>
>> Having a stable api is very important.
> That is a good point. While Greg did call out the separation of the
> device_initialize() and device_add() calls, his biggest concern was with
> the empty dev_release() functions. Maybe it would be best to first submit
> a patch that only addresses the dev_release() functions? Maybe it would be
> OK to leave the create and register functions as separate functions in order
> to maintain a stable API...

On further investigation, I don't think we have much choice in changing
the API. We are almost coming full circle here. In 2018, there was only
an fpga_mgr_register() function. It was returning an errno value or zero.
It was also setting the drvdata of the parent driver with a pointer to
the resulting fpga_manager structure.

It looks like the reason the interface was split into two (create and
register) was so that the fpga_manager pointer could be returned (by the
create function) without writing the drvdata of the parent driver:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7085e2a94f7df5f419e3cfb2fe809ce6564e9629

When the interfaces were split, fpga_mgr_free() was also created to free
the memory if the register function was not called. The fpga_mgr_free()
function was also called by the fpga_mgr_dev_release() function. A later
patch was submitted to simplify the memory management for the class driver:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=084181fe8cc7472695b907f0018f4cd00934cb12

It is this second commit that empties the fpga_mgr_dev_release() function
and instead uses devm_fpga_mgr_create() to manage the memory for the class
driver.

The fix I am proposing essentially takes us back to the 2018 implementation,
but returns the fpga_manager pointer or ERR_PTR() instead of returning zero or
an errno.

The definition of the API is integral to the memory management issue. I cannot
think of an acceptable solution for the upstream that doesn't involve
changing the API. It could result in some ifdef's in an out-of-tree driver,
but I don't think we can avoid it in this case.

> - Russ
>
>> My recommendation is to keep the public api and hide the change with an exported but not public function.
>>
>> Tom
>>
>>> -void fpga_mgr_free(struct fpga_manager *mgr);
>>> -int fpga_mgr_register(struct fpga_manager *mgr);
>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>>> +                       struct fpga_compat_id *compat_id,
>>> +                       const struct fpga_manager_ops *mops,
>>> +                       void *priv);
>>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
>>>   -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>>> -
>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>> -                      const struct fpga_manager_ops *mops,
>>> -                      void *priv);
>>> +struct fpga_manager *
>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>> +               struct fpga_compat_id *compat_id,
>>> +               const struct fpga_manager_ops *mops,
>>> +               void *priv);
>>>     #endif /*_LINUX_FPGA_MGR_H */


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 0/3] fpga: Use standard class dev_release function
  2021-05-31 15:24 ` [PATCH v4 0/3] fpga: Use standard class dev_release function Xu Yilun
@ 2021-06-03 22:49   ` Russ Weight
  0 siblings, 0 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-03 22:49 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong



On 5/31/21 8:24 AM, Xu Yilun wrote:
> On Fri, May 28, 2021 at 02:57:52PM -0700, Russ Weight wrote:
>> The FPGA framework has a convention of using managed resource
>> functions to allow parent drivers to manage the data structures
>> allocated by the class drivers. They use an empty *_dev_release()
>> function to satisfy the class driver.
>>
>> This is inconsistent with linux driver model.
>>
>> These changes remove the managed resource functions and populate
>> the class dev_release callback functions. They also merge the
>> create and register functions into a single register function for
>> each of the fpga-mgr, fpga-region, and fpga-bridge class drivers.
>>
>> For more context, refer to this email thread:
>>
>> https://marc.info/?l=linux-fpga&m=162127412218557&w=2
>>
>> I turned on the configs assocated with each of the modified files,
>> but I must have been missing some dependencies, because not all
>> of them compiled. I did a run-time test specifically with the
>> dfl-fme infrastructure. This would have exercised the region,
>> bridge, and fpga-mgr frameworks.
>>
>> Changelog v3 -> v4:
>>   - Added the compat_id parameter to fpga_mgr_register() and
>>     devm_fpga_mgr_register() to ensure that the compat_id is set before
>>     the device_register() call.
>>   - Added the compat_id parameter to fpga_bridge_register() to ensure
> A typo                             ... fpga_region_register() ...
Thanks Yilun - I'll fix that.

- Russ
>
> Thanks,
> Yilun
>
>>     that the compat_id is set before the device_register() call.
>>   - Modified the dfl_fpga_feature_devs_enumerate() function to restore
>>     the fpga_region_register() call to the correct location.
>>
>> Changelog v2 -> v3:
>>   - Cleaned up comment headers for fpga_mgr_register(), fpga_bridge_register(),
>>     and fpga_region_register().
>>   - Fixed error return on ida_simple_get() failure for fpga_mgr_register(),
>>     fpga_bridge_register(), and fpga_region_register().
>>   - Fixed error return value for fpga_bridge_register(): ERR_PTR(ret) instead
>>     of NULL.
>>
>> Changelog v1 -> v2:
>>   - Restored devm_fpga_mgr_register() functionality to the fpga-mgr
>>     class driver, adapted for the combined create/register functionality.
>>   - All previous callers of devm_fpga_mgr_register() will continue to call
>>     devm_fpga_mgr_register().
>>   - replaced unnecessary ternary operators in return statements with
>>     standard if conditions.
>>
>> Russ Weight (3):
>>   fpga: mgr: Use standard dev_release for class driver
>>   fpga: bridge: Use standard dev_release for class driver
>>   fpga: region: Use standard dev_release for class driver
>>
>>  drivers/fpga/altera-cvp.c           |  12 +-
>>  drivers/fpga/altera-fpga2sdram.c    |  12 +-
>>  drivers/fpga/altera-freeze-bridge.c |  10 +-
>>  drivers/fpga/altera-hps2fpga.c      |  12 +-
>>  drivers/fpga/altera-pr-ip-core.c    |   9 +-
>>  drivers/fpga/altera-ps-spi.c        |  10 +-
>>  drivers/fpga/dfl-fme-br.c           |  10 +-
>>  drivers/fpga/dfl-fme-mgr.c          |  12 +-
>>  drivers/fpga/dfl-fme-region.c       |  12 +-
>>  drivers/fpga/dfl.c                  |  12 +-
>>  drivers/fpga/fpga-bridge.c          | 119 +++++---------------
>>  drivers/fpga/fpga-mgr.c             | 168 ++++++++--------------------
>>  drivers/fpga/fpga-region.c          | 106 ++++--------------
>>  drivers/fpga/ice40-spi.c            |  10 +-
>>  drivers/fpga/machxo2-spi.c          |  10 +-
>>  drivers/fpga/of-fpga-region.c       |  10 +-
>>  drivers/fpga/socfpga-a10.c          |  16 +--
>>  drivers/fpga/socfpga.c              |  10 +-
>>  drivers/fpga/stratix10-soc.c        |  15 +--
>>  drivers/fpga/ts73xx-fpga.c          |  10 +-
>>  drivers/fpga/xilinx-pr-decoupler.c  |  17 +--
>>  drivers/fpga/xilinx-spi.c           |  12 +-
>>  drivers/fpga/zynq-fpga.c            |  16 +--
>>  drivers/fpga/zynqmp-fpga.c          |  10 +-
>>  include/linux/fpga/fpga-bridge.h    |  12 +-
>>  include/linux/fpga/fpga-mgr.h       |  19 ++--
>>  include/linux/fpga/fpga-region.h    |  13 +--
>>  27 files changed, 210 insertions(+), 474 deletions(-)
>>
>> -- 
>> 2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 3/3] fpga: region: Use standard dev_release for class driver
  2021-05-31 15:23   ` Xu Yilun
@ 2021-06-03 22:55     ` Russ Weight
  0 siblings, 0 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-03 22:55 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong



On 5/31/21 8:23 AM, Xu Yilun wrote:
> On Fri, May 28, 2021 at 02:57:55PM -0700, Russ Weight wrote:
>> The FPGA region class driver data structure is being treated as a
>> managed resource instead of using standard dev_release call-back
>> 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>
>> ---
>> v4:
>>   - Added the compat_id parameter to fpga_bridge_register() to ensure
> A typo                           ...   fpga_region_register() ...

Thanks - I'll fix it

>>     that the compat_id is set before the device_register() call.
>>   - Modified the dfl_fpga_feature_devs_enumerate() function to restore
>>     the fpga_region_register() call to the correct location.
>> v3:
>>   - Cleaned up comment header for fpga_region_register()
>>   - Fix fpga_region_register() error return on ida_simple_get() failure
>> v2:
>>   - No changes
>> ---
>>  drivers/fpga/dfl-fme-region.c    |  12 ++--
>>  drivers/fpga/dfl.c               |  12 ++--
>>  drivers/fpga/fpga-region.c       | 106 +++++++------------------------
>>  drivers/fpga/of-fpga-region.c    |  10 +--
>>  include/linux/fpga/fpga-region.h |  13 ++--
>>  5 files changed, 38 insertions(+), 115 deletions(-)
>>
>> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
>> index 1eeb42af1012..9e44c623a5e9 100644
>> --- a/drivers/fpga/dfl-fme-region.c
>> +++ b/drivers/fpga/dfl-fme-region.c
>> @@ -39,20 +39,16 @@ static int fme_region_probe(struct platform_device *pdev)
>>  	if (IS_ERR(mgr))
>>  		return -EPROBE_DEFER;
>>  
>> -	region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges);
>> -	if (!region) {
>> -		ret = -ENOMEM;
>> +	region = fpga_region_register(dev, mgr, mgr->compat_id,
>> +				      fme_region_get_bridges);
>> +	if (IS_ERR(region)) {
>> +		ret = PTR_ERR(region);
>>  		goto eprobe_mgr_put;
>>  	}
>>  
>>  	region->priv = pdata;
>> -	region->compat_id = mgr->compat_id;
> I think the region->priv has the same issue as compat_id, could we
> introduce a fpga_region_ops for fpga_region_register, wrapping all
> input parameters, such as mgr, (*get_bridges)(), priv, compat_id?

OK - I'll look at making this change.

>
>>  	platform_set_drvdata(pdev, region);
>>  
>> -	ret = fpga_region_register(region);
>> -	if (ret)
>> -		goto eprobe_mgr_put;
>> -
>>  	dev_dbg(dev, "DFL FME FPGA Region probed\n");
>>  
>>  	return 0;
>> diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
>> index 511b20ff35a3..6e628c07c1dc 100644
>> --- a/drivers/fpga/dfl.c
>> +++ b/drivers/fpga/dfl.c
>> @@ -1400,19 +1400,15 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
>>  	if (!cdev)
>>  		return ERR_PTR(-ENOMEM);
>>  
>> -	cdev->region = devm_fpga_region_create(info->dev, NULL, NULL);
>> -	if (!cdev->region) {
>> -		ret = -ENOMEM;
>> -		goto free_cdev_exit;
>> -	}
>> -
>>  	cdev->parent = info->dev;
>>  	mutex_init(&cdev->lock);
>>  	INIT_LIST_HEAD(&cdev->port_dev_list);
>>  
>> -	ret = fpga_region_register(cdev->region);
>> -	if (ret)
>> +	cdev->region = fpga_region_register(info->dev, NULL, NULL, NULL);
>> +	if (IS_ERR(cdev->region)) {
>> +		ret = PTR_ERR(cdev->region);
>>  		goto free_cdev_exit;
>> +	}
>>  
>>  	/* create and init build info for enumeration */
>>  	binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
>> diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
>> index c3134b89c3fe..3d28880bf1cc 100644
>> --- a/drivers/fpga/fpga-region.c
>> +++ b/drivers/fpga/fpga-region.c
>> @@ -180,39 +180,38 @@ static struct attribute *fpga_region_attrs[] = {
>>  ATTRIBUTE_GROUPS(fpga_region);
>>  
>>  /**
>> - * fpga_region_create - alloc and init a struct fpga_region
>> + * fpga_region_register - create and register a FPGA Region device
>>   * @dev: device parent
>>   * @mgr: manager that programs this region
>> + * @compat_id: FPGA manager id for compatibility check.
>>   * @get_bridges: optional function to get bridges to a list
>>   *
>> - * The caller of this function is responsible for freeing the resulting region
>> - * struct with fpga_region_free().  Using devm_fpga_region_create() instead is
>> - * recommended.
>> - *
>> - * Return: struct fpga_region or NULL
>> + * Return struct fpga_region pointer on success, or ERR_PTR() on error.
> Please also restore the doc format.

will do

>
>>   */
>> -struct fpga_region
>> -*fpga_region_create(struct device *dev,
>> -		    struct fpga_manager *mgr,
>> -		    int (*get_bridges)(struct fpga_region *))
>> +struct fpga_region *
>> +fpga_region_register(struct device *dev, struct fpga_manager *mgr,
>> +		     struct fpga_compat_id *compat_id,
>> +		     int (*get_bridges)(struct fpga_region *))
>>  {
>>  	struct fpga_region *region;
>>  	int id, ret = 0;
>>  
>>  	region = kzalloc(sizeof(*region), GFP_KERNEL);
>>  	if (!region)
>> -		return NULL;
>> +		return ERR_PTR(-ENOMEM);
>>  
>>  	id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
>> -	if (id < 0)
>> +	if (id < 0) {
>> +		ret = id;
>>  		goto err_free;
>> +	}
>>  
>>  	region->mgr = mgr;
>> +	region->compat_id = compat_id;
>>  	region->get_bridges = get_bridges;
>>  	mutex_init(&region->mutex);
>>  	INIT_LIST_HEAD(&region->bridge_list);
>>  
>> -	device_initialize(&region->dev);
>>  	region->dev.class = fpga_region_class;
>>  	region->dev.parent = dev;
>>  	region->dev.of_node = dev->of_node;
>> @@ -222,6 +221,12 @@ struct fpga_region
>>  	if (ret)
>>  		goto err_remove;
>>  
>> +	ret = device_register(&region->dev);
>> +	if (ret) {
>> +		put_device(&region->dev);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>>  	return region;
>>  
>>  err_remove:
>> @@ -229,76 +234,7 @@ struct fpga_region
>>  err_free:
>>  	kfree(region);
>>  
>> -	return NULL;
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_region_create);
>> -
>> -/**
>> - * fpga_region_free - free a FPGA region created by fpga_region_create()
>> - * @region: FPGA region
>> - */
>> -void fpga_region_free(struct fpga_region *region)
>> -{
>> -	ida_simple_remove(&fpga_region_ida, region->dev.id);
>> -	kfree(region);
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_region_free);
>> -
>> -static void devm_fpga_region_release(struct device *dev, void *res)
>> -{
>> -	struct fpga_region *region = *(struct fpga_region **)res;
>> -
>> -	fpga_region_free(region);
>> -}
>> -
>> -/**
>> - * devm_fpga_region_create - create and initialize a managed FPGA region struct
>> - * @dev: device parent
>> - * @mgr: manager that programs this region
>> - * @get_bridges: optional function to get bridges to a list
>> - *
>> - * This function is intended for use in a FPGA region driver's probe function.
>> - * After the region driver creates the region struct with
>> - * devm_fpga_region_create(), it should register it with fpga_region_register().
>> - * The region driver's remove function should call fpga_region_unregister().
>> - * The region 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_region_register(), the struct will still get cleaned up.
>> - *
>> - * Return: struct fpga_region or NULL
>> - */
>> -struct fpga_region
>> -*devm_fpga_region_create(struct device *dev,
>> -			 struct fpga_manager *mgr,
>> -			 int (*get_bridges)(struct fpga_region *))
>> -{
>> -	struct fpga_region **ptr, *region;
>> -
>> -	ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL);
>> -	if (!ptr)
>> -		return NULL;
>> -
>> -	region = fpga_region_create(dev, mgr, get_bridges);
>> -	if (!region) {
>> -		devres_free(ptr);
>> -	} else {
>> -		*ptr = region;
>> -		devres_add(dev, ptr);
>> -	}
>> -
>> -	return region;
>> -}
>> -EXPORT_SYMBOL_GPL(devm_fpga_region_create);
>> -
>> -/**
>> - * fpga_region_register - register a FPGA region
>> - * @region: FPGA region
>> - *
>> - * Return: 0 or -errno
>> - */
>> -int fpga_region_register(struct fpga_region *region)
>> -{
>> -	return device_add(&region->dev);
>> +	return ERR_PTR(ret);
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_region_register);
>>  
>> @@ -316,6 +252,10 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister);
>>  
>>  static void fpga_region_dev_release(struct device *dev)
>>  {
>> +	struct fpga_region *region = to_fpga_region(dev);
>> +
>> +	ida_simple_remove(&fpga_region_ida, region->dev.id);
>> +	kfree(region);
>>  }
>>  
>>  /**
>> diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
>> index e405309baadc..c115e5fc0c62 100644
>> --- a/drivers/fpga/of-fpga-region.c
>> +++ b/drivers/fpga/of-fpga-region.c
>> @@ -405,16 +405,12 @@ static int of_fpga_region_probe(struct platform_device *pdev)
>>  	if (IS_ERR(mgr))
>>  		return -EPROBE_DEFER;
>>  
>> -	region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges);
>> -	if (!region) {
>> -		ret = -ENOMEM;
>> +	region = fpga_region_register(dev, mgr, NULL, of_fpga_region_get_bridges);
>> +	if (IS_ERR(region)) {
>> +		ret = PTR_ERR(region);
>>  		goto eprobe_mgr_put;
>>  	}
>>  
>> -	ret = fpga_region_register(region);
>> -	if (ret)
>> -		goto eprobe_mgr_put;
>> -
>>  	of_platform_populate(np, fpga_region_of_match, NULL, &region->dev);
>>  	platform_set_drvdata(pdev, region);
>>  
>> diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
>> index 27cb706275db..1e4c2dc7a63b 100644
>> --- a/include/linux/fpga/fpga-region.h
>> +++ b/include/linux/fpga/fpga-region.h
>> @@ -37,15 +37,10 @@ struct fpga_region *fpga_region_class_find(
>>  
>>  int fpga_region_program_fpga(struct fpga_region *region);
>>  
>> -struct fpga_region
>> -*fpga_region_create(struct device *dev, struct fpga_manager *mgr,
>> -		    int (*get_bridges)(struct fpga_region *));
>> -void fpga_region_free(struct fpga_region *region);
>> -int fpga_region_register(struct fpga_region *region);
>> +struct fpga_region *
>> +fpga_region_register(struct device *dev, struct fpga_manager *mgr,
>> +		     struct fpga_compat_id *compat_id,
>> +		     int (*get_bridges)(struct fpga_region *));
> Same concern as fpga_mgr_register, shall we introduce a fpga_region_ops
> to wrap mgr, (*get_bridges), compat_id and priv? So we don't have to change
> all the callers everytime we need a new input for fpga_region.

Sure -I'll look at doing this for both the fpga-mgr and fpga-region code.

- Russ
>
> Thanks,
> Yilun
>
>>  void fpga_region_unregister(struct fpga_region *region);
>>  
>> -struct fpga_region
>> -*devm_fpga_region_create(struct device *dev, struct fpga_manager *mgr,
>> -			int (*get_bridges)(struct fpga_region *));
>> -
>>  #endif /* _FPGA_REGION_H */
>> -- 
>> 2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-03 22:45       ` Russ Weight
@ 2021-06-04 20:56         ` Tom Rix
  2021-06-04 21:17           ` Russ Weight
  2021-06-05 15:42         ` Xu Yilun
  1 sibling, 1 reply; 19+ messages in thread
From: Tom Rix @ 2021-06-04 20:56 UTC (permalink / raw)
  To: Russ Weight, mdf, linux-fpga
  Cc: lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong


On 6/3/21 3:45 PM, Russ Weight wrote:
>
> On 6/1/21 5:38 PM, Russ Weight wrote:
>> On 6/1/21 4:24 PM, Tom Rix wrote:
>>> On 5/28/21 2:57 PM, Russ Weight wrote:
>>>> The FPGA manager class driver data structure is being treated as a
>>>> managed resource instead of using standard dev_release call-back
>>>> to release the class data structure. This change removes the
>>>> managed resource code for the freeing of the class data structure
>>>> and combines the create() and register() functions into a single
>>>> register() function.
>>>>
>>>> The devm_fpga_mgr_register() function is retained.
>>>>
>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>>> ---
>>>> v4:
>>>>     - Added the compat_id parameter to fpga_mgr_register() and
>>> On a refactor, it is best to do the refactoring as mechanically as possible.
>>>
>>> While compat_id may be useful, it should be done in a separate patch.
>> These would have to be included with the changes merge the create and register
>> functions into a single function.
>>
>>> Unless it is really needed for your next security update patch consider resubmitting later
>>>
>>>>       devm_fpga_mgr_register() to ensure that the compat_id is set before
>>>>       the device_register() call.
>>>> v3:
>>>>     - Cleaned up comment header for fpga_mgr_register()
>>>>     - Fix error return on ida_simple_get() failure
>>>> v2:
>>>>     - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>>>       create/register functionality.
>>>>     - All previous callers of devm_fpga_mgr_register() will continue to call
>>>>       devm_fpga_mgr_register().
>>>>     - replaced unnecessary ternary operators in return statements with standard
>>>>       if conditions.
>>>> ---
>>>>    drivers/fpga/altera-cvp.c        |  12 +--
>>>>    drivers/fpga/altera-pr-ip-core.c |   9 +-
>>>>    drivers/fpga/altera-ps-spi.c     |  10 +-
>>>>    drivers/fpga/dfl-fme-mgr.c       |  12 +--
>>>>    drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>>>>    drivers/fpga/ice40-spi.c         |  10 +-
>>>>    drivers/fpga/machxo2-spi.c       |  10 +-
>>>>    drivers/fpga/socfpga-a10.c       |  16 ++-
>>>>    drivers/fpga/socfpga.c           |  10 +-
>>>>    drivers/fpga/stratix10-soc.c     |  15 +--
>>>>    drivers/fpga/ts73xx-fpga.c       |  10 +-
>>>>    drivers/fpga/xilinx-spi.c        |  12 +--
>>>>    drivers/fpga/zynq-fpga.c         |  16 ++-
>>>>    drivers/fpga/zynqmp-fpga.c       |  10 +-
>>>>    include/linux/fpga/fpga-mgr.h    |  19 ++--
>>>>    15 files changed, 120 insertions(+), 219 deletions(-)
>>> ./Documentation/driver-api/fpga/fpga-mgr.rst
>>>
>>> still refers to fpga_mgr_create.
>> Thanks for catching that.
>>
>>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>>>> index 4e0edb60bfba..25eaa97ae7aa 100644
>>>> --- a/drivers/fpga/altera-cvp.c
>>>> +++ b/drivers/fpga/altera-cvp.c
>>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>>>        snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>>>             ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>>>    -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>>>> -                   &altera_cvp_ops, conf);
>>>> -    if (!mgr) {
>>>> -        ret = -ENOMEM;
>>>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
>>>> +                &altera_cvp_ops, conf);
>>>> +    if (IS_ERR(mgr)) {
>>>> +        ret = PTR_ERR(mgr);
>>>>            goto err_unmap;
>>>>        }
>>>>          pci_set_drvdata(pdev, mgr);
>>>>    -    ret = fpga_mgr_register(mgr);
>>>> -    if (ret)
>>>> -        goto err_unmap;
>>>> -
>>>>        return 0;
>>>>      err_unmap:
>>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>>>> index 5b130c4d9882..bfd4de1e7ac5 100644
>>>> --- a/drivers/fpga/altera-pr-ip-core.c
>>>> +++ b/drivers/fpga/altera-pr-ip-core.c
>>>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>>>            (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>>>            (int)(val & ALT_PR_CSR_PR_START));
>>>>    -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
>>>> +                     &alt_pr_ops, priv);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>> +    return 0;
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(alt_pr_register);
>>>>    diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>>>> index 23bfd4d1ad0f..e6fc7208213c 100644
>>>> --- a/drivers/fpga/altera-ps-spi.c
>>>> +++ b/drivers/fpga/altera-ps-spi.c
>>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>>>        snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>>>             dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>>>    -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>>>> -                   &altera_ps_ops, conf);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
>>>> +                     &altera_ps_ops, conf);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>> +    return 0;
>>>>    }
>>>>      static const struct spi_device_id altera_ps_spi_ids[] = {
>>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>>>> index d5861d13b306..c9b98afa7d50 100644
>>>> --- a/drivers/fpga/dfl-fme-mgr.c
>>>> +++ b/drivers/fpga/dfl-fme-mgr.c
>>>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>>>          fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>>>    -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>>>> -                   &fme_mgr_ops, priv);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> -
>>>> -    mgr->compat_id = compat_id;
>>>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
>>>> +                     &fme_mgr_ops, priv);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>> +    return 0;
>>>>    }
>>>>      static struct platform_driver fme_mgr_driver = {
>>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>>>> index b85bc47c91a9..d6d64ce34417 100644
>>>> --- a/drivers/fpga/fpga-mgr.c
>>>> +++ b/drivers/fpga/fpga-mgr.c
>>>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>>>    EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>>>      /**
>>>> - * fpga_mgr_create - create and initialize a FPGA manager struct
>>>> + * fpga_mgr_register - create and register a FPGA Manager device
>>>>     * @dev:    fpga manager device from pdev
>>>>     * @name:    fpga manager name
>>>> + * @compat_id:    FPGA manager id for compatibility check.
>>> for consistency, use lowercase fpga
>>>
>>> similar issue below.
>> OK
>>
>>>>     * @mops:    pointer to structure of fpga manager ops
>>>>     * @priv:    fpga manager private data
>>>>     *
>>>> - * The caller of this function is responsible for freeing the struct with
>>>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
>>>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
>>>> + * Using devm_fpga_mgr_register instead is recommended.
>>>>     *
>>>> - * Return: pointer to struct fpga_manager or NULL
>>>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>>>>     */
>>>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>> -                     const struct fpga_manager_ops *mops,
>>>> -                     void *priv)
>>>> +struct fpga_manager *
>>>> +fpga_mgr_register(struct device *dev, const char *name,
>>>> +          struct fpga_compat_id *compat_id,
>>>> +          const struct fpga_manager_ops *mops, void *priv)
>>>>    {
>>>>        struct fpga_manager *mgr;
>>>>        int id, ret;
>>>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>            !mops->write_init || (!mops->write && !mops->write_sg) ||
>>>>            (mops->write && mops->write_sg)) {
>>>>            dev_err(dev, "Attempt to register without fpga_manager_ops\n");
>>>> -        return NULL;
>>>> +        return ERR_PTR(-EINVAL);
>>>>        }
>>>>          if (!name || !strlen(name)) {
>>>>            dev_err(dev, "Attempt to register with no name!\n");
>>>> -        return NULL;
>>>> +        return ERR_PTR(-EINVAL);
>>>>        }
>>>>          mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>>>>        if (!mgr)
>>>> -        return NULL;
>>>> +        return ERR_PTR(-ENOMEM);
>>>>          id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
>>>> -    if (id < 0)
>>>> +    if (id < 0) {
>>>> +        ret = id;
>>> ida_* returns a normal error, so needs a wrapping in
>>>
>>> ret = ERR_PTR(id)
>> Thanks - I'll fix that
> Actually, if you follow the "goto error_kfree", you will see that ret is
> wrapped in ERR_PTR() in the return statement, so id does not need to be
> wrapped here.
>
>>>>            goto error_kfree;
>>>> +    }
>>>>          mutex_init(&mgr->ref_mutex);
>>>>          mgr->name = name;
>>>> +    mgr->compat_id = compat_id;
>>>>        mgr->mops = mops;
>>>>        mgr->priv = priv;
>>>>    -    device_initialize(&mgr->dev);
>>>>        mgr->dev.class = fpga_mgr_class;
>>>>        mgr->dev.groups = mops->groups;
>>>>        mgr->dev.parent = dev;
>>>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>        if (ret)
>>>>            goto error_device;
>>>>    -    return mgr;
>>>> -
>>>> -error_device:
>>>> -    ida_simple_remove(&fpga_mgr_ida, id);
>>>> -error_kfree:
>>>> -    kfree(mgr);
>>>> -
>>>> -    return NULL;
>>>> -}
>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>>>> -
>>>> -/**
>>>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
>>>> - * @mgr:    fpga manager struct
>>>> - */
>>>> -void fpga_mgr_free(struct fpga_manager *mgr)
>>>> -{
>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>> -    kfree(mgr);
>>>> -}
>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
>>>> -
>>>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
>>>> -{
>>>> -    struct fpga_mgr_devres *dr = res;
>>>> -
>>>> -    fpga_mgr_free(dr->mgr);
>>>> -}
>>>> -
>>>> -/**
>>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>>>> - * @dev:    fpga manager device from pdev
>>>> - * @name:    fpga manager name
>>>> - * @mops:    pointer to structure of fpga manager ops
>>>> - * @priv:    fpga manager private data
>>>> - *
>>>> - * This function is intended for use in a FPGA manager driver's probe function.
>>>> - * After the manager driver creates the manager struct with
>>>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
>>>> - * manager driver's remove function should call fpga_mgr_unregister().  The
>>>> - * manager 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_mgr_register(), the struct will still get cleaned up.
>>>> - *
>>>> - * Return: pointer to struct fpga_manager or NULL
>>>> - */
>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>> -                      const struct fpga_manager_ops *mops,
>>>> -                      void *priv)
>>>> -{
>>>> -    struct fpga_mgr_devres *dr;
>>>> -
>>>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>>>> -    if (!dr)
>>>> -        return NULL;
>>>> -
>>>> -    dr->mgr = fpga_mgr_create(dev, name, mops, priv);
>>>> -    if (!dr->mgr) {
>>>> -        devres_free(dr);
>>>> -        return NULL;
>>>> -    }
>>>> -
>>>> -    devres_add(dev, dr);
>>>> -
>>>> -    return dr->mgr;
>>>> -}
>>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>>>> -
>>>> -/**
>>>> - * fpga_mgr_register - register a FPGA manager
>>>> - * @mgr: fpga manager struct
>>>> - *
>>>> - * Return: 0 on success, negative error code otherwise.
>>>> - */
>>>> -int fpga_mgr_register(struct fpga_manager *mgr)
>>>> -{
>>>> -    int ret;
>>>> -
>>>>        /*
>>>>         * Initialize framework state by requesting low level driver read state
>>>>         * from device.  FPGA may be in reset mode or may have been programmed
>>>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>>>         */
>>>>        mgr->state = mgr->mops->state(mgr);
>>>>    -    ret = device_add(&mgr->dev);
>>>> -    if (ret)
>>>> -        goto error_device;
>>>> -
>>>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
>>> can we keep this dev_info() line and move it below device_register() ?
>> In Greg's response to my driver, he said he said that a well-behaved driver does
>> not need dev_info() calls...
>>>> +    ret = device_register(&mgr->dev);
>>>> +    if (ret) {
>>>> +        put_device(&mgr->dev);
>>> pr_err() here ?
>> OK - I can add a message
>>
>>>> +        return ERR_PTR(ret);
>>>> +    }
>>>>    -    return 0;
>>>> +    return mgr;
>>>>      error_device:
>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>> +    ida_simple_remove(&fpga_mgr_ida, id);
>>>> +error_kfree:
>>>> +    kfree(mgr);
>>>>    -    return ret;
>>>> +    return ERR_PTR(ret);
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>>>    @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>>>    -static int fpga_mgr_devres_match(struct device *dev, void *res,
>>>> -                 void *match_data)
>>>> -{
>>>> -    struct fpga_mgr_devres *dr = res;
>>>> -
>>>> -    return match_data == dr->mgr;
>>>> -}
>>>> -
>>>>    static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>    {
>>>>        struct fpga_mgr_devres *dr = res;
>>>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>      /**
>>>>     * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>>>> - * @dev: managing device for this FPGA manager
>>>> - * @mgr: fpga manager struct
>>>> + * @dev:    fpga manager device from pdev
>>>> + * @name:    fpga manager name
>>>> + * @compat_id:    FPGA manager id for compatibility check.
>>>> + * @mops:    pointer to structure of fpga manager ops
>>>> + * @priv:    fpga manager private data
>>>>     *
>>>>     * This is the devres variant of fpga_mgr_register() for which the unregister
>>>>     * function will be called automatically when the managing device is detached.
>>>>     */
>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>>>> +struct fpga_manager *
>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>> +               struct fpga_compat_id *compat_id,
>>>> +               const struct fpga_manager_ops *mops, void *priv)
>>>>    {
>>>>        struct fpga_mgr_devres *dr;
>>>> -    int ret;
>>>> -
>>>> -    /*
>>>> -     * Make sure that the struct fpga_manager * that is passed in is
>>>> -     * managed itself.
>>>> -     */
>>>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>>>> -                 fpga_mgr_devres_match, mgr)))
>>>> -        return -EINVAL;
>>>> +    struct fpga_manager *mgr;
>>>>          dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>>>        if (!dr)
>>>> -        return -ENOMEM;
>>>> +        return ERR_PTR(-ENOMEM);
>>>>    -    ret = fpga_mgr_register(mgr);
>>>> -    if (ret) {
>>>> +    mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
>>>> +    if (IS_ERR(mgr)) {
>>>>            devres_free(dr);
>>>> -        return ret;
>>>> +        return mgr;
>>>>        }
>>>>          dr->mgr = mgr;
>>>>        devres_add(dev, dr);
>>>>    -    return 0;
>>>> +    return mgr;
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>>>>      static void fpga_mgr_dev_release(struct device *dev)
>>>>    {
>>>> +    struct fpga_manager *mgr = to_fpga_manager(dev);
>>>> +
>>>> +    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>> +    kfree(mgr);
>>>>    }
>>>>      static int __init fpga_mgr_class_init(void)
>>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>>>> index 69dec5af23c3..b1c7c0713e11 100644
>>>> --- a/drivers/fpga/ice40-spi.c
>>>> +++ b/drivers/fpga/ice40-spi.c
>>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>>>            return ret;
>>>>        }
>>>>    -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>>>> -                   &ice40_fpga_ops, priv);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>>>> +                     NULL, &ice40_fpga_ops, priv);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>> +    return 0;
>>>>    }
>>>>      static const struct of_device_id ice40_fpga_of_match[] = {
>>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>>>> index 114a64d2b7a4..75cc6efaa1b6 100644
>>>> --- a/drivers/fpga/machxo2-spi.c
>>>> +++ b/drivers/fpga/machxo2-spi.c
>>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>>>            return -EINVAL;
>>>>        }
>>>>    -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>> -                   &machxo2_ops, spi);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>> +                     NULL, &machxo2_ops, spi);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>> +    return 0;
>>>>    }
>>>>      static const struct of_device_id of_match[] = {
>>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>>>> index 573d88bdf730..80b8891fb302 100644
>>>> --- a/drivers/fpga/socfpga-a10.c
>>>> +++ b/drivers/fpga/socfpga-a10.c
>>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>>>            return -EBUSY;
>>>>        }
>>>>    -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>>>> -                   &socfpga_a10_fpga_mgr_ops, priv);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> -
>>>> -    platform_set_drvdata(pdev, mgr);
>>>> -
>>>> -    ret = fpga_mgr_register(mgr);
>>>> -    if (ret) {
>>>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>>>> +                NULL, &socfpga_a10_fpga_mgr_ops, priv);
>>>> +    if (IS_ERR(mgr)) {
>>>>            clk_disable_unprepare(priv->clk);
>>>> -        return ret;
>>>> +        return PTR_ERR(mgr);
>>>>        }
>>>>    +    platform_set_drvdata(pdev, mgr);
>>>> +
>>>>        return 0;
>>>>    }
>>>>    diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>>>> index 1f467173fc1f..06edfab8a496 100644
>>>> --- a/drivers/fpga/socfpga.c
>>>> +++ b/drivers/fpga/socfpga.c
>>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>>>        if (ret)
>>>>            return ret;
>>>>    -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>>>> -                   &socfpga_fpga_ops, priv);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>>>> +                     NULL, &socfpga_fpga_ops, priv);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>> +    return 0;
>>>>    }
>>>>      #ifdef CONFIG_OF
>>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>>>> index 657a70c5fc99..434094c3ac69 100644
>>>> --- a/drivers/fpga/stratix10-soc.c
>>>> +++ b/drivers/fpga/stratix10-soc.c
>>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>>>          init_completion(&priv->status_return_completion);
>>>>    -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>>>> -                  &s10_ops, priv);
>>>> -    if (!mgr) {
>>>> -        dev_err(dev, "unable to create FPGA manager\n");
>>>> -        ret = -ENOMEM;
>>>> -        goto probe_err;
>>>> -    }
>>>> -
>>>> -    ret = fpga_mgr_register(mgr);
>>>> -    if (ret) {
>>>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>>>> +                NULL, &s10_ops, priv);
>>>> +    if (IS_ERR(mgr)) {
>>>>            dev_err(dev, "unable to register FPGA manager\n");
>>>> -        fpga_mgr_free(mgr);
>>>> +        ret = PTR_ERR(mgr);
>>>>            goto probe_err;
>>>>        }
>>>>    diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>>>> index 101f016c6ed8..5efbec2b9f9f 100644
>>>> --- a/drivers/fpga/ts73xx-fpga.c
>>>> +++ b/drivers/fpga/ts73xx-fpga.c
>>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>>>        if (IS_ERR(priv->io_base))
>>>>            return PTR_ERR(priv->io_base);
>>>>    -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>>>> -                   &ts73xx_fpga_ops, priv);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>>> This is missing compat_id param.
>> Thanks - I thought I'll do another pass and make sure I catch them all.
>>
>>> Similar for others.
>>>
>>>> +                     &ts73xx_fpga_ops, priv);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(kdev, mgr);
>>>> +    return 0;
>>>>    }
>>>>      static struct platform_driver ts73xx_fpga_driver = {
>>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>>>> index fee4d0abf6bf..6f1894929687 100644
>>>> --- a/drivers/fpga/xilinx-spi.c
>>>> +++ b/drivers/fpga/xilinx-spi.c
>>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>>>            return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>>>                         "Failed to get DONE gpio\n");
>>>>    -    mgr = devm_fpga_mgr_create(&spi->dev,
>>>> -                   "Xilinx Slave Serial FPGA Manager",
>>>> -                   &xilinx_spi_ops, conf);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> +    mgr = devm_fpga_mgr_register(&spi->dev,
>>>> +                     "Xilinx Slave Serial FPGA Manager",
>>>> +                     NULL, &xilinx_spi_ops, conf);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>> +    return 0;
>>>>    }
>>>>      static const struct of_device_id xlnx_spi_of_match[] = {
>>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>>>> index 07fa8d9ec675..74bbb9710f4e 100644
>>>> --- a/drivers/fpga/zynq-fpga.c
>>>> +++ b/drivers/fpga/zynq-fpga.c
>>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>>>          clk_disable(priv->clk);
>>>>    -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>>>> -                   &zynq_fpga_ops, priv);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> -
>>>> -    platform_set_drvdata(pdev, mgr);
>>>> -
>>>> -    err = fpga_mgr_register(mgr);
>>>> -    if (err) {
>>>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>>> This is missing compat_id param as well.
>>>> +                &zynq_fpga_ops, priv);
>>>> +    if (IS_ERR(mgr)) {
>>>>            dev_err(dev, "unable to register FPGA manager\n");
>>>>            clk_unprepare(priv->clk);
>>>> -        return err;
>>>> +        return PTR_ERR(mgr);
>>>>        }
>>>>    +    platform_set_drvdata(pdev, mgr);
>>>> +
>>>>        return 0;
>>>>    }
>>>>    diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>>>> index 125743c9797f..1a6d520985be 100644
>>>> --- a/drivers/fpga/zynqmp-fpga.c
>>>> +++ b/drivers/fpga/zynqmp-fpga.c
>>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>>>          priv->dev = dev;
>>>>    -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>>>> -                   &zynqmp_fpga_ops, priv);
>>>> -    if (!mgr)
>>>> -        return -ENOMEM;
>>>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>>>> +                     &zynqmp_fpga_ops, priv);
>>>> +    if (IS_ERR(mgr))
>>>> +        return PTR_ERR(mgr);
>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>> +    return 0;
>>>>    }
>>>>      static const struct of_device_id zynqmp_fpga_of_match[] = {
>>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>>>> index 2bc3030a69e5..3e1859cc0640 100644
>>>> --- a/include/linux/fpga/fpga-mgr.h
>>>> +++ b/include/linux/fpga/fpga-mgr.h
>>>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>>>      void fpga_mgr_put(struct fpga_manager *mgr);
>>>>    -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>> -                     const struct fpga_manager_ops *mops,
>>>> -                     void *priv);
>>> My biggest concern is that removing this will break all oot fpga drivers.
>>>
>>> The other patches have similar api changes
>>>
>>> Having a stable api is very important.
>> That is a good point. While Greg did call out the separation of the
>> device_initialize() and device_add() calls, his biggest concern was with
>> the empty dev_release() functions. Maybe it would be best to first submit
>> a patch that only addresses the dev_release() functions? Maybe it would be
>> OK to leave the create and register functions as separate functions in order
>> to maintain a stable API...
> On further investigation, I don't think we have much choice in changing
> the API. We are almost coming full circle here. In 2018, there was only
> an fpga_mgr_register() function. It was returning an errno value or zero.
> It was also setting the drvdata of the parent driver with a pointer to
> the resulting fpga_manager structure.
>
> It looks like the reason the interface was split into two (create and
> register) was so that the fpga_manager pointer could be returned (by the
> create function) without writing the drvdata of the parent driver:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7085e2a94f7df5f419e3cfb2fe809ce6564e9629
>
> When the interfaces were split, fpga_mgr_free() was also created to free
> the memory if the register function was not called. The fpga_mgr_free()
> function was also called by the fpga_mgr_dev_release() function. A later
> patch was submitted to simplify the memory management for the class driver:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=084181fe8cc7472695b907f0018f4cd00934cb12
>
> It is this second commit that empties the fpga_mgr_dev_release() function
> and instead uses devm_fpga_mgr_create() to manage the memory for the class
> driver.
>
> The fix I am proposing essentially takes us back to the 2018 implementation,
> but returns the fpga_manager pointer or ERR_PTR() instead of returning zero or
> an errno.
>
> The definition of the API is integral to the memory management issue. I cannot
> think of an acceptable solution for the upstream that doesn't involve
> changing the API. It could result in some ifdef's in an out-of-tree driver,
> but I don't think we can avoid it in this case.

IMO, the important patch is the next patch for the update.

It seems like this one is adding features and stepping on the api when 
it seems that only removing the empty release is needed.

Can most of this one be deferred until after the update ?

Tom

>
>> - Russ
>>
>>> My recommendation is to keep the public api and hide the change with an exported but not public function.
>>>
>>> Tom
>>>
>>>> -void fpga_mgr_free(struct fpga_manager *mgr);
>>>> -int fpga_mgr_register(struct fpga_manager *mgr);
>>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>>>> +                       struct fpga_compat_id *compat_id,
>>>> +                       const struct fpga_manager_ops *mops,
>>>> +                       void *priv);
>>>>    void fpga_mgr_unregister(struct fpga_manager *mgr);
>>>>    -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>>>> -
>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>> -                      const struct fpga_manager_ops *mops,
>>>> -                      void *priv);
>>>> +struct fpga_manager *
>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>> +               struct fpga_compat_id *compat_id,
>>>> +               const struct fpga_manager_ops *mops,
>>>> +               void *priv);
>>>>      #endif /*_LINUX_FPGA_MGR_H */


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-04 20:56         ` Tom Rix
@ 2021-06-04 21:17           ` Russ Weight
  0 siblings, 0 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-04 21:17 UTC (permalink / raw)
  To: Tom Rix, mdf, linux-fpga
  Cc: lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong



On 6/4/21 1:56 PM, Tom Rix wrote:
>
> On 6/3/21 3:45 PM, Russ Weight wrote:
>>
>> On 6/1/21 5:38 PM, Russ Weight wrote:
>>> On 6/1/21 4:24 PM, Tom Rix wrote:
>>>> On 5/28/21 2:57 PM, Russ Weight wrote:
>>>>> The FPGA manager class driver data structure is being treated as a
>>>>> managed resource instead of using standard dev_release call-back
>>>>> to release the class data structure. This change removes the
>>>>> managed resource code for the freeing of the class data structure
>>>>> and combines the create() and register() functions into a single
>>>>> register() function.
>>>>>
>>>>> The devm_fpga_mgr_register() function is retained.
>>>>>
>>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>>>> ---
>>>>> v4:
>>>>>     - Added the compat_id parameter to fpga_mgr_register() and
>>>> On a refactor, it is best to do the refactoring as mechanically as possible.
>>>>
>>>> While compat_id may be useful, it should be done in a separate patch.
>>> These would have to be included with the changes merge the create and register
>>> functions into a single function.
>>>
>>>> Unless it is really needed for your next security update patch consider resubmitting later
>>>>
>>>>>       devm_fpga_mgr_register() to ensure that the compat_id is set before
>>>>>       the device_register() call.
>>>>> v3:
>>>>>     - Cleaned up comment header for fpga_mgr_register()
>>>>>     - Fix error return on ida_simple_get() failure
>>>>> v2:
>>>>>     - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>>>>       create/register functionality.
>>>>>     - All previous callers of devm_fpga_mgr_register() will continue to call
>>>>>       devm_fpga_mgr_register().
>>>>>     - replaced unnecessary ternary operators in return statements with standard
>>>>>       if conditions.
>>>>> ---
>>>>>    drivers/fpga/altera-cvp.c        |  12 +--
>>>>>    drivers/fpga/altera-pr-ip-core.c |   9 +-
>>>>>    drivers/fpga/altera-ps-spi.c     |  10 +-
>>>>>    drivers/fpga/dfl-fme-mgr.c       |  12 +--
>>>>>    drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>>>>>    drivers/fpga/ice40-spi.c         |  10 +-
>>>>>    drivers/fpga/machxo2-spi.c       |  10 +-
>>>>>    drivers/fpga/socfpga-a10.c       |  16 ++-
>>>>>    drivers/fpga/socfpga.c           |  10 +-
>>>>>    drivers/fpga/stratix10-soc.c     |  15 +--
>>>>>    drivers/fpga/ts73xx-fpga.c       |  10 +-
>>>>>    drivers/fpga/xilinx-spi.c        |  12 +--
>>>>>    drivers/fpga/zynq-fpga.c         |  16 ++-
>>>>>    drivers/fpga/zynqmp-fpga.c       |  10 +-
>>>>>    include/linux/fpga/fpga-mgr.h    |  19 ++--
>>>>>    15 files changed, 120 insertions(+), 219 deletions(-)
>>>> ./Documentation/driver-api/fpga/fpga-mgr.rst
>>>>
>>>> still refers to fpga_mgr_create.
>>> Thanks for catching that.
>>>
>>>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>>>>> index 4e0edb60bfba..25eaa97ae7aa 100644
>>>>> --- a/drivers/fpga/altera-cvp.c
>>>>> +++ b/drivers/fpga/altera-cvp.c
>>>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>>>>        snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>>>>             ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>>>>    -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>>>>> -                   &altera_cvp_ops, conf);
>>>>> -    if (!mgr) {
>>>>> -        ret = -ENOMEM;
>>>>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
>>>>> +                &altera_cvp_ops, conf);
>>>>> +    if (IS_ERR(mgr)) {
>>>>> +        ret = PTR_ERR(mgr);
>>>>>            goto err_unmap;
>>>>>        }
>>>>>          pci_set_drvdata(pdev, mgr);
>>>>>    -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret)
>>>>> -        goto err_unmap;
>>>>> -
>>>>>        return 0;
>>>>>      err_unmap:
>>>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>>>>> index 5b130c4d9882..bfd4de1e7ac5 100644
>>>>> --- a/drivers/fpga/altera-pr-ip-core.c
>>>>> +++ b/drivers/fpga/altera-pr-ip-core.c
>>>>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>>>>            (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>>>>            (int)(val & ALT_PR_CSR_PR_START));
>>>>>    -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
>>>>> +                     &alt_pr_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>    EXPORT_SYMBOL_GPL(alt_pr_register);
>>>>>    diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>>>>> index 23bfd4d1ad0f..e6fc7208213c 100644
>>>>> --- a/drivers/fpga/altera-ps-spi.c
>>>>> +++ b/drivers/fpga/altera-ps-spi.c
>>>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>>>>        snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>>>>             dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>>>>    -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>>>>> -                   &altera_ps_ops, conf);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
>>>>> +                     &altera_ps_ops, conf);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>      static const struct spi_device_id altera_ps_spi_ids[] = {
>>>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>>>>> index d5861d13b306..c9b98afa7d50 100644
>>>>> --- a/drivers/fpga/dfl-fme-mgr.c
>>>>> +++ b/drivers/fpga/dfl-fme-mgr.c
>>>>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>>>>          fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>>>>    -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>>>>> -                   &fme_mgr_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    mgr->compat_id = compat_id;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
>>>>> +                     &fme_mgr_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>      static struct platform_driver fme_mgr_driver = {
>>>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>>>>> index b85bc47c91a9..d6d64ce34417 100644
>>>>> --- a/drivers/fpga/fpga-mgr.c
>>>>> +++ b/drivers/fpga/fpga-mgr.c
>>>>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>>>>    EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>>>>      /**
>>>>> - * fpga_mgr_create - create and initialize a FPGA manager struct
>>>>> + * fpga_mgr_register - create and register a FPGA Manager device
>>>>>     * @dev:    fpga manager device from pdev
>>>>>     * @name:    fpga manager name
>>>>> + * @compat_id:    FPGA manager id for compatibility check.
>>>> for consistency, use lowercase fpga
>>>>
>>>> similar issue below.
>>> OK
>>>
>>>>>     * @mops:    pointer to structure of fpga manager ops
>>>>>     * @priv:    fpga manager private data
>>>>>     *
>>>>> - * The caller of this function is responsible for freeing the struct with
>>>>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
>>>>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
>>>>> + * Using devm_fpga_mgr_register instead is recommended.
>>>>>     *
>>>>> - * Return: pointer to struct fpga_manager or NULL
>>>>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>>>>>     */
>>>>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                     const struct fpga_manager_ops *mops,
>>>>> -                     void *priv)
>>>>> +struct fpga_manager *
>>>>> +fpga_mgr_register(struct device *dev, const char *name,
>>>>> +          struct fpga_compat_id *compat_id,
>>>>> +          const struct fpga_manager_ops *mops, void *priv)
>>>>>    {
>>>>>        struct fpga_manager *mgr;
>>>>>        int id, ret;
>>>>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>>            !mops->write_init || (!mops->write && !mops->write_sg) ||
>>>>>            (mops->write && mops->write_sg)) {
>>>>>            dev_err(dev, "Attempt to register without fpga_manager_ops\n");
>>>>> -        return NULL;
>>>>> +        return ERR_PTR(-EINVAL);
>>>>>        }
>>>>>          if (!name || !strlen(name)) {
>>>>>            dev_err(dev, "Attempt to register with no name!\n");
>>>>> -        return NULL;
>>>>> +        return ERR_PTR(-EINVAL);
>>>>>        }
>>>>>          mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>>>>>        if (!mgr)
>>>>> -        return NULL;
>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>          id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
>>>>> -    if (id < 0)
>>>>> +    if (id < 0) {
>>>>> +        ret = id;
>>>> ida_* returns a normal error, so needs a wrapping in
>>>>
>>>> ret = ERR_PTR(id)
>>> Thanks - I'll fix that
>> Actually, if you follow the "goto error_kfree", you will see that ret is
>> wrapped in ERR_PTR() in the return statement, so id does not need to be
>> wrapped here.
>>
>>>>>            goto error_kfree;
>>>>> +    }
>>>>>          mutex_init(&mgr->ref_mutex);
>>>>>          mgr->name = name;
>>>>> +    mgr->compat_id = compat_id;
>>>>>        mgr->mops = mops;
>>>>>        mgr->priv = priv;
>>>>>    -    device_initialize(&mgr->dev);
>>>>>        mgr->dev.class = fpga_mgr_class;
>>>>>        mgr->dev.groups = mops->groups;
>>>>>        mgr->dev.parent = dev;
>>>>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>>        if (ret)
>>>>>            goto error_device;
>>>>>    -    return mgr;
>>>>> -
>>>>> -error_device:
>>>>> -    ida_simple_remove(&fpga_mgr_ida, id);
>>>>> -error_kfree:
>>>>> -    kfree(mgr);
>>>>> -
>>>>> -    return NULL;
>>>>> -}
>>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>>>>> -
>>>>> -/**
>>>>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
>>>>> - * @mgr:    fpga manager struct
>>>>> - */
>>>>> -void fpga_mgr_free(struct fpga_manager *mgr)
>>>>> -{
>>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>> -    kfree(mgr);
>>>>> -}
>>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
>>>>> -
>>>>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
>>>>> -{
>>>>> -    struct fpga_mgr_devres *dr = res;
>>>>> -
>>>>> -    fpga_mgr_free(dr->mgr);
>>>>> -}
>>>>> -
>>>>> -/**
>>>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>>>>> - * @dev:    fpga manager device from pdev
>>>>> - * @name:    fpga manager name
>>>>> - * @mops:    pointer to structure of fpga manager ops
>>>>> - * @priv:    fpga manager private data
>>>>> - *
>>>>> - * This function is intended for use in a FPGA manager driver's probe function.
>>>>> - * After the manager driver creates the manager struct with
>>>>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
>>>>> - * manager driver's remove function should call fpga_mgr_unregister().  The
>>>>> - * manager 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_mgr_register(), the struct will still get cleaned up.
>>>>> - *
>>>>> - * Return: pointer to struct fpga_manager or NULL
>>>>> - */
>>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                      const struct fpga_manager_ops *mops,
>>>>> -                      void *priv)
>>>>> -{
>>>>> -    struct fpga_mgr_devres *dr;
>>>>> -
>>>>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>>>>> -    if (!dr)
>>>>> -        return NULL;
>>>>> -
>>>>> -    dr->mgr = fpga_mgr_create(dev, name, mops, priv);
>>>>> -    if (!dr->mgr) {
>>>>> -        devres_free(dr);
>>>>> -        return NULL;
>>>>> -    }
>>>>> -
>>>>> -    devres_add(dev, dr);
>>>>> -
>>>>> -    return dr->mgr;
>>>>> -}
>>>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>>>>> -
>>>>> -/**
>>>>> - * fpga_mgr_register - register a FPGA manager
>>>>> - * @mgr: fpga manager struct
>>>>> - *
>>>>> - * Return: 0 on success, negative error code otherwise.
>>>>> - */
>>>>> -int fpga_mgr_register(struct fpga_manager *mgr)
>>>>> -{
>>>>> -    int ret;
>>>>> -
>>>>>        /*
>>>>>         * Initialize framework state by requesting low level driver read state
>>>>>         * from device.  FPGA may be in reset mode or may have been programmed
>>>>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>>>>         */
>>>>>        mgr->state = mgr->mops->state(mgr);
>>>>>    -    ret = device_add(&mgr->dev);
>>>>> -    if (ret)
>>>>> -        goto error_device;
>>>>> -
>>>>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
>>>> can we keep this dev_info() line and move it below device_register() ?
>>> In Greg's response to my driver, he said he said that a well-behaved driver does
>>> not need dev_info() calls...
>>>>> +    ret = device_register(&mgr->dev);
>>>>> +    if (ret) {
>>>>> +        put_device(&mgr->dev);
>>>> pr_err() here ?
>>> OK - I can add a message
>>>
>>>>> +        return ERR_PTR(ret);
>>>>> +    }
>>>>>    -    return 0;
>>>>> +    return mgr;
>>>>>      error_device:
>>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>> +    ida_simple_remove(&fpga_mgr_ida, id);
>>>>> +error_kfree:
>>>>> +    kfree(mgr);
>>>>>    -    return ret;
>>>>> +    return ERR_PTR(ret);
>>>>>    }
>>>>>    EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>>>>    @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>>>>    }
>>>>>    EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>>>>    -static int fpga_mgr_devres_match(struct device *dev, void *res,
>>>>> -                 void *match_data)
>>>>> -{
>>>>> -    struct fpga_mgr_devres *dr = res;
>>>>> -
>>>>> -    return match_data == dr->mgr;
>>>>> -}
>>>>> -
>>>>>    static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>>    {
>>>>>        struct fpga_mgr_devres *dr = res;
>>>>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>>      /**
>>>>>     * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>>>>> - * @dev: managing device for this FPGA manager
>>>>> - * @mgr: fpga manager struct
>>>>> + * @dev:    fpga manager device from pdev
>>>>> + * @name:    fpga manager name
>>>>> + * @compat_id:    FPGA manager id for compatibility check.
>>>>> + * @mops:    pointer to structure of fpga manager ops
>>>>> + * @priv:    fpga manager private data
>>>>>     *
>>>>>     * This is the devres variant of fpga_mgr_register() for which the unregister
>>>>>     * function will be called automatically when the managing device is detached.
>>>>>     */
>>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>>>>> +struct fpga_manager *
>>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>>> +               struct fpga_compat_id *compat_id,
>>>>> +               const struct fpga_manager_ops *mops, void *priv)
>>>>>    {
>>>>>        struct fpga_mgr_devres *dr;
>>>>> -    int ret;
>>>>> -
>>>>> -    /*
>>>>> -     * Make sure that the struct fpga_manager * that is passed in is
>>>>> -     * managed itself.
>>>>> -     */
>>>>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>>>>> -                 fpga_mgr_devres_match, mgr)))
>>>>> -        return -EINVAL;
>>>>> +    struct fpga_manager *mgr;
>>>>>          dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>>>>        if (!dr)
>>>>> -        return -ENOMEM;
>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>    -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>            devres_free(dr);
>>>>> -        return ret;
>>>>> +        return mgr;
>>>>>        }
>>>>>          dr->mgr = mgr;
>>>>>        devres_add(dev, dr);
>>>>>    -    return 0;
>>>>> +    return mgr;
>>>>>    }
>>>>>    EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>>>>>      static void fpga_mgr_dev_release(struct device *dev)
>>>>>    {
>>>>> +    struct fpga_manager *mgr = to_fpga_manager(dev);
>>>>> +
>>>>> +    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>> +    kfree(mgr);
>>>>>    }
>>>>>      static int __init fpga_mgr_class_init(void)
>>>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>>>>> index 69dec5af23c3..b1c7c0713e11 100644
>>>>> --- a/drivers/fpga/ice40-spi.c
>>>>> +++ b/drivers/fpga/ice40-spi.c
>>>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>>>>            return ret;
>>>>>        }
>>>>>    -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>>>>> -                   &ice40_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>>>>> +                     NULL, &ice40_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>      static const struct of_device_id ice40_fpga_of_match[] = {
>>>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>>>>> index 114a64d2b7a4..75cc6efaa1b6 100644
>>>>> --- a/drivers/fpga/machxo2-spi.c
>>>>> +++ b/drivers/fpga/machxo2-spi.c
>>>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>>>>            return -EINVAL;
>>>>>        }
>>>>>    -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>>> -                   &machxo2_ops, spi);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>>> +                     NULL, &machxo2_ops, spi);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>      static const struct of_device_id of_match[] = {
>>>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>>>>> index 573d88bdf730..80b8891fb302 100644
>>>>> --- a/drivers/fpga/socfpga-a10.c
>>>>> +++ b/drivers/fpga/socfpga-a10.c
>>>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>>>>            return -EBUSY;
>>>>>        }
>>>>>    -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>>>>> -                   &socfpga_a10_fpga_mgr_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    platform_set_drvdata(pdev, mgr);
>>>>> -
>>>>> -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>>>>> +                NULL, &socfpga_a10_fpga_mgr_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>            clk_disable_unprepare(priv->clk);
>>>>> -        return ret;
>>>>> +        return PTR_ERR(mgr);
>>>>>        }
>>>>>    +    platform_set_drvdata(pdev, mgr);
>>>>> +
>>>>>        return 0;
>>>>>    }
>>>>>    diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>>>>> index 1f467173fc1f..06edfab8a496 100644
>>>>> --- a/drivers/fpga/socfpga.c
>>>>> +++ b/drivers/fpga/socfpga.c
>>>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>>>>        if (ret)
>>>>>            return ret;
>>>>>    -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>>>>> -                   &socfpga_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>>>>> +                     NULL, &socfpga_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>      #ifdef CONFIG_OF
>>>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>>>>> index 657a70c5fc99..434094c3ac69 100644
>>>>> --- a/drivers/fpga/stratix10-soc.c
>>>>> +++ b/drivers/fpga/stratix10-soc.c
>>>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>>>>          init_completion(&priv->status_return_completion);
>>>>>    -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>>>>> -                  &s10_ops, priv);
>>>>> -    if (!mgr) {
>>>>> -        dev_err(dev, "unable to create FPGA manager\n");
>>>>> -        ret = -ENOMEM;
>>>>> -        goto probe_err;
>>>>> -    }
>>>>> -
>>>>> -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>>>>> +                NULL, &s10_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>            dev_err(dev, "unable to register FPGA manager\n");
>>>>> -        fpga_mgr_free(mgr);
>>>>> +        ret = PTR_ERR(mgr);
>>>>>            goto probe_err;
>>>>>        }
>>>>>    diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>>>>> index 101f016c6ed8..5efbec2b9f9f 100644
>>>>> --- a/drivers/fpga/ts73xx-fpga.c
>>>>> +++ b/drivers/fpga/ts73xx-fpga.c
>>>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>>>>        if (IS_ERR(priv->io_base))
>>>>>            return PTR_ERR(priv->io_base);
>>>>>    -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>>>>> -                   &ts73xx_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>>>> This is missing compat_id param.
>>> Thanks - I thought I'll do another pass and make sure I catch them all.
>>>
>>>> Similar for others.
>>>>
>>>>> +                     &ts73xx_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(kdev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>      static struct platform_driver ts73xx_fpga_driver = {
>>>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>>>>> index fee4d0abf6bf..6f1894929687 100644
>>>>> --- a/drivers/fpga/xilinx-spi.c
>>>>> +++ b/drivers/fpga/xilinx-spi.c
>>>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>>>>            return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>>>>                         "Failed to get DONE gpio\n");
>>>>>    -    mgr = devm_fpga_mgr_create(&spi->dev,
>>>>> -                   "Xilinx Slave Serial FPGA Manager",
>>>>> -                   &xilinx_spi_ops, conf);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(&spi->dev,
>>>>> +                     "Xilinx Slave Serial FPGA Manager",
>>>>> +                     NULL, &xilinx_spi_ops, conf);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>      static const struct of_device_id xlnx_spi_of_match[] = {
>>>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>>>>> index 07fa8d9ec675..74bbb9710f4e 100644
>>>>> --- a/drivers/fpga/zynq-fpga.c
>>>>> +++ b/drivers/fpga/zynq-fpga.c
>>>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>>>>          clk_disable(priv->clk);
>>>>>    -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>>>>> -                   &zynq_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    platform_set_drvdata(pdev, mgr);
>>>>> -
>>>>> -    err = fpga_mgr_register(mgr);
>>>>> -    if (err) {
>>>>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>>>> This is missing compat_id param as well.
>>>>> +                &zynq_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>            dev_err(dev, "unable to register FPGA manager\n");
>>>>>            clk_unprepare(priv->clk);
>>>>> -        return err;
>>>>> +        return PTR_ERR(mgr);
>>>>>        }
>>>>>    +    platform_set_drvdata(pdev, mgr);
>>>>> +
>>>>>        return 0;
>>>>>    }
>>>>>    diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>>>>> index 125743c9797f..1a6d520985be 100644
>>>>> --- a/drivers/fpga/zynqmp-fpga.c
>>>>> +++ b/drivers/fpga/zynqmp-fpga.c
>>>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>>>>          priv->dev = dev;
>>>>>    -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>>>>> -                   &zynqmp_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>>>>> +                     &zynqmp_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>    -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>    }
>>>>>      static const struct of_device_id zynqmp_fpga_of_match[] = {
>>>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>>>>> index 2bc3030a69e5..3e1859cc0640 100644
>>>>> --- a/include/linux/fpga/fpga-mgr.h
>>>>> +++ b/include/linux/fpga/fpga-mgr.h
>>>>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>>>>      void fpga_mgr_put(struct fpga_manager *mgr);
>>>>>    -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                     const struct fpga_manager_ops *mops,
>>>>> -                     void *priv);
>>>> My biggest concern is that removing this will break all oot fpga drivers.
>>>>
>>>> The other patches have similar api changes
>>>>
>>>> Having a stable api is very important.
>>> That is a good point. While Greg did call out the separation of the
>>> device_initialize() and device_add() calls, his biggest concern was with
>>> the empty dev_release() functions. Maybe it would be best to first submit
>>> a patch that only addresses the dev_release() functions? Maybe it would be
>>> OK to leave the create and register functions as separate functions in order
>>> to maintain a stable API...
>> On further investigation, I don't think we have much choice in changing
>> the API. We are almost coming full circle here. In 2018, there was only
>> an fpga_mgr_register() function. It was returning an errno value or zero.
>> It was also setting the drvdata of the parent driver with a pointer to
>> the resulting fpga_manager structure.
>>
>> It looks like the reason the interface was split into two (create and
>> register) was so that the fpga_manager pointer could be returned (by the
>> create function) without writing the drvdata of the parent driver:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7085e2a94f7df5f419e3cfb2fe809ce6564e9629
>>
>> When the interfaces were split, fpga_mgr_free() was also created to free
>> the memory if the register function was not called. The fpga_mgr_free()
>> function was also called by the fpga_mgr_dev_release() function. A later
>> patch was submitted to simplify the memory management for the class driver:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=084181fe8cc7472695b907f0018f4cd00934cb12
>>
>> It is this second commit that empties the fpga_mgr_dev_release() function
>> and instead uses devm_fpga_mgr_create() to manage the memory for the class
>> driver.
>>
>> The fix I am proposing essentially takes us back to the 2018 implementation,
>> but returns the fpga_manager pointer or ERR_PTR() instead of returning zero or
>> an errno.
>>
>> The definition of the API is integral to the memory management issue. I cannot
>> think of an acceptable solution for the upstream that doesn't involve
>> changing the API. It could result in some ifdef's in an out-of-tree driver,
>> but I don't think we can avoid it in this case.
>
> IMO, the important patch is the next patch for the update.
>
> It seems like this one is adding features and stepping on the api when it seems that only removing the empty release is needed.
>
> Can most of this one be deferred until after the update ?

As I understand it, removing the empty dev_release() function is not an option:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/core.c?id=084181fe8cc7472695b907f0018f4cd00934cb12#n890

The warning message makes it clear that the pointer must be populated. Providing an empty function just circumvents the warning. The purpose of this patch set is to populate the dev_release function properly, but that means removing the devm_* support that wants to manage the same memory. The whole memory management issue is greatly simplified by merging the create and register functions.

- Russ

>
> Tom
>
>>
>>> - Russ
>>>
>>>> My recommendation is to keep the public api and hide the change with an exported but not public function.
>>>>
>>>> Tom
>>>>
>>>>> -void fpga_mgr_free(struct fpga_manager *mgr);
>>>>> -int fpga_mgr_register(struct fpga_manager *mgr);
>>>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>>>>> +                       struct fpga_compat_id *compat_id,
>>>>> +                       const struct fpga_manager_ops *mops,
>>>>> +                       void *priv);
>>>>>    void fpga_mgr_unregister(struct fpga_manager *mgr);
>>>>>    -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>>>>> -
>>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                      const struct fpga_manager_ops *mops,
>>>>> -                      void *priv);
>>>>> +struct fpga_manager *
>>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>>> +               struct fpga_compat_id *compat_id,
>>>>> +               const struct fpga_manager_ops *mops,
>>>>> +               void *priv);
>>>>>      #endif /*_LINUX_FPGA_MGR_H */
>


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-03 22:45       ` Russ Weight
  2021-06-04 20:56         ` Tom Rix
@ 2021-06-05 15:42         ` Xu Yilun
  2021-06-07 23:50           ` Russ Weight
  1 sibling, 1 reply; 19+ messages in thread
From: Xu Yilun @ 2021-06-05 15:42 UTC (permalink / raw)
  To: Russ Weight
  Cc: Tom Rix, mdf, linux-fpga, lgoncalv, hao.wu, matthew.gerlach,
	richard.gong

On Thu, Jun 03, 2021 at 03:45:17PM -0700, Russ Weight wrote:
> 
> 
> On 6/1/21 5:38 PM, Russ Weight wrote:
> >
> > On 6/1/21 4:24 PM, Tom Rix wrote:
> >> On 5/28/21 2:57 PM, Russ Weight wrote:
> >>> The FPGA manager class driver data structure is being treated as a
> >>> managed resource instead of using standard dev_release call-back
> >>> to release the class data structure. This change removes the
> >>> managed resource code for the freeing of the class data structure
> >>> and combines the create() and register() functions into a single
> >>> register() function.
> >>>
> >>> The devm_fpga_mgr_register() function is retained.
> >>>
> >>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> >>> ---
> >>> v4:
> >>>    - Added the compat_id parameter to fpga_mgr_register() and
> >> On a refactor, it is best to do the refactoring as mechanically as possible.
> >>
> >> While compat_id may be useful, it should be done in a separate patch.
> > These would have to be included with the changes merge the create and register
> > functions into a single function.
> >
> >>
> >> Unless it is really needed for your next security update patch consider resubmitting later
> >>
> >>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
> >>>      the device_register() call.
> >>> v3:
> >>>    - Cleaned up comment header for fpga_mgr_register()
> >>>    - Fix error return on ida_simple_get() failure
> >>> v2:
> >>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
> >>>      create/register functionality.
> >>>    - All previous callers of devm_fpga_mgr_register() will continue to call
> >>>      devm_fpga_mgr_register().
> >>>    - replaced unnecessary ternary operators in return statements with standard
> >>>      if conditions.
> >>> ---
> >>>   drivers/fpga/altera-cvp.c        |  12 +--
> >>>   drivers/fpga/altera-pr-ip-core.c |   9 +-
> >>>   drivers/fpga/altera-ps-spi.c     |  10 +-
> >>>   drivers/fpga/dfl-fme-mgr.c       |  12 +--
> >>>   drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
> >>>   drivers/fpga/ice40-spi.c         |  10 +-
> >>>   drivers/fpga/machxo2-spi.c       |  10 +-
> >>>   drivers/fpga/socfpga-a10.c       |  16 ++-
> >>>   drivers/fpga/socfpga.c           |  10 +-
> >>>   drivers/fpga/stratix10-soc.c     |  15 +--
> >>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
> >>>   drivers/fpga/xilinx-spi.c        |  12 +--
> >>>   drivers/fpga/zynq-fpga.c         |  16 ++-
> >>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
> >>>   include/linux/fpga/fpga-mgr.h    |  19 ++--
> >>>   15 files changed, 120 insertions(+), 219 deletions(-)
> >> ./Documentation/driver-api/fpga/fpga-mgr.rst
> >>
> >> still refers to fpga_mgr_create.
> > Thanks for catching that.
> >
> >>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> >>> index 4e0edb60bfba..25eaa97ae7aa 100644
> >>> --- a/drivers/fpga/altera-cvp.c
> >>> +++ b/drivers/fpga/altera-cvp.c
> >>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
> >>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
> >>>            ALTERA_CVP_MGR_NAME, pci_name(pdev));
> >>>   -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> >>> -                   &altera_cvp_ops, conf);
> >>> -    if (!mgr) {
> >>> -        ret = -ENOMEM;
> >>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
> >>> +                &altera_cvp_ops, conf);
> >>> +    if (IS_ERR(mgr)) {
> >>> +        ret = PTR_ERR(mgr);
> >>>           goto err_unmap;
> >>>       }
> >>>         pci_set_drvdata(pdev, mgr);
> >>>   -    ret = fpga_mgr_register(mgr);
> >>> -    if (ret)
> >>> -        goto err_unmap;
> >>> -
> >>>       return 0;
> >>>     err_unmap:
> >>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
> >>> index 5b130c4d9882..bfd4de1e7ac5 100644
> >>> --- a/drivers/fpga/altera-pr-ip-core.c
> >>> +++ b/drivers/fpga/altera-pr-ip-core.c
> >>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
> >>>           (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
> >>>           (int)(val & ALT_PR_CSR_PR_START));
> >>>   -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
> >>> +                     &alt_pr_ops, priv);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>> +    return 0;
> >>>   }
> >>>   EXPORT_SYMBOL_GPL(alt_pr_register);
> >>>   diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> >>> index 23bfd4d1ad0f..e6fc7208213c 100644
> >>> --- a/drivers/fpga/altera-ps-spi.c
> >>> +++ b/drivers/fpga/altera-ps-spi.c
> >>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
> >>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
> >>>            dev_driver_string(&spi->dev), dev_name(&spi->dev));
> >>>   -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> >>> -                   &altera_ps_ops, conf);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
> >>> +                     &altera_ps_ops, conf);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
> >>> +    return 0;
> >>>   }
> >>>     static const struct spi_device_id altera_ps_spi_ids[] = {
> >>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> >>> index d5861d13b306..c9b98afa7d50 100644
> >>> --- a/drivers/fpga/dfl-fme-mgr.c
> >>> +++ b/drivers/fpga/dfl-fme-mgr.c
> >>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
> >>>         fme_mgr_get_compat_id(priv->ioaddr, compat_id);
> >>>   -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
> >>> -                   &fme_mgr_ops, priv);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> -
> >>> -    mgr->compat_id = compat_id;
> >>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
> >>> +                     &fme_mgr_ops, priv);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>> +    return 0;
> >>>   }
> >>>     static struct platform_driver fme_mgr_driver = {
> >>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> >>> index b85bc47c91a9..d6d64ce34417 100644
> >>> --- a/drivers/fpga/fpga-mgr.c
> >>> +++ b/drivers/fpga/fpga-mgr.c
> >>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
> >>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
> >>>     /**
> >>> - * fpga_mgr_create - create and initialize a FPGA manager struct
> >>> + * fpga_mgr_register - create and register a FPGA Manager device
> >>>    * @dev:    fpga manager device from pdev
> >>>    * @name:    fpga manager name
> >>> + * @compat_id:    FPGA manager id for compatibility check.
> >> for consistency, use lowercase fpga
> >>
> >> similar issue below.
> > OK
> >
> >>>    * @mops:    pointer to structure of fpga manager ops
> >>>    * @priv:    fpga manager private data
> >>>    *
> >>> - * The caller of this function is responsible for freeing the struct with
> >>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
> >>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
> >>> + * Using devm_fpga_mgr_register instead is recommended.
> >>>    *
> >>> - * Return: pointer to struct fpga_manager or NULL
> >>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
> >>>    */
> >>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>> -                     const struct fpga_manager_ops *mops,
> >>> -                     void *priv)
> >>> +struct fpga_manager *
> >>> +fpga_mgr_register(struct device *dev, const char *name,
> >>> +          struct fpga_compat_id *compat_id,
> >>> +          const struct fpga_manager_ops *mops, void *priv)
> >>>   {
> >>>       struct fpga_manager *mgr;
> >>>       int id, ret;
> >>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>>           !mops->write_init || (!mops->write && !mops->write_sg) ||
> >>>           (mops->write && mops->write_sg)) {
> >>>           dev_err(dev, "Attempt to register without fpga_manager_ops\n");
> >>> -        return NULL;
> >>> +        return ERR_PTR(-EINVAL);
> >>>       }
> >>>         if (!name || !strlen(name)) {
> >>>           dev_err(dev, "Attempt to register with no name!\n");
> >>> -        return NULL;
> >>> +        return ERR_PTR(-EINVAL);
> >>>       }
> >>>         mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
> >>>       if (!mgr)
> >>> -        return NULL;
> >>> +        return ERR_PTR(-ENOMEM);
> >>>         id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
> >>> -    if (id < 0)
> >>> +    if (id < 0) {
> >>> +        ret = id;
> >> ida_* returns a normal error, so needs a wrapping in
> >>
> >> ret = ERR_PTR(id)
> > Thanks - I'll fix that
> Actually, if you follow the "goto error_kfree", you will see that ret is
> wrapped in ERR_PTR() in the return statement, so id does not need to be
> wrapped here.
> 
> >
> >>>           goto error_kfree;
> >>> +    }
> >>>         mutex_init(&mgr->ref_mutex);
> >>>         mgr->name = name;
> >>> +    mgr->compat_id = compat_id;
> >>>       mgr->mops = mops;
> >>>       mgr->priv = priv;
> >>>   -    device_initialize(&mgr->dev);
> >>>       mgr->dev.class = fpga_mgr_class;
> >>>       mgr->dev.groups = mops->groups;
> >>>       mgr->dev.parent = dev;
> >>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>>       if (ret)
> >>>           goto error_device;
> >>>   -    return mgr;
> >>> -
> >>> -error_device:
> >>> -    ida_simple_remove(&fpga_mgr_ida, id);
> >>> -error_kfree:
> >>> -    kfree(mgr);
> >>> -
> >>> -    return NULL;
> >>> -}
> >>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
> >>> -
> >>> -/**
> >>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
> >>> - * @mgr:    fpga manager struct
> >>> - */
> >>> -void fpga_mgr_free(struct fpga_manager *mgr)
> >>> -{
> >>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> >>> -    kfree(mgr);
> >>> -}
> >>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
> >>> -
> >>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
> >>> -{
> >>> -    struct fpga_mgr_devres *dr = res;
> >>> -
> >>> -    fpga_mgr_free(dr->mgr);
> >>> -}
> >>> -
> >>> -/**
> >>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
> >>> - * @dev:    fpga manager device from pdev
> >>> - * @name:    fpga manager name
> >>> - * @mops:    pointer to structure of fpga manager ops
> >>> - * @priv:    fpga manager private data
> >>> - *
> >>> - * This function is intended for use in a FPGA manager driver's probe function.
> >>> - * After the manager driver creates the manager struct with
> >>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
> >>> - * manager driver's remove function should call fpga_mgr_unregister().  The
> >>> - * manager 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_mgr_register(), the struct will still get cleaned up.
> >>> - *
> >>> - * Return: pointer to struct fpga_manager or NULL
> >>> - */
> >>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> >>> -                      const struct fpga_manager_ops *mops,
> >>> -                      void *priv)
> >>> -{
> >>> -    struct fpga_mgr_devres *dr;
> >>> -
> >>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
> >>> -    if (!dr)
> >>> -        return NULL;
> >>> -
> >>> -    dr->mgr = fpga_mgr_create(dev, name, mops, priv);
> >>> -    if (!dr->mgr) {
> >>> -        devres_free(dr);
> >>> -        return NULL;
> >>> -    }
> >>> -
> >>> -    devres_add(dev, dr);
> >>> -
> >>> -    return dr->mgr;
> >>> -}
> >>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
> >>> -
> >>> -/**
> >>> - * fpga_mgr_register - register a FPGA manager
> >>> - * @mgr: fpga manager struct
> >>> - *
> >>> - * Return: 0 on success, negative error code otherwise.
> >>> - */
> >>> -int fpga_mgr_register(struct fpga_manager *mgr)
> >>> -{
> >>> -    int ret;
> >>> -
> >>>       /*
> >>>        * Initialize framework state by requesting low level driver read state
> >>>        * from device.  FPGA may be in reset mode or may have been programmed
> >>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
> >>>        */
> >>>       mgr->state = mgr->mops->state(mgr);
> >>>   -    ret = device_add(&mgr->dev);
> >>> -    if (ret)
> >>> -        goto error_device;
> >>> -
> >>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
> >> can we keep this dev_info() line and move it below device_register() ?
> > In Greg's response to my driver, he said he said that a well-behaved driver does
> > not need dev_info() calls...
> >>> +    ret = device_register(&mgr->dev);
> >>> +    if (ret) {
> >>> +        put_device(&mgr->dev);
> >> pr_err() here ?
> > OK - I can add a message
> >
> >>> +        return ERR_PTR(ret);
> >>> +    }
> >>>   -    return 0;
> >>> +    return mgr;
> >>>     error_device:
> >>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> >>> +    ida_simple_remove(&fpga_mgr_ida, id);
> >>> +error_kfree:
> >>> +    kfree(mgr);
> >>>   -    return ret;
> >>> +    return ERR_PTR(ret);
> >>>   }
> >>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
> >>>   @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
> >>>   }
> >>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
> >>>   -static int fpga_mgr_devres_match(struct device *dev, void *res,
> >>> -                 void *match_data)
> >>> -{
> >>> -    struct fpga_mgr_devres *dr = res;
> >>> -
> >>> -    return match_data == dr->mgr;
> >>> -}
> >>> -
> >>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
> >>>   {
> >>>       struct fpga_mgr_devres *dr = res;
> >>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
> >>>     /**
> >>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
> >>> - * @dev: managing device for this FPGA manager
> >>> - * @mgr: fpga manager struct
> >>> + * @dev:    fpga manager device from pdev
> >>> + * @name:    fpga manager name
> >>> + * @compat_id:    FPGA manager id for compatibility check.
> >>> + * @mops:    pointer to structure of fpga manager ops
> >>> + * @priv:    fpga manager private data
> >>>    *
> >>>    * This is the devres variant of fpga_mgr_register() for which the unregister
> >>>    * function will be called automatically when the managing device is detached.
> >>>    */
> >>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
> >>> +struct fpga_manager *
> >>> +devm_fpga_mgr_register(struct device *dev, const char *name,
> >>> +               struct fpga_compat_id *compat_id,
> >>> +               const struct fpga_manager_ops *mops, void *priv)
> >>>   {
> >>>       struct fpga_mgr_devres *dr;
> >>> -    int ret;
> >>> -
> >>> -    /*
> >>> -     * Make sure that the struct fpga_manager * that is passed in is
> >>> -     * managed itself.
> >>> -     */
> >>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
> >>> -                 fpga_mgr_devres_match, mgr)))
> >>> -        return -EINVAL;
> >>> +    struct fpga_manager *mgr;
> >>>         dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
> >>>       if (!dr)
> >>> -        return -ENOMEM;
> >>> +        return ERR_PTR(-ENOMEM);
> >>>   -    ret = fpga_mgr_register(mgr);
> >>> -    if (ret) {
> >>> +    mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
> >>> +    if (IS_ERR(mgr)) {
> >>>           devres_free(dr);
> >>> -        return ret;
> >>> +        return mgr;
> >>>       }
> >>>         dr->mgr = mgr;
> >>>       devres_add(dev, dr);
> >>>   -    return 0;
> >>> +    return mgr;
> >>>   }
> >>>   EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
> >>>     static void fpga_mgr_dev_release(struct device *dev)
> >>>   {
> >>> +    struct fpga_manager *mgr = to_fpga_manager(dev);
> >>> +
> >>> +    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> >>> +    kfree(mgr);
> >>>   }
> >>>     static int __init fpga_mgr_class_init(void)
> >>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> >>> index 69dec5af23c3..b1c7c0713e11 100644
> >>> --- a/drivers/fpga/ice40-spi.c
> >>> +++ b/drivers/fpga/ice40-spi.c
> >>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
> >>>           return ret;
> >>>       }
> >>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
> >>> -                   &ice40_fpga_ops, priv);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> >>> +                     NULL, &ice40_fpga_ops, priv);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>> +    return 0;
> >>>   }
> >>>     static const struct of_device_id ice40_fpga_of_match[] = {
> >>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
> >>> index 114a64d2b7a4..75cc6efaa1b6 100644
> >>> --- a/drivers/fpga/machxo2-spi.c
> >>> +++ b/drivers/fpga/machxo2-spi.c
> >>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
> >>>           return -EINVAL;
> >>>       }
> >>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
> >>> -                   &machxo2_ops, spi);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
> >>> +                     NULL, &machxo2_ops, spi);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>> +    return 0;
> >>>   }
> >>>     static const struct of_device_id of_match[] = {
> >>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
> >>> index 573d88bdf730..80b8891fb302 100644
> >>> --- a/drivers/fpga/socfpga-a10.c
> >>> +++ b/drivers/fpga/socfpga-a10.c
> >>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
> >>>           return -EBUSY;
> >>>       }
> >>>   -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
> >>> -                   &socfpga_a10_fpga_mgr_ops, priv);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> -
> >>> -    platform_set_drvdata(pdev, mgr);
> >>> -
> >>> -    ret = fpga_mgr_register(mgr);
> >>> -    if (ret) {
> >>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
> >>> +                NULL, &socfpga_a10_fpga_mgr_ops, priv);
> >>> +    if (IS_ERR(mgr)) {
> >>>           clk_disable_unprepare(priv->clk);
> >>> -        return ret;
> >>> +        return PTR_ERR(mgr);
> >>>       }
> >>>   +    platform_set_drvdata(pdev, mgr);
> >>> +
> >>>       return 0;
> >>>   }
> >>>   diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
> >>> index 1f467173fc1f..06edfab8a496 100644
> >>> --- a/drivers/fpga/socfpga.c
> >>> +++ b/drivers/fpga/socfpga.c
> >>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
> >>>       if (ret)
> >>>           return ret;
> >>>   -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
> >>> -                   &socfpga_fpga_ops, priv);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
> >>> +                     NULL, &socfpga_fpga_ops, priv);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>> +    return 0;
> >>>   }
> >>>     #ifdef CONFIG_OF
> >>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> >>> index 657a70c5fc99..434094c3ac69 100644
> >>> --- a/drivers/fpga/stratix10-soc.c
> >>> +++ b/drivers/fpga/stratix10-soc.c
> >>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
> >>>         init_completion(&priv->status_return_completion);
> >>>   -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
> >>> -                  &s10_ops, priv);
> >>> -    if (!mgr) {
> >>> -        dev_err(dev, "unable to create FPGA manager\n");
> >>> -        ret = -ENOMEM;
> >>> -        goto probe_err;
> >>> -    }
> >>> -
> >>> -    ret = fpga_mgr_register(mgr);
> >>> -    if (ret) {
> >>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
> >>> +                NULL, &s10_ops, priv);
> >>> +    if (IS_ERR(mgr)) {
> >>>           dev_err(dev, "unable to register FPGA manager\n");
> >>> -        fpga_mgr_free(mgr);
> >>> +        ret = PTR_ERR(mgr);
> >>>           goto probe_err;
> >>>       }
> >>>   diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
> >>> index 101f016c6ed8..5efbec2b9f9f 100644
> >>> --- a/drivers/fpga/ts73xx-fpga.c
> >>> +++ b/drivers/fpga/ts73xx-fpga.c
> >>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
> >>>       if (IS_ERR(priv->io_base))
> >>>           return PTR_ERR(priv->io_base);
> >>>   -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
> >>> -                   &ts73xx_fpga_ops, priv);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
> >> This is missing compat_id param.
> > Thanks - I thought I'll do another pass and make sure I catch them all.
> >
> >> Similar for others.
> >>
> >>> +                     &ts73xx_fpga_ops, priv);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(kdev, mgr);
> >>> +    return 0;
> >>>   }
> >>>     static struct platform_driver ts73xx_fpga_driver = {
> >>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
> >>> index fee4d0abf6bf..6f1894929687 100644
> >>> --- a/drivers/fpga/xilinx-spi.c
> >>> +++ b/drivers/fpga/xilinx-spi.c
> >>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
> >>>           return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
> >>>                        "Failed to get DONE gpio\n");
> >>>   -    mgr = devm_fpga_mgr_create(&spi->dev,
> >>> -                   "Xilinx Slave Serial FPGA Manager",
> >>> -                   &xilinx_spi_ops, conf);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> +    mgr = devm_fpga_mgr_register(&spi->dev,
> >>> +                     "Xilinx Slave Serial FPGA Manager",
> >>> +                     NULL, &xilinx_spi_ops, conf);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
> >>> +    return 0;
> >>>   }
> >>>     static const struct of_device_id xlnx_spi_of_match[] = {
> >>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
> >>> index 07fa8d9ec675..74bbb9710f4e 100644
> >>> --- a/drivers/fpga/zynq-fpga.c
> >>> +++ b/drivers/fpga/zynq-fpga.c
> >>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
> >>>         clk_disable(priv->clk);
> >>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
> >>> -                   &zynq_fpga_ops, priv);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> -
> >>> -    platform_set_drvdata(pdev, mgr);
> >>> -
> >>> -    err = fpga_mgr_register(mgr);
> >>> -    if (err) {
> >>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
> >> This is missing compat_id param as well.
> >>> +                &zynq_fpga_ops, priv);
> >>> +    if (IS_ERR(mgr)) {
> >>>           dev_err(dev, "unable to register FPGA manager\n");
> >>>           clk_unprepare(priv->clk);
> >>> -        return err;
> >>> +        return PTR_ERR(mgr);
> >>>       }
> >>>   +    platform_set_drvdata(pdev, mgr);
> >>> +
> >>>       return 0;
> >>>   }
> >>>   diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
> >>> index 125743c9797f..1a6d520985be 100644
> >>> --- a/drivers/fpga/zynqmp-fpga.c
> >>> +++ b/drivers/fpga/zynqmp-fpga.c
> >>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
> >>>         priv->dev = dev;
> >>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
> >>> -                   &zynqmp_fpga_ops, priv);
> >>> -    if (!mgr)
> >>> -        return -ENOMEM;
> >>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
> >>> +                     &zynqmp_fpga_ops, priv);
> >>> +    if (IS_ERR(mgr))
> >>> +        return PTR_ERR(mgr);
> >>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>> +    return 0;
> >>>   }
> >>>     static const struct of_device_id zynqmp_fpga_of_match[] = {
> >>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> >>> index 2bc3030a69e5..3e1859cc0640 100644
> >>> --- a/include/linux/fpga/fpga-mgr.h
> >>> +++ b/include/linux/fpga/fpga-mgr.h
> >>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
> >>>     void fpga_mgr_put(struct fpga_manager *mgr);
> >>>   -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>> -                     const struct fpga_manager_ops *mops,
> >>> -                     void *priv);
> >> My biggest concern is that removing this will break all oot fpga drivers.
> >>
> >> The other patches have similar api changes
> >>
> >> Having a stable api is very important.
> > That is a good point. While Greg did call out the separation of the
> > device_initialize() and device_add() calls, his biggest concern was with
> > the empty dev_release() functions. Maybe it would be best to first submit
> > a patch that only addresses the dev_release() functions? Maybe it would be
> > OK to leave the create and register functions as separate functions in order
> > to maintain a stable API...
> 
> On further investigation, I don't think we have much choice in changing
> the API. We are almost coming full circle here. In 2018, there was only
> an fpga_mgr_register() function. It was returning an errno value or zero.
> It was also setting the drvdata of the parent driver with a pointer to
> the resulting fpga_manager structure.
> 
> It looks like the reason the interface was split into two (create and
> register) was so that the fpga_manager pointer could be returned (by the
> create function) without writing the drvdata of the parent driver:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7085e2a94f7df5f419e3cfb2fe809ce6564e9629
> 
> When the interfaces were split, fpga_mgr_free() was also created to free
> the memory if the register function was not called. The fpga_mgr_free()
> function was also called by the fpga_mgr_dev_release() function. A later
> patch was submitted to simplify the memory management for the class driver:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=084181fe8cc7472695b907f0018f4cd00934cb12
> 
> It is this second commit that empties the fpga_mgr_dev_release() function
> and instead uses devm_fpga_mgr_create() to manage the memory for the class
> driver.
> 
> The fix I am proposing essentially takes us back to the 2018 implementation,
> but returns the fpga_manager pointer or ERR_PTR() instead of returning zero or
> an errno.
> 
> The definition of the API is integral to the memory management issue. I cannot
> think of an acceptable solution for the upstream that doesn't involve
> changing the API. It could result in some ifdef's in an out-of-tree driver,
> but I don't think we can avoid it in this case.

I agree the stable API is important, and I think it is still possible to fix
the memory management issue while keeping the APIs.

We should do the actual resource free work in class.dev_release(), and
fpga_mgr_free should call put_device() to trigger it.

And I see no problem with the current devm APIs.

Thanks,
Yilun

> 
> > - Russ
> >
> >> My recommendation is to keep the public api and hide the change with an exported but not public function.
> >>
> >> Tom
> >>
> >>> -void fpga_mgr_free(struct fpga_manager *mgr);
> >>> -int fpga_mgr_register(struct fpga_manager *mgr);
> >>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
> >>> +                       struct fpga_compat_id *compat_id,
> >>> +                       const struct fpga_manager_ops *mops,
> >>> +                       void *priv);
> >>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
> >>>   -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
> >>> -
> >>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> >>> -                      const struct fpga_manager_ops *mops,
> >>> -                      void *priv);
> >>> +struct fpga_manager *
> >>> +devm_fpga_mgr_register(struct device *dev, const char *name,
> >>> +               struct fpga_compat_id *compat_id,
> >>> +               const struct fpga_manager_ops *mops,
> >>> +               void *priv);
> >>>     #endif /*_LINUX_FPGA_MGR_H */

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-05 15:42         ` Xu Yilun
@ 2021-06-07 23:50           ` Russ Weight
  2021-06-08  1:40             ` Xu Yilun
  0 siblings, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-06-07 23:50 UTC (permalink / raw)
  To: Xu Yilun
  Cc: Tom Rix, mdf, linux-fpga, lgoncalv, hao.wu, matthew.gerlach,
	richard.gong



On 6/5/21 8:42 AM, Xu Yilun wrote:
> On Thu, Jun 03, 2021 at 03:45:17PM -0700, Russ Weight wrote:
>>
>> On 6/1/21 5:38 PM, Russ Weight wrote:
>>> On 6/1/21 4:24 PM, Tom Rix wrote:
>>>> On 5/28/21 2:57 PM, Russ Weight wrote:
>>>>> The FPGA manager class driver data structure is being treated as a
>>>>> managed resource instead of using standard dev_release call-back
>>>>> to release the class data structure. This change removes the
>>>>> managed resource code for the freeing of the class data structure
>>>>> and combines the create() and register() functions into a single
>>>>> register() function.
>>>>>
>>>>> The devm_fpga_mgr_register() function is retained.
>>>>>
>>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>>>> ---
>>>>> v4:
>>>>>    - Added the compat_id parameter to fpga_mgr_register() and
>>>> On a refactor, it is best to do the refactoring as mechanically as possible.
>>>>
>>>> While compat_id may be useful, it should be done in a separate patch.
>>> These would have to be included with the changes merge the create and register
>>> functions into a single function.
>>>
>>>> Unless it is really needed for your next security update patch consider resubmitting later
>>>>
>>>>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
>>>>>      the device_register() call.
>>>>> v3:
>>>>>    - Cleaned up comment header for fpga_mgr_register()
>>>>>    - Fix error return on ida_simple_get() failure
>>>>> v2:
>>>>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>>>>      create/register functionality.
>>>>>    - All previous callers of devm_fpga_mgr_register() will continue to call
>>>>>      devm_fpga_mgr_register().
>>>>>    - replaced unnecessary ternary operators in return statements with standard
>>>>>      if conditions.
>>>>> ---
>>>>>   drivers/fpga/altera-cvp.c        |  12 +--
>>>>>   drivers/fpga/altera-pr-ip-core.c |   9 +-
>>>>>   drivers/fpga/altera-ps-spi.c     |  10 +-
>>>>>   drivers/fpga/dfl-fme-mgr.c       |  12 +--
>>>>>   drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>>>>>   drivers/fpga/ice40-spi.c         |  10 +-
>>>>>   drivers/fpga/machxo2-spi.c       |  10 +-
>>>>>   drivers/fpga/socfpga-a10.c       |  16 ++-
>>>>>   drivers/fpga/socfpga.c           |  10 +-
>>>>>   drivers/fpga/stratix10-soc.c     |  15 +--
>>>>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
>>>>>   drivers/fpga/xilinx-spi.c        |  12 +--
>>>>>   drivers/fpga/zynq-fpga.c         |  16 ++-
>>>>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
>>>>>   include/linux/fpga/fpga-mgr.h    |  19 ++--
>>>>>   15 files changed, 120 insertions(+), 219 deletions(-)
>>>> ./Documentation/driver-api/fpga/fpga-mgr.rst
>>>>
>>>> still refers to fpga_mgr_create.
>>> Thanks for catching that.
>>>
>>>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>>>>> index 4e0edb60bfba..25eaa97ae7aa 100644
>>>>> --- a/drivers/fpga/altera-cvp.c
>>>>> +++ b/drivers/fpga/altera-cvp.c
>>>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>>>>            ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>>>>   -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>>>>> -                   &altera_cvp_ops, conf);
>>>>> -    if (!mgr) {
>>>>> -        ret = -ENOMEM;
>>>>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
>>>>> +                &altera_cvp_ops, conf);
>>>>> +    if (IS_ERR(mgr)) {
>>>>> +        ret = PTR_ERR(mgr);
>>>>>           goto err_unmap;
>>>>>       }
>>>>>         pci_set_drvdata(pdev, mgr);
>>>>>   -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret)
>>>>> -        goto err_unmap;
>>>>> -
>>>>>       return 0;
>>>>>     err_unmap:
>>>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>>>>> index 5b130c4d9882..bfd4de1e7ac5 100644
>>>>> --- a/drivers/fpga/altera-pr-ip-core.c
>>>>> +++ b/drivers/fpga/altera-pr-ip-core.c
>>>>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>>>>           (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>>>>           (int)(val & ALT_PR_CSR_PR_START));
>>>>>   -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
>>>>> +                     &alt_pr_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>   EXPORT_SYMBOL_GPL(alt_pr_register);
>>>>>   diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>>>>> index 23bfd4d1ad0f..e6fc7208213c 100644
>>>>> --- a/drivers/fpga/altera-ps-spi.c
>>>>> +++ b/drivers/fpga/altera-ps-spi.c
>>>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>>>>            dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>>>>> -                   &altera_ps_ops, conf);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
>>>>> +                     &altera_ps_ops, conf);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct spi_device_id altera_ps_spi_ids[] = {
>>>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>>>>> index d5861d13b306..c9b98afa7d50 100644
>>>>> --- a/drivers/fpga/dfl-fme-mgr.c
>>>>> +++ b/drivers/fpga/dfl-fme-mgr.c
>>>>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>>>>         fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>>>>> -                   &fme_mgr_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    mgr->compat_id = compat_id;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
>>>>> +                     &fme_mgr_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static struct platform_driver fme_mgr_driver = {
>>>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>>>>> index b85bc47c91a9..d6d64ce34417 100644
>>>>> --- a/drivers/fpga/fpga-mgr.c
>>>>> +++ b/drivers/fpga/fpga-mgr.c
>>>>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>>>>     /**
>>>>> - * fpga_mgr_create - create and initialize a FPGA manager struct
>>>>> + * fpga_mgr_register - create and register a FPGA Manager device
>>>>>    * @dev:    fpga manager device from pdev
>>>>>    * @name:    fpga manager name
>>>>> + * @compat_id:    FPGA manager id for compatibility check.
>>>> for consistency, use lowercase fpga
>>>>
>>>> similar issue below.
>>> OK
>>>
>>>>>    * @mops:    pointer to structure of fpga manager ops
>>>>>    * @priv:    fpga manager private data
>>>>>    *
>>>>> - * The caller of this function is responsible for freeing the struct with
>>>>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
>>>>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
>>>>> + * Using devm_fpga_mgr_register instead is recommended.
>>>>>    *
>>>>> - * Return: pointer to struct fpga_manager or NULL
>>>>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>>>>>    */
>>>>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                     const struct fpga_manager_ops *mops,
>>>>> -                     void *priv)
>>>>> +struct fpga_manager *
>>>>> +fpga_mgr_register(struct device *dev, const char *name,
>>>>> +          struct fpga_compat_id *compat_id,
>>>>> +          const struct fpga_manager_ops *mops, void *priv)
>>>>>   {
>>>>>       struct fpga_manager *mgr;
>>>>>       int id, ret;
>>>>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>>           !mops->write_init || (!mops->write && !mops->write_sg) ||
>>>>>           (mops->write && mops->write_sg)) {
>>>>>           dev_err(dev, "Attempt to register without fpga_manager_ops\n");
>>>>> -        return NULL;
>>>>> +        return ERR_PTR(-EINVAL);
>>>>>       }
>>>>>         if (!name || !strlen(name)) {
>>>>>           dev_err(dev, "Attempt to register with no name!\n");
>>>>> -        return NULL;
>>>>> +        return ERR_PTR(-EINVAL);
>>>>>       }
>>>>>         mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>>>>>       if (!mgr)
>>>>> -        return NULL;
>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>         id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
>>>>> -    if (id < 0)
>>>>> +    if (id < 0) {
>>>>> +        ret = id;
>>>> ida_* returns a normal error, so needs a wrapping in
>>>>
>>>> ret = ERR_PTR(id)
>>> Thanks - I'll fix that
>> Actually, if you follow the "goto error_kfree", you will see that ret is
>> wrapped in ERR_PTR() in the return statement, so id does not need to be
>> wrapped here.
>>
>>>>>           goto error_kfree;
>>>>> +    }
>>>>>         mutex_init(&mgr->ref_mutex);
>>>>>         mgr->name = name;
>>>>> +    mgr->compat_id = compat_id;
>>>>>       mgr->mops = mops;
>>>>>       mgr->priv = priv;
>>>>>   -    device_initialize(&mgr->dev);
>>>>>       mgr->dev.class = fpga_mgr_class;
>>>>>       mgr->dev.groups = mops->groups;
>>>>>       mgr->dev.parent = dev;
>>>>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>>       if (ret)
>>>>>           goto error_device;
>>>>>   -    return mgr;
>>>>> -
>>>>> -error_device:
>>>>> -    ida_simple_remove(&fpga_mgr_ida, id);
>>>>> -error_kfree:
>>>>> -    kfree(mgr);
>>>>> -
>>>>> -    return NULL;
>>>>> -}
>>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>>>>> -
>>>>> -/**
>>>>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
>>>>> - * @mgr:    fpga manager struct
>>>>> - */
>>>>> -void fpga_mgr_free(struct fpga_manager *mgr)
>>>>> -{
>>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>> -    kfree(mgr);
>>>>> -}
>>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
>>>>> -
>>>>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
>>>>> -{
>>>>> -    struct fpga_mgr_devres *dr = res;
>>>>> -
>>>>> -    fpga_mgr_free(dr->mgr);
>>>>> -}
>>>>> -
>>>>> -/**
>>>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>>>>> - * @dev:    fpga manager device from pdev
>>>>> - * @name:    fpga manager name
>>>>> - * @mops:    pointer to structure of fpga manager ops
>>>>> - * @priv:    fpga manager private data
>>>>> - *
>>>>> - * This function is intended for use in a FPGA manager driver's probe function.
>>>>> - * After the manager driver creates the manager struct with
>>>>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
>>>>> - * manager driver's remove function should call fpga_mgr_unregister().  The
>>>>> - * manager 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_mgr_register(), the struct will still get cleaned up.
>>>>> - *
>>>>> - * Return: pointer to struct fpga_manager or NULL
>>>>> - */
>>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                      const struct fpga_manager_ops *mops,
>>>>> -                      void *priv)
>>>>> -{
>>>>> -    struct fpga_mgr_devres *dr;
>>>>> -
>>>>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>>>>> -    if (!dr)
>>>>> -        return NULL;
>>>>> -
>>>>> -    dr->mgr = fpga_mgr_create(dev, name, mops, priv);
>>>>> -    if (!dr->mgr) {
>>>>> -        devres_free(dr);
>>>>> -        return NULL;
>>>>> -    }
>>>>> -
>>>>> -    devres_add(dev, dr);
>>>>> -
>>>>> -    return dr->mgr;
>>>>> -}
>>>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>>>>> -
>>>>> -/**
>>>>> - * fpga_mgr_register - register a FPGA manager
>>>>> - * @mgr: fpga manager struct
>>>>> - *
>>>>> - * Return: 0 on success, negative error code otherwise.
>>>>> - */
>>>>> -int fpga_mgr_register(struct fpga_manager *mgr)
>>>>> -{
>>>>> -    int ret;
>>>>> -
>>>>>       /*
>>>>>        * Initialize framework state by requesting low level driver read state
>>>>>        * from device.  FPGA may be in reset mode or may have been programmed
>>>>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>>>>        */
>>>>>       mgr->state = mgr->mops->state(mgr);
>>>>>   -    ret = device_add(&mgr->dev);
>>>>> -    if (ret)
>>>>> -        goto error_device;
>>>>> -
>>>>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
>>>> can we keep this dev_info() line and move it below device_register() ?
>>> In Greg's response to my driver, he said he said that a well-behaved driver does
>>> not need dev_info() calls...
>>>>> +    ret = device_register(&mgr->dev);
>>>>> +    if (ret) {
>>>>> +        put_device(&mgr->dev);
>>>> pr_err() here ?
>>> OK - I can add a message
>>>
>>>>> +        return ERR_PTR(ret);
>>>>> +    }
>>>>>   -    return 0;
>>>>> +    return mgr;
>>>>>     error_device:
>>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>> +    ida_simple_remove(&fpga_mgr_ida, id);
>>>>> +error_kfree:
>>>>> +    kfree(mgr);
>>>>>   -    return ret;
>>>>> +    return ERR_PTR(ret);
>>>>>   }
>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>>>>   @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>>>>   }
>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>>>>   -static int fpga_mgr_devres_match(struct device *dev, void *res,
>>>>> -                 void *match_data)
>>>>> -{
>>>>> -    struct fpga_mgr_devres *dr = res;
>>>>> -
>>>>> -    return match_data == dr->mgr;
>>>>> -}
>>>>> -
>>>>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>>   {
>>>>>       struct fpga_mgr_devres *dr = res;
>>>>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>>     /**
>>>>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>>>>> - * @dev: managing device for this FPGA manager
>>>>> - * @mgr: fpga manager struct
>>>>> + * @dev:    fpga manager device from pdev
>>>>> + * @name:    fpga manager name
>>>>> + * @compat_id:    FPGA manager id for compatibility check.
>>>>> + * @mops:    pointer to structure of fpga manager ops
>>>>> + * @priv:    fpga manager private data
>>>>>    *
>>>>>    * This is the devres variant of fpga_mgr_register() for which the unregister
>>>>>    * function will be called automatically when the managing device is detached.
>>>>>    */
>>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>>>>> +struct fpga_manager *
>>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>>> +               struct fpga_compat_id *compat_id,
>>>>> +               const struct fpga_manager_ops *mops, void *priv)
>>>>>   {
>>>>>       struct fpga_mgr_devres *dr;
>>>>> -    int ret;
>>>>> -
>>>>> -    /*
>>>>> -     * Make sure that the struct fpga_manager * that is passed in is
>>>>> -     * managed itself.
>>>>> -     */
>>>>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>>>>> -                 fpga_mgr_devres_match, mgr)))
>>>>> -        return -EINVAL;
>>>>> +    struct fpga_manager *mgr;
>>>>>         dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>>>>       if (!dr)
>>>>> -        return -ENOMEM;
>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>   -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>           devres_free(dr);
>>>>> -        return ret;
>>>>> +        return mgr;
>>>>>       }
>>>>>         dr->mgr = mgr;
>>>>>       devres_add(dev, dr);
>>>>>   -    return 0;
>>>>> +    return mgr;
>>>>>   }
>>>>>   EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>>>>>     static void fpga_mgr_dev_release(struct device *dev)
>>>>>   {
>>>>> +    struct fpga_manager *mgr = to_fpga_manager(dev);
>>>>> +
>>>>> +    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>> +    kfree(mgr);
>>>>>   }
>>>>>     static int __init fpga_mgr_class_init(void)
>>>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>>>>> index 69dec5af23c3..b1c7c0713e11 100644
>>>>> --- a/drivers/fpga/ice40-spi.c
>>>>> +++ b/drivers/fpga/ice40-spi.c
>>>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>>>>           return ret;
>>>>>       }
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>>>>> -                   &ice40_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>>>>> +                     NULL, &ice40_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct of_device_id ice40_fpga_of_match[] = {
>>>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>>>>> index 114a64d2b7a4..75cc6efaa1b6 100644
>>>>> --- a/drivers/fpga/machxo2-spi.c
>>>>> +++ b/drivers/fpga/machxo2-spi.c
>>>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>>>>           return -EINVAL;
>>>>>       }
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>>> -                   &machxo2_ops, spi);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>>> +                     NULL, &machxo2_ops, spi);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct of_device_id of_match[] = {
>>>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>>>>> index 573d88bdf730..80b8891fb302 100644
>>>>> --- a/drivers/fpga/socfpga-a10.c
>>>>> +++ b/drivers/fpga/socfpga-a10.c
>>>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>>>>           return -EBUSY;
>>>>>       }
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>>>>> -                   &socfpga_a10_fpga_mgr_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    platform_set_drvdata(pdev, mgr);
>>>>> -
>>>>> -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>>>>> +                NULL, &socfpga_a10_fpga_mgr_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>           clk_disable_unprepare(priv->clk);
>>>>> -        return ret;
>>>>> +        return PTR_ERR(mgr);
>>>>>       }
>>>>>   +    platform_set_drvdata(pdev, mgr);
>>>>> +
>>>>>       return 0;
>>>>>   }
>>>>>   diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>>>>> index 1f467173fc1f..06edfab8a496 100644
>>>>> --- a/drivers/fpga/socfpga.c
>>>>> +++ b/drivers/fpga/socfpga.c
>>>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>>>>       if (ret)
>>>>>           return ret;
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>>>>> -                   &socfpga_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>>>>> +                     NULL, &socfpga_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     #ifdef CONFIG_OF
>>>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>>>>> index 657a70c5fc99..434094c3ac69 100644
>>>>> --- a/drivers/fpga/stratix10-soc.c
>>>>> +++ b/drivers/fpga/stratix10-soc.c
>>>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>>>>         init_completion(&priv->status_return_completion);
>>>>>   -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>>>>> -                  &s10_ops, priv);
>>>>> -    if (!mgr) {
>>>>> -        dev_err(dev, "unable to create FPGA manager\n");
>>>>> -        ret = -ENOMEM;
>>>>> -        goto probe_err;
>>>>> -    }
>>>>> -
>>>>> -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>>>>> +                NULL, &s10_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>           dev_err(dev, "unable to register FPGA manager\n");
>>>>> -        fpga_mgr_free(mgr);
>>>>> +        ret = PTR_ERR(mgr);
>>>>>           goto probe_err;
>>>>>       }
>>>>>   diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>>>>> index 101f016c6ed8..5efbec2b9f9f 100644
>>>>> --- a/drivers/fpga/ts73xx-fpga.c
>>>>> +++ b/drivers/fpga/ts73xx-fpga.c
>>>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>>>>       if (IS_ERR(priv->io_base))
>>>>>           return PTR_ERR(priv->io_base);
>>>>>   -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>>>>> -                   &ts73xx_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>>>> This is missing compat_id param.
>>> Thanks - I thought I'll do another pass and make sure I catch them all.
>>>
>>>> Similar for others.
>>>>
>>>>> +                     &ts73xx_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(kdev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static struct platform_driver ts73xx_fpga_driver = {
>>>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>>>>> index fee4d0abf6bf..6f1894929687 100644
>>>>> --- a/drivers/fpga/xilinx-spi.c
>>>>> +++ b/drivers/fpga/xilinx-spi.c
>>>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>>>>           return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>>>>                        "Failed to get DONE gpio\n");
>>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev,
>>>>> -                   "Xilinx Slave Serial FPGA Manager",
>>>>> -                   &xilinx_spi_ops, conf);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(&spi->dev,
>>>>> +                     "Xilinx Slave Serial FPGA Manager",
>>>>> +                     NULL, &xilinx_spi_ops, conf);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct of_device_id xlnx_spi_of_match[] = {
>>>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>>>>> index 07fa8d9ec675..74bbb9710f4e 100644
>>>>> --- a/drivers/fpga/zynq-fpga.c
>>>>> +++ b/drivers/fpga/zynq-fpga.c
>>>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>>>>         clk_disable(priv->clk);
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>>>>> -                   &zynq_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    platform_set_drvdata(pdev, mgr);
>>>>> -
>>>>> -    err = fpga_mgr_register(mgr);
>>>>> -    if (err) {
>>>>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>>>> This is missing compat_id param as well.
>>>>> +                &zynq_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>           dev_err(dev, "unable to register FPGA manager\n");
>>>>>           clk_unprepare(priv->clk);
>>>>> -        return err;
>>>>> +        return PTR_ERR(mgr);
>>>>>       }
>>>>>   +    platform_set_drvdata(pdev, mgr);
>>>>> +
>>>>>       return 0;
>>>>>   }
>>>>>   diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>>>>> index 125743c9797f..1a6d520985be 100644
>>>>> --- a/drivers/fpga/zynqmp-fpga.c
>>>>> +++ b/drivers/fpga/zynqmp-fpga.c
>>>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>>>>         priv->dev = dev;
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>>>>> -                   &zynqmp_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>>>>> +                     &zynqmp_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct of_device_id zynqmp_fpga_of_match[] = {
>>>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>>>>> index 2bc3030a69e5..3e1859cc0640 100644
>>>>> --- a/include/linux/fpga/fpga-mgr.h
>>>>> +++ b/include/linux/fpga/fpga-mgr.h
>>>>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>>>>     void fpga_mgr_put(struct fpga_manager *mgr);
>>>>>   -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                     const struct fpga_manager_ops *mops,
>>>>> -                     void *priv);
>>>> My biggest concern is that removing this will break all oot fpga drivers.
>>>>
>>>> The other patches have similar api changes
>>>>
>>>> Having a stable api is very important.
>>> That is a good point. While Greg did call out the separation of the
>>> device_initialize() and device_add() calls, his biggest concern was with
>>> the empty dev_release() functions. Maybe it would be best to first submit
>>> a patch that only addresses the dev_release() functions? Maybe it would be
>>> OK to leave the create and register functions as separate functions in order
>>> to maintain a stable API...
>> On further investigation, I don't think we have much choice in changing
>> the API. We are almost coming full circle here. In 2018, there was only
>> an fpga_mgr_register() function. It was returning an errno value or zero.
>> It was also setting the drvdata of the parent driver with a pointer to
>> the resulting fpga_manager structure.
>>
>> It looks like the reason the interface was split into two (create and
>> register) was so that the fpga_manager pointer could be returned (by the
>> create function) without writing the drvdata of the parent driver:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7085e2a94f7df5f419e3cfb2fe809ce6564e9629
>>
>> When the interfaces were split, fpga_mgr_free() was also created to free
>> the memory if the register function was not called. The fpga_mgr_free()
>> function was also called by the fpga_mgr_dev_release() function. A later
>> patch was submitted to simplify the memory management for the class driver:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=084181fe8cc7472695b907f0018f4cd00934cb12
>>
>> It is this second commit that empties the fpga_mgr_dev_release() function
>> and instead uses devm_fpga_mgr_create() to manage the memory for the class
>> driver.
>>
>> The fix I am proposing essentially takes us back to the 2018 implementation,
>> but returns the fpga_manager pointer or ERR_PTR() instead of returning zero or
>> an errno.
>>
>> The definition of the API is integral to the memory management issue. I cannot
>> think of an acceptable solution for the upstream that doesn't involve
>> changing the API. It could result in some ifdef's in an out-of-tree driver,
>> but I don't think we can avoid it in this case.
> I agree the stable API is important, and I think it is still possible to fix
> the memory management issue while keeping the APIs.
>
> We should do the actual resource free work in class.dev_release(), and
> fpga_mgr_free should call put_device() to trigger it.
>
> And I see no problem with the current devm APIs.

Consider the case where both devm_fpga_mgr_create() and devm_fpga_mgr_register()
are called, and both succeed. When the driver is unloaded devm_fpga_mgr_release()
is called on behalf of the create and it will call put_device(). Then
devm_fpga_mgr_unregister() will be called on behalf of the register, and it
will call device_unregister(), which also calls put_device(). In this case, I
think there is an extra call to put_device(), right? Or is that not a problem?

The only way I can think to avoid the extra put_device() call for this case
would be to set a flag when-and-if fpga_mgr_register() succeeds and then refuse
to call put_device() in devm_fpga_mgr_release() in order to avoid the duplicate
call to put_device(). But that feels like a hack to me.

Am I missing something? Is there a cleaner way to make this work?

- Russ
>
> Thanks,
> Yilun
>
>>> - Russ
>>>
>>>> My recommendation is to keep the public api and hide the change with an exported but not public function.
>>>>
>>>> Tom
>>>>
>>>>> -void fpga_mgr_free(struct fpga_manager *mgr);
>>>>> -int fpga_mgr_register(struct fpga_manager *mgr);
>>>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>>>>> +                       struct fpga_compat_id *compat_id,
>>>>> +                       const struct fpga_manager_ops *mops,
>>>>> +                       void *priv);
>>>>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
>>>>>   -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>>>>> -
>>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                      const struct fpga_manager_ops *mops,
>>>>> -                      void *priv);
>>>>> +struct fpga_manager *
>>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>>> +               struct fpga_compat_id *compat_id,
>>>>> +               const struct fpga_manager_ops *mops,
>>>>> +               void *priv);
>>>>>     #endif /*_LINUX_FPGA_MGR_H */


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-07 23:50           ` Russ Weight
@ 2021-06-08  1:40             ` Xu Yilun
  2021-06-08 16:44               ` Russ Weight
  0 siblings, 1 reply; 19+ messages in thread
From: Xu Yilun @ 2021-06-08  1:40 UTC (permalink / raw)
  To: Russ Weight
  Cc: Tom Rix, mdf, linux-fpga, lgoncalv, hao.wu, matthew.gerlach,
	richard.gong

On Mon, Jun 07, 2021 at 04:50:58PM -0700, Russ Weight wrote:
> 
> 
> On 6/5/21 8:42 AM, Xu Yilun wrote:
> > On Thu, Jun 03, 2021 at 03:45:17PM -0700, Russ Weight wrote:
> >>
> >> On 6/1/21 5:38 PM, Russ Weight wrote:
> >>> On 6/1/21 4:24 PM, Tom Rix wrote:
> >>>> On 5/28/21 2:57 PM, Russ Weight wrote:
> >>>>> The FPGA manager class driver data structure is being treated as a
> >>>>> managed resource instead of using standard dev_release call-back
> >>>>> to release the class data structure. This change removes the
> >>>>> managed resource code for the freeing of the class data structure
> >>>>> and combines the create() and register() functions into a single
> >>>>> register() function.
> >>>>>
> >>>>> The devm_fpga_mgr_register() function is retained.
> >>>>>
> >>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> >>>>> ---
> >>>>> v4:
> >>>>>    - Added the compat_id parameter to fpga_mgr_register() and
> >>>> On a refactor, it is best to do the refactoring as mechanically as possible.
> >>>>
> >>>> While compat_id may be useful, it should be done in a separate patch.
> >>> These would have to be included with the changes merge the create and register
> >>> functions into a single function.
> >>>
> >>>> Unless it is really needed for your next security update patch consider resubmitting later
> >>>>
> >>>>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
> >>>>>      the device_register() call.
> >>>>> v3:
> >>>>>    - Cleaned up comment header for fpga_mgr_register()
> >>>>>    - Fix error return on ida_simple_get() failure
> >>>>> v2:
> >>>>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
> >>>>>      create/register functionality.
> >>>>>    - All previous callers of devm_fpga_mgr_register() will continue to call
> >>>>>      devm_fpga_mgr_register().
> >>>>>    - replaced unnecessary ternary operators in return statements with standard
> >>>>>      if conditions.
> >>>>> ---
> >>>>>   drivers/fpga/altera-cvp.c        |  12 +--
> >>>>>   drivers/fpga/altera-pr-ip-core.c |   9 +-
> >>>>>   drivers/fpga/altera-ps-spi.c     |  10 +-
> >>>>>   drivers/fpga/dfl-fme-mgr.c       |  12 +--
> >>>>>   drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
> >>>>>   drivers/fpga/ice40-spi.c         |  10 +-
> >>>>>   drivers/fpga/machxo2-spi.c       |  10 +-
> >>>>>   drivers/fpga/socfpga-a10.c       |  16 ++-
> >>>>>   drivers/fpga/socfpga.c           |  10 +-
> >>>>>   drivers/fpga/stratix10-soc.c     |  15 +--
> >>>>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
> >>>>>   drivers/fpga/xilinx-spi.c        |  12 +--
> >>>>>   drivers/fpga/zynq-fpga.c         |  16 ++-
> >>>>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
> >>>>>   include/linux/fpga/fpga-mgr.h    |  19 ++--
> >>>>>   15 files changed, 120 insertions(+), 219 deletions(-)
> >>>> ./Documentation/driver-api/fpga/fpga-mgr.rst
> >>>>
> >>>> still refers to fpga_mgr_create.
> >>> Thanks for catching that.
> >>>
> >>>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> >>>>> index 4e0edb60bfba..25eaa97ae7aa 100644
> >>>>> --- a/drivers/fpga/altera-cvp.c
> >>>>> +++ b/drivers/fpga/altera-cvp.c
> >>>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
> >>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
> >>>>>            ALTERA_CVP_MGR_NAME, pci_name(pdev));
> >>>>>   -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> >>>>> -                   &altera_cvp_ops, conf);
> >>>>> -    if (!mgr) {
> >>>>> -        ret = -ENOMEM;
> >>>>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
> >>>>> +                &altera_cvp_ops, conf);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>> +        ret = PTR_ERR(mgr);
> >>>>>           goto err_unmap;
> >>>>>       }
> >>>>>         pci_set_drvdata(pdev, mgr);
> >>>>>   -    ret = fpga_mgr_register(mgr);
> >>>>> -    if (ret)
> >>>>> -        goto err_unmap;
> >>>>> -
> >>>>>       return 0;
> >>>>>     err_unmap:
> >>>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
> >>>>> index 5b130c4d9882..bfd4de1e7ac5 100644
> >>>>> --- a/drivers/fpga/altera-pr-ip-core.c
> >>>>> +++ b/drivers/fpga/altera-pr-ip-core.c
> >>>>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
> >>>>>           (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
> >>>>>           (int)(val & ALT_PR_CSR_PR_START));
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
> >>>>> +                     &alt_pr_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>   EXPORT_SYMBOL_GPL(alt_pr_register);
> >>>>>   diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> >>>>> index 23bfd4d1ad0f..e6fc7208213c 100644
> >>>>> --- a/drivers/fpga/altera-ps-spi.c
> >>>>> +++ b/drivers/fpga/altera-ps-spi.c
> >>>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
> >>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
> >>>>>            dev_driver_string(&spi->dev), dev_name(&spi->dev));
> >>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> >>>>> -                   &altera_ps_ops, conf);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
> >>>>> +                     &altera_ps_ops, conf);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct spi_device_id altera_ps_spi_ids[] = {
> >>>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> >>>>> index d5861d13b306..c9b98afa7d50 100644
> >>>>> --- a/drivers/fpga/dfl-fme-mgr.c
> >>>>> +++ b/drivers/fpga/dfl-fme-mgr.c
> >>>>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
> >>>>>         fme_mgr_get_compat_id(priv->ioaddr, compat_id);
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
> >>>>> -                   &fme_mgr_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> -
> >>>>> -    mgr->compat_id = compat_id;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
> >>>>> +                     &fme_mgr_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static struct platform_driver fme_mgr_driver = {
> >>>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> >>>>> index b85bc47c91a9..d6d64ce34417 100644
> >>>>> --- a/drivers/fpga/fpga-mgr.c
> >>>>> +++ b/drivers/fpga/fpga-mgr.c
> >>>>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
> >>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
> >>>>>     /**
> >>>>> - * fpga_mgr_create - create and initialize a FPGA manager struct
> >>>>> + * fpga_mgr_register - create and register a FPGA Manager device
> >>>>>    * @dev:    fpga manager device from pdev
> >>>>>    * @name:    fpga manager name
> >>>>> + * @compat_id:    FPGA manager id for compatibility check.
> >>>> for consistency, use lowercase fpga
> >>>>
> >>>> similar issue below.
> >>> OK
> >>>
> >>>>>    * @mops:    pointer to structure of fpga manager ops
> >>>>>    * @priv:    fpga manager private data
> >>>>>    *
> >>>>> - * The caller of this function is responsible for freeing the struct with
> >>>>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
> >>>>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
> >>>>> + * Using devm_fpga_mgr_register instead is recommended.
> >>>>>    *
> >>>>> - * Return: pointer to struct fpga_manager or NULL
> >>>>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
> >>>>>    */
> >>>>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>>>> -                     const struct fpga_manager_ops *mops,
> >>>>> -                     void *priv)
> >>>>> +struct fpga_manager *
> >>>>> +fpga_mgr_register(struct device *dev, const char *name,
> >>>>> +          struct fpga_compat_id *compat_id,
> >>>>> +          const struct fpga_manager_ops *mops, void *priv)
> >>>>>   {
> >>>>>       struct fpga_manager *mgr;
> >>>>>       int id, ret;
> >>>>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>>>>           !mops->write_init || (!mops->write && !mops->write_sg) ||
> >>>>>           (mops->write && mops->write_sg)) {
> >>>>>           dev_err(dev, "Attempt to register without fpga_manager_ops\n");
> >>>>> -        return NULL;
> >>>>> +        return ERR_PTR(-EINVAL);
> >>>>>       }
> >>>>>         if (!name || !strlen(name)) {
> >>>>>           dev_err(dev, "Attempt to register with no name!\n");
> >>>>> -        return NULL;
> >>>>> +        return ERR_PTR(-EINVAL);
> >>>>>       }
> >>>>>         mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
> >>>>>       if (!mgr)
> >>>>> -        return NULL;
> >>>>> +        return ERR_PTR(-ENOMEM);
> >>>>>         id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
> >>>>> -    if (id < 0)
> >>>>> +    if (id < 0) {
> >>>>> +        ret = id;
> >>>> ida_* returns a normal error, so needs a wrapping in
> >>>>
> >>>> ret = ERR_PTR(id)
> >>> Thanks - I'll fix that
> >> Actually, if you follow the "goto error_kfree", you will see that ret is
> >> wrapped in ERR_PTR() in the return statement, so id does not need to be
> >> wrapped here.
> >>
> >>>>>           goto error_kfree;
> >>>>> +    }
> >>>>>         mutex_init(&mgr->ref_mutex);
> >>>>>         mgr->name = name;
> >>>>> +    mgr->compat_id = compat_id;
> >>>>>       mgr->mops = mops;
> >>>>>       mgr->priv = priv;
> >>>>>   -    device_initialize(&mgr->dev);
> >>>>>       mgr->dev.class = fpga_mgr_class;
> >>>>>       mgr->dev.groups = mops->groups;
> >>>>>       mgr->dev.parent = dev;
> >>>>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>>>>       if (ret)
> >>>>>           goto error_device;
> >>>>>   -    return mgr;
> >>>>> -
> >>>>> -error_device:
> >>>>> -    ida_simple_remove(&fpga_mgr_ida, id);
> >>>>> -error_kfree:
> >>>>> -    kfree(mgr);
> >>>>> -
> >>>>> -    return NULL;
> >>>>> -}
> >>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
> >>>>> -
> >>>>> -/**
> >>>>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
> >>>>> - * @mgr:    fpga manager struct
> >>>>> - */
> >>>>> -void fpga_mgr_free(struct fpga_manager *mgr)
> >>>>> -{
> >>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> >>>>> -    kfree(mgr);
> >>>>> -}
> >>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
> >>>>> -
> >>>>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
> >>>>> -{
> >>>>> -    struct fpga_mgr_devres *dr = res;
> >>>>> -
> >>>>> -    fpga_mgr_free(dr->mgr);
> >>>>> -}
> >>>>> -
> >>>>> -/**
> >>>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
> >>>>> - * @dev:    fpga manager device from pdev
> >>>>> - * @name:    fpga manager name
> >>>>> - * @mops:    pointer to structure of fpga manager ops
> >>>>> - * @priv:    fpga manager private data
> >>>>> - *
> >>>>> - * This function is intended for use in a FPGA manager driver's probe function.
> >>>>> - * After the manager driver creates the manager struct with
> >>>>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
> >>>>> - * manager driver's remove function should call fpga_mgr_unregister().  The
> >>>>> - * manager 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_mgr_register(), the struct will still get cleaned up.
> >>>>> - *
> >>>>> - * Return: pointer to struct fpga_manager or NULL
> >>>>> - */
> >>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> >>>>> -                      const struct fpga_manager_ops *mops,
> >>>>> -                      void *priv)
> >>>>> -{
> >>>>> -    struct fpga_mgr_devres *dr;
> >>>>> -
> >>>>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
> >>>>> -    if (!dr)
> >>>>> -        return NULL;
> >>>>> -
> >>>>> -    dr->mgr = fpga_mgr_create(dev, name, mops, priv);
> >>>>> -    if (!dr->mgr) {
> >>>>> -        devres_free(dr);
> >>>>> -        return NULL;
> >>>>> -    }
> >>>>> -
> >>>>> -    devres_add(dev, dr);
> >>>>> -
> >>>>> -    return dr->mgr;
> >>>>> -}
> >>>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
> >>>>> -
> >>>>> -/**
> >>>>> - * fpga_mgr_register - register a FPGA manager
> >>>>> - * @mgr: fpga manager struct
> >>>>> - *
> >>>>> - * Return: 0 on success, negative error code otherwise.
> >>>>> - */
> >>>>> -int fpga_mgr_register(struct fpga_manager *mgr)
> >>>>> -{
> >>>>> -    int ret;
> >>>>> -
> >>>>>       /*
> >>>>>        * Initialize framework state by requesting low level driver read state
> >>>>>        * from device.  FPGA may be in reset mode or may have been programmed
> >>>>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
> >>>>>        */
> >>>>>       mgr->state = mgr->mops->state(mgr);
> >>>>>   -    ret = device_add(&mgr->dev);
> >>>>> -    if (ret)
> >>>>> -        goto error_device;
> >>>>> -
> >>>>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
> >>>> can we keep this dev_info() line and move it below device_register() ?
> >>> In Greg's response to my driver, he said he said that a well-behaved driver does
> >>> not need dev_info() calls...
> >>>>> +    ret = device_register(&mgr->dev);
> >>>>> +    if (ret) {
> >>>>> +        put_device(&mgr->dev);
> >>>> pr_err() here ?
> >>> OK - I can add a message
> >>>
> >>>>> +        return ERR_PTR(ret);
> >>>>> +    }
> >>>>>   -    return 0;
> >>>>> +    return mgr;
> >>>>>     error_device:
> >>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> >>>>> +    ida_simple_remove(&fpga_mgr_ida, id);
> >>>>> +error_kfree:
> >>>>> +    kfree(mgr);
> >>>>>   -    return ret;
> >>>>> +    return ERR_PTR(ret);
> >>>>>   }
> >>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
> >>>>>   @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
> >>>>>   }
> >>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
> >>>>>   -static int fpga_mgr_devres_match(struct device *dev, void *res,
> >>>>> -                 void *match_data)
> >>>>> -{
> >>>>> -    struct fpga_mgr_devres *dr = res;
> >>>>> -
> >>>>> -    return match_data == dr->mgr;
> >>>>> -}
> >>>>> -
> >>>>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
> >>>>>   {
> >>>>>       struct fpga_mgr_devres *dr = res;
> >>>>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
> >>>>>     /**
> >>>>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
> >>>>> - * @dev: managing device for this FPGA manager
> >>>>> - * @mgr: fpga manager struct
> >>>>> + * @dev:    fpga manager device from pdev
> >>>>> + * @name:    fpga manager name
> >>>>> + * @compat_id:    FPGA manager id for compatibility check.
> >>>>> + * @mops:    pointer to structure of fpga manager ops
> >>>>> + * @priv:    fpga manager private data
> >>>>>    *
> >>>>>    * This is the devres variant of fpga_mgr_register() for which the unregister
> >>>>>    * function will be called automatically when the managing device is detached.
> >>>>>    */
> >>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
> >>>>> +struct fpga_manager *
> >>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
> >>>>> +               struct fpga_compat_id *compat_id,
> >>>>> +               const struct fpga_manager_ops *mops, void *priv)
> >>>>>   {
> >>>>>       struct fpga_mgr_devres *dr;
> >>>>> -    int ret;
> >>>>> -
> >>>>> -    /*
> >>>>> -     * Make sure that the struct fpga_manager * that is passed in is
> >>>>> -     * managed itself.
> >>>>> -     */
> >>>>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
> >>>>> -                 fpga_mgr_devres_match, mgr)))
> >>>>> -        return -EINVAL;
> >>>>> +    struct fpga_manager *mgr;
> >>>>>         dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
> >>>>>       if (!dr)
> >>>>> -        return -ENOMEM;
> >>>>> +        return ERR_PTR(-ENOMEM);
> >>>>>   -    ret = fpga_mgr_register(mgr);
> >>>>> -    if (ret) {
> >>>>> +    mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>>           devres_free(dr);
> >>>>> -        return ret;
> >>>>> +        return mgr;
> >>>>>       }
> >>>>>         dr->mgr = mgr;
> >>>>>       devres_add(dev, dr);
> >>>>>   -    return 0;
> >>>>> +    return mgr;
> >>>>>   }
> >>>>>   EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
> >>>>>     static void fpga_mgr_dev_release(struct device *dev)
> >>>>>   {
> >>>>> +    struct fpga_manager *mgr = to_fpga_manager(dev);
> >>>>> +
> >>>>> +    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> >>>>> +    kfree(mgr);
> >>>>>   }
> >>>>>     static int __init fpga_mgr_class_init(void)
> >>>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> >>>>> index 69dec5af23c3..b1c7c0713e11 100644
> >>>>> --- a/drivers/fpga/ice40-spi.c
> >>>>> +++ b/drivers/fpga/ice40-spi.c
> >>>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
> >>>>>           return ret;
> >>>>>       }
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
> >>>>> -                   &ice40_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> >>>>> +                     NULL, &ice40_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct of_device_id ice40_fpga_of_match[] = {
> >>>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
> >>>>> index 114a64d2b7a4..75cc6efaa1b6 100644
> >>>>> --- a/drivers/fpga/machxo2-spi.c
> >>>>> +++ b/drivers/fpga/machxo2-spi.c
> >>>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
> >>>>>           return -EINVAL;
> >>>>>       }
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
> >>>>> -                   &machxo2_ops, spi);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
> >>>>> +                     NULL, &machxo2_ops, spi);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct of_device_id of_match[] = {
> >>>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
> >>>>> index 573d88bdf730..80b8891fb302 100644
> >>>>> --- a/drivers/fpga/socfpga-a10.c
> >>>>> +++ b/drivers/fpga/socfpga-a10.c
> >>>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
> >>>>>           return -EBUSY;
> >>>>>       }
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
> >>>>> -                   &socfpga_a10_fpga_mgr_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> -
> >>>>> -    platform_set_drvdata(pdev, mgr);
> >>>>> -
> >>>>> -    ret = fpga_mgr_register(mgr);
> >>>>> -    if (ret) {
> >>>>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
> >>>>> +                NULL, &socfpga_a10_fpga_mgr_ops, priv);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>>           clk_disable_unprepare(priv->clk);
> >>>>> -        return ret;
> >>>>> +        return PTR_ERR(mgr);
> >>>>>       }
> >>>>>   +    platform_set_drvdata(pdev, mgr);
> >>>>> +
> >>>>>       return 0;
> >>>>>   }
> >>>>>   diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
> >>>>> index 1f467173fc1f..06edfab8a496 100644
> >>>>> --- a/drivers/fpga/socfpga.c
> >>>>> +++ b/drivers/fpga/socfpga.c
> >>>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
> >>>>>       if (ret)
> >>>>>           return ret;
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
> >>>>> -                   &socfpga_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
> >>>>> +                     NULL, &socfpga_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     #ifdef CONFIG_OF
> >>>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> >>>>> index 657a70c5fc99..434094c3ac69 100644
> >>>>> --- a/drivers/fpga/stratix10-soc.c
> >>>>> +++ b/drivers/fpga/stratix10-soc.c
> >>>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
> >>>>>         init_completion(&priv->status_return_completion);
> >>>>>   -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
> >>>>> -                  &s10_ops, priv);
> >>>>> -    if (!mgr) {
> >>>>> -        dev_err(dev, "unable to create FPGA manager\n");
> >>>>> -        ret = -ENOMEM;
> >>>>> -        goto probe_err;
> >>>>> -    }
> >>>>> -
> >>>>> -    ret = fpga_mgr_register(mgr);
> >>>>> -    if (ret) {
> >>>>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
> >>>>> +                NULL, &s10_ops, priv);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>>           dev_err(dev, "unable to register FPGA manager\n");
> >>>>> -        fpga_mgr_free(mgr);
> >>>>> +        ret = PTR_ERR(mgr);
> >>>>>           goto probe_err;
> >>>>>       }
> >>>>>   diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
> >>>>> index 101f016c6ed8..5efbec2b9f9f 100644
> >>>>> --- a/drivers/fpga/ts73xx-fpga.c
> >>>>> +++ b/drivers/fpga/ts73xx-fpga.c
> >>>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
> >>>>>       if (IS_ERR(priv->io_base))
> >>>>>           return PTR_ERR(priv->io_base);
> >>>>>   -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
> >>>>> -                   &ts73xx_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
> >>>> This is missing compat_id param.
> >>> Thanks - I thought I'll do another pass and make sure I catch them all.
> >>>
> >>>> Similar for others.
> >>>>
> >>>>> +                     &ts73xx_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(kdev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static struct platform_driver ts73xx_fpga_driver = {
> >>>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
> >>>>> index fee4d0abf6bf..6f1894929687 100644
> >>>>> --- a/drivers/fpga/xilinx-spi.c
> >>>>> +++ b/drivers/fpga/xilinx-spi.c
> >>>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
> >>>>>           return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
> >>>>>                        "Failed to get DONE gpio\n");
> >>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev,
> >>>>> -                   "Xilinx Slave Serial FPGA Manager",
> >>>>> -                   &xilinx_spi_ops, conf);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(&spi->dev,
> >>>>> +                     "Xilinx Slave Serial FPGA Manager",
> >>>>> +                     NULL, &xilinx_spi_ops, conf);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct of_device_id xlnx_spi_of_match[] = {
> >>>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
> >>>>> index 07fa8d9ec675..74bbb9710f4e 100644
> >>>>> --- a/drivers/fpga/zynq-fpga.c
> >>>>> +++ b/drivers/fpga/zynq-fpga.c
> >>>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
> >>>>>         clk_disable(priv->clk);
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
> >>>>> -                   &zynq_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> -
> >>>>> -    platform_set_drvdata(pdev, mgr);
> >>>>> -
> >>>>> -    err = fpga_mgr_register(mgr);
> >>>>> -    if (err) {
> >>>>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
> >>>> This is missing compat_id param as well.
> >>>>> +                &zynq_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>>           dev_err(dev, "unable to register FPGA manager\n");
> >>>>>           clk_unprepare(priv->clk);
> >>>>> -        return err;
> >>>>> +        return PTR_ERR(mgr);
> >>>>>       }
> >>>>>   +    platform_set_drvdata(pdev, mgr);
> >>>>> +
> >>>>>       return 0;
> >>>>>   }
> >>>>>   diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
> >>>>> index 125743c9797f..1a6d520985be 100644
> >>>>> --- a/drivers/fpga/zynqmp-fpga.c
> >>>>> +++ b/drivers/fpga/zynqmp-fpga.c
> >>>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
> >>>>>         priv->dev = dev;
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
> >>>>> -                   &zynqmp_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
> >>>>> +                     &zynqmp_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct of_device_id zynqmp_fpga_of_match[] = {
> >>>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> >>>>> index 2bc3030a69e5..3e1859cc0640 100644
> >>>>> --- a/include/linux/fpga/fpga-mgr.h
> >>>>> +++ b/include/linux/fpga/fpga-mgr.h
> >>>>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
> >>>>>     void fpga_mgr_put(struct fpga_manager *mgr);
> >>>>>   -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>>>> -                     const struct fpga_manager_ops *mops,
> >>>>> -                     void *priv);
> >>>> My biggest concern is that removing this will break all oot fpga drivers.
> >>>>
> >>>> The other patches have similar api changes
> >>>>
> >>>> Having a stable api is very important.
> >>> That is a good point. While Greg did call out the separation of the
> >>> device_initialize() and device_add() calls, his biggest concern was with
> >>> the empty dev_release() functions. Maybe it would be best to first submit
> >>> a patch that only addresses the dev_release() functions? Maybe it would be
> >>> OK to leave the create and register functions as separate functions in order
> >>> to maintain a stable API...
> >> On further investigation, I don't think we have much choice in changing
> >> the API. We are almost coming full circle here. In 2018, there was only
> >> an fpga_mgr_register() function. It was returning an errno value or zero.
> >> It was also setting the drvdata of the parent driver with a pointer to
> >> the resulting fpga_manager structure.
> >>
> >> It looks like the reason the interface was split into two (create and
> >> register) was so that the fpga_manager pointer could be returned (by the
> >> create function) without writing the drvdata of the parent driver:
> >>
> >> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7085e2a94f7df5f419e3cfb2fe809ce6564e9629
> >>
> >> When the interfaces were split, fpga_mgr_free() was also created to free
> >> the memory if the register function was not called. The fpga_mgr_free()
> >> function was also called by the fpga_mgr_dev_release() function. A later
> >> patch was submitted to simplify the memory management for the class driver:
> >>
> >> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=084181fe8cc7472695b907f0018f4cd00934cb12
> >>
> >> It is this second commit that empties the fpga_mgr_dev_release() function
> >> and instead uses devm_fpga_mgr_create() to manage the memory for the class
> >> driver.
> >>
> >> The fix I am proposing essentially takes us back to the 2018 implementation,
> >> but returns the fpga_manager pointer or ERR_PTR() instead of returning zero or
> >> an errno.
> >>
> >> The definition of the API is integral to the memory management issue. I cannot
> >> think of an acceptable solution for the upstream that doesn't involve
> >> changing the API. It could result in some ifdef's in an out-of-tree driver,
> >> but I don't think we can avoid it in this case.
> > I agree the stable API is important, and I think it is still possible to fix
> > the memory management issue while keeping the APIs.
> >
> > We should do the actual resource free work in class.dev_release(), and
> > fpga_mgr_free should call put_device() to trigger it.
> >
> > And I see no problem with the current devm APIs.
> 
> Consider the case where both devm_fpga_mgr_create() and devm_fpga_mgr_register()
> are called, and both succeed. When the driver is unloaded devm_fpga_mgr_release()
> is called on behalf of the create and it will call put_device(). Then
> devm_fpga_mgr_unregister() will be called on behalf of the register, and it
> will call device_unregister(), which also calls put_device(). In this case, I
> think there is an extra call to put_device(), right? Or is that not a problem?

The real problem is the non-devm functions.

fpga_mgr_create() calls device_initialize() so fpga_mgr_free() should call
put_device() for deinitialization.

fpga_mgr_register() calls device_add(), so fpga_mgr_unregister() should
call device_del().

Thanks,
Yilun

> 
> The only way I can think to avoid the extra put_device() call for this case
> would be to set a flag when-and-if fpga_mgr_register() succeeds and then refuse
> to call put_device() in devm_fpga_mgr_release() in order to avoid the duplicate
> call to put_device(). But that feels like a hack to me.
> 
> Am I missing something? Is there a cleaner way to make this work?
> 
> - Russ
> >
> > Thanks,
> > Yilun
> >
> >>> - Russ
> >>>
> >>>> My recommendation is to keep the public api and hide the change with an exported but not public function.
> >>>>
> >>>> Tom
> >>>>
> >>>>> -void fpga_mgr_free(struct fpga_manager *mgr);
> >>>>> -int fpga_mgr_register(struct fpga_manager *mgr);
> >>>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
> >>>>> +                       struct fpga_compat_id *compat_id,
> >>>>> +                       const struct fpga_manager_ops *mops,
> >>>>> +                       void *priv);
> >>>>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
> >>>>>   -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
> >>>>> -
> >>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> >>>>> -                      const struct fpga_manager_ops *mops,
> >>>>> -                      void *priv);
> >>>>> +struct fpga_manager *
> >>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
> >>>>> +               struct fpga_compat_id *compat_id,
> >>>>> +               const struct fpga_manager_ops *mops,
> >>>>> +               void *priv);
> >>>>>     #endif /*_LINUX_FPGA_MGR_H */

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-08  1:40             ` Xu Yilun
@ 2021-06-08 16:44               ` Russ Weight
  0 siblings, 0 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-08 16:44 UTC (permalink / raw)
  To: Xu Yilun
  Cc: Tom Rix, mdf, linux-fpga, lgoncalv, hao.wu, matthew.gerlach,
	richard.gong



On 6/7/21 6:40 PM, Xu Yilun wrote:
> On Mon, Jun 07, 2021 at 04:50:58PM -0700, Russ Weight wrote:
>>
>> On 6/5/21 8:42 AM, Xu Yilun wrote:
>>> On Thu, Jun 03, 2021 at 03:45:17PM -0700, Russ Weight wrote:
>>>> On 6/1/21 5:38 PM, Russ Weight wrote:
>>>>> On 6/1/21 4:24 PM, Tom Rix wrote:
>>>>>> On 5/28/21 2:57 PM, Russ Weight wrote:
>>>>>>> The FPGA manager class driver data structure is being treated as a
>>>>>>> managed resource instead of using standard dev_release call-back
>>>>>>> to release the class data structure. This change removes the
>>>>>>> managed resource code for the freeing of the class data structure
>>>>>>> and combines the create() and register() functions into a single
>>>>>>> register() function.
>>>>>>>
>>>>>>> The devm_fpga_mgr_register() function is retained.
>>>>>>>
>>>>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>>>>>> ---
>>>>>>> v4:
>>>>>>>    - Added the compat_id parameter to fpga_mgr_register() and
>>>>>> On a refactor, it is best to do the refactoring as mechanically as possible.
>>>>>>
>>>>>> While compat_id may be useful, it should be done in a separate patch.
>>>>> These would have to be included with the changes merge the create and register
>>>>> functions into a single function.
>>>>>
>>>>>> Unless it is really needed for your next security update patch consider resubmitting later
>>>>>>
>>>>>>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
>>>>>>>      the device_register() call.
>>>>>>> v3:
>>>>>>>    - Cleaned up comment header for fpga_mgr_register()
>>>>>>>    - Fix error return on ida_simple_get() failure
>>>>>>> v2:
>>>>>>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>>>>>>      create/register functionality.
>>>>>>>    - All previous callers of devm_fpga_mgr_register() will continue to call
>>>>>>>      devm_fpga_mgr_register().
>>>>>>>    - replaced unnecessary ternary operators in return statements with standard
>>>>>>>      if conditions.
>>>>>>> ---
>>>>>>>   drivers/fpga/altera-cvp.c        |  12 +--
>>>>>>>   drivers/fpga/altera-pr-ip-core.c |   9 +-
>>>>>>>   drivers/fpga/altera-ps-spi.c     |  10 +-
>>>>>>>   drivers/fpga/dfl-fme-mgr.c       |  12 +--
>>>>>>>   drivers/fpga/fpga-mgr.c          | 168 +++++++++----------------------
>>>>>>>   drivers/fpga/ice40-spi.c         |  10 +-
>>>>>>>   drivers/fpga/machxo2-spi.c       |  10 +-
>>>>>>>   drivers/fpga/socfpga-a10.c       |  16 ++-
>>>>>>>   drivers/fpga/socfpga.c           |  10 +-
>>>>>>>   drivers/fpga/stratix10-soc.c     |  15 +--
>>>>>>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
>>>>>>>   drivers/fpga/xilinx-spi.c        |  12 +--
>>>>>>>   drivers/fpga/zynq-fpga.c         |  16 ++-
>>>>>>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
>>>>>>>   include/linux/fpga/fpga-mgr.h    |  19 ++--
>>>>>>>   15 files changed, 120 insertions(+), 219 deletions(-)
>>>>>> ./Documentation/driver-api/fpga/fpga-mgr.rst
>>>>>>
>>>>>> still refers to fpga_mgr_create.
>>>>> Thanks for catching that.
>>>>>
>>>>>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>>>>>>> index 4e0edb60bfba..25eaa97ae7aa 100644
>>>>>>> --- a/drivers/fpga/altera-cvp.c
>>>>>>> +++ b/drivers/fpga/altera-cvp.c
>>>>>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>>>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>>>>>>            ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>>>>>>   -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>>>>>>> -                   &altera_cvp_ops, conf);
>>>>>>> -    if (!mgr) {
>>>>>>> -        ret = -ENOMEM;
>>>>>>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, NULL,
>>>>>>> +                &altera_cvp_ops, conf);
>>>>>>> +    if (IS_ERR(mgr)) {
>>>>>>> +        ret = PTR_ERR(mgr);
>>>>>>>           goto err_unmap;
>>>>>>>       }
>>>>>>>         pci_set_drvdata(pdev, mgr);
>>>>>>>   -    ret = fpga_mgr_register(mgr);
>>>>>>> -    if (ret)
>>>>>>> -        goto err_unmap;
>>>>>>> -
>>>>>>>       return 0;
>>>>>>>     err_unmap:
>>>>>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>>>>>>> index 5b130c4d9882..bfd4de1e7ac5 100644
>>>>>>> --- a/drivers/fpga/altera-pr-ip-core.c
>>>>>>> +++ b/drivers/fpga/altera-pr-ip-core.c
>>>>>>> @@ -191,11 +191,12 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>>>>>>           (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>>>>>>           (int)(val & ALT_PR_CSR_PR_START));
>>>>>>>   -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), NULL,
>>>>>>> +                     &alt_pr_ops, priv);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>   EXPORT_SYMBOL_GPL(alt_pr_register);
>>>>>>>   diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>>>>>>> index 23bfd4d1ad0f..e6fc7208213c 100644
>>>>>>> --- a/drivers/fpga/altera-ps-spi.c
>>>>>>> +++ b/drivers/fpga/altera-ps-spi.c
>>>>>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>>>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>>>>>>            dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>>>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>>>>>>> -                   &altera_ps_ops, conf);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, NULL,
>>>>>>> +                     &altera_ps_ops, conf);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>     static const struct spi_device_id altera_ps_spi_ids[] = {
>>>>>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>>>>>>> index d5861d13b306..c9b98afa7d50 100644
>>>>>>> --- a/drivers/fpga/dfl-fme-mgr.c
>>>>>>> +++ b/drivers/fpga/dfl-fme-mgr.c
>>>>>>> @@ -308,14 +308,12 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>>>>>>         fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>>>>>>   -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>>>>>>> -                   &fme_mgr_ops, priv);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> -
>>>>>>> -    mgr->compat_id = compat_id;
>>>>>>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager", compat_id,
>>>>>>> +                     &fme_mgr_ops, priv);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>     static struct platform_driver fme_mgr_driver = {
>>>>>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>>>>>>> index b85bc47c91a9..d6d64ce34417 100644
>>>>>>> --- a/drivers/fpga/fpga-mgr.c
>>>>>>> +++ b/drivers/fpga/fpga-mgr.c
>>>>>>> @@ -550,20 +550,22 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>>>>>>     /**
>>>>>>> - * fpga_mgr_create - create and initialize a FPGA manager struct
>>>>>>> + * fpga_mgr_register - create and register a FPGA Manager device
>>>>>>>    * @dev:    fpga manager device from pdev
>>>>>>>    * @name:    fpga manager name
>>>>>>> + * @compat_id:    FPGA manager id for compatibility check.
>>>>>> for consistency, use lowercase fpga
>>>>>>
>>>>>> similar issue below.
>>>>> OK
>>>>>
>>>>>>>    * @mops:    pointer to structure of fpga manager ops
>>>>>>>    * @priv:    fpga manager private data
>>>>>>>    *
>>>>>>> - * The caller of this function is responsible for freeing the struct with
>>>>>>> - * fpga_mgr_free().  Using devm_fpga_mgr_create() instead is recommended.
>>>>>>> + * The caller of this function is responsible for calling fpga_mgr_unregister().
>>>>>>> + * Using devm_fpga_mgr_register instead is recommended.
>>>>>>>    *
>>>>>>> - * Return: pointer to struct fpga_manager or NULL
>>>>>>> + * Return: struct fpga_manager pointer on success, ERR_PTR() on error.
>>>>>>>    */
>>>>>>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>>>> -                     const struct fpga_manager_ops *mops,
>>>>>>> -                     void *priv)
>>>>>>> +struct fpga_manager *
>>>>>>> +fpga_mgr_register(struct device *dev, const char *name,
>>>>>>> +          struct fpga_compat_id *compat_id,
>>>>>>> +          const struct fpga_manager_ops *mops, void *priv)
>>>>>>>   {
>>>>>>>       struct fpga_manager *mgr;
>>>>>>>       int id, ret;
>>>>>>> @@ -572,29 +574,31 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>>>>           !mops->write_init || (!mops->write && !mops->write_sg) ||
>>>>>>>           (mops->write && mops->write_sg)) {
>>>>>>>           dev_err(dev, "Attempt to register without fpga_manager_ops\n");
>>>>>>> -        return NULL;
>>>>>>> +        return ERR_PTR(-EINVAL);
>>>>>>>       }
>>>>>>>         if (!name || !strlen(name)) {
>>>>>>>           dev_err(dev, "Attempt to register with no name!\n");
>>>>>>> -        return NULL;
>>>>>>> +        return ERR_PTR(-EINVAL);
>>>>>>>       }
>>>>>>>         mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
>>>>>>>       if (!mgr)
>>>>>>> -        return NULL;
>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>         id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
>>>>>>> -    if (id < 0)
>>>>>>> +    if (id < 0) {
>>>>>>> +        ret = id;
>>>>>> ida_* returns a normal error, so needs a wrapping in
>>>>>>
>>>>>> ret = ERR_PTR(id)
>>>>> Thanks - I'll fix that
>>>> Actually, if you follow the "goto error_kfree", you will see that ret is
>>>> wrapped in ERR_PTR() in the return statement, so id does not need to be
>>>> wrapped here.
>>>>
>>>>>>>           goto error_kfree;
>>>>>>> +    }
>>>>>>>         mutex_init(&mgr->ref_mutex);
>>>>>>>         mgr->name = name;
>>>>>>> +    mgr->compat_id = compat_id;
>>>>>>>       mgr->mops = mops;
>>>>>>>       mgr->priv = priv;
>>>>>>>   -    device_initialize(&mgr->dev);
>>>>>>>       mgr->dev.class = fpga_mgr_class;
>>>>>>>       mgr->dev.groups = mops->groups;
>>>>>>>       mgr->dev.parent = dev;
>>>>>>> @@ -605,84 +609,6 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>>>>       if (ret)
>>>>>>>           goto error_device;
>>>>>>>   -    return mgr;
>>>>>>> -
>>>>>>> -error_device:
>>>>>>> -    ida_simple_remove(&fpga_mgr_ida, id);
>>>>>>> -error_kfree:
>>>>>>> -    kfree(mgr);
>>>>>>> -
>>>>>>> -    return NULL;
>>>>>>> -}
>>>>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>>>>>>> -
>>>>>>> -/**
>>>>>>> - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
>>>>>>> - * @mgr:    fpga manager struct
>>>>>>> - */
>>>>>>> -void fpga_mgr_free(struct fpga_manager *mgr)
>>>>>>> -{
>>>>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>>>> -    kfree(mgr);
>>>>>>> -}
>>>>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_free);
>>>>>>> -
>>>>>>> -static void devm_fpga_mgr_release(struct device *dev, void *res)
>>>>>>> -{
>>>>>>> -    struct fpga_mgr_devres *dr = res;
>>>>>>> -
>>>>>>> -    fpga_mgr_free(dr->mgr);
>>>>>>> -}
>>>>>>> -
>>>>>>> -/**
>>>>>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>>>>>>> - * @dev:    fpga manager device from pdev
>>>>>>> - * @name:    fpga manager name
>>>>>>> - * @mops:    pointer to structure of fpga manager ops
>>>>>>> - * @priv:    fpga manager private data
>>>>>>> - *
>>>>>>> - * This function is intended for use in a FPGA manager driver's probe function.
>>>>>>> - * After the manager driver creates the manager struct with
>>>>>>> - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
>>>>>>> - * manager driver's remove function should call fpga_mgr_unregister().  The
>>>>>>> - * manager 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_mgr_register(), the struct will still get cleaned up.
>>>>>>> - *
>>>>>>> - * Return: pointer to struct fpga_manager or NULL
>>>>>>> - */
>>>>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>>>>> -                      const struct fpga_manager_ops *mops,
>>>>>>> -                      void *priv)
>>>>>>> -{
>>>>>>> -    struct fpga_mgr_devres *dr;
>>>>>>> -
>>>>>>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>>>>>>> -    if (!dr)
>>>>>>> -        return NULL;
>>>>>>> -
>>>>>>> -    dr->mgr = fpga_mgr_create(dev, name, mops, priv);
>>>>>>> -    if (!dr->mgr) {
>>>>>>> -        devres_free(dr);
>>>>>>> -        return NULL;
>>>>>>> -    }
>>>>>>> -
>>>>>>> -    devres_add(dev, dr);
>>>>>>> -
>>>>>>> -    return dr->mgr;
>>>>>>> -}
>>>>>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>>>>>>> -
>>>>>>> -/**
>>>>>>> - * fpga_mgr_register - register a FPGA manager
>>>>>>> - * @mgr: fpga manager struct
>>>>>>> - *
>>>>>>> - * Return: 0 on success, negative error code otherwise.
>>>>>>> - */
>>>>>>> -int fpga_mgr_register(struct fpga_manager *mgr)
>>>>>>> -{
>>>>>>> -    int ret;
>>>>>>> -
>>>>>>>       /*
>>>>>>>        * Initialize framework state by requesting low level driver read state
>>>>>>>        * from device.  FPGA may be in reset mode or may have been programmed
>>>>>>> @@ -690,18 +616,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>>>>>>        */
>>>>>>>       mgr->state = mgr->mops->state(mgr);
>>>>>>>   -    ret = device_add(&mgr->dev);
>>>>>>> -    if (ret)
>>>>>>> -        goto error_device;
>>>>>>> -
>>>>>>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
>>>>>> can we keep this dev_info() line and move it below device_register() ?
>>>>> In Greg's response to my driver, he said he said that a well-behaved driver does
>>>>> not need dev_info() calls...
>>>>>>> +    ret = device_register(&mgr->dev);
>>>>>>> +    if (ret) {
>>>>>>> +        put_device(&mgr->dev);
>>>>>> pr_err() here ?
>>>>> OK - I can add a message
>>>>>
>>>>>>> +        return ERR_PTR(ret);
>>>>>>> +    }
>>>>>>>   -    return 0;
>>>>>>> +    return mgr;
>>>>>>>     error_device:
>>>>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>>>> +    ida_simple_remove(&fpga_mgr_ida, id);
>>>>>>> +error_kfree:
>>>>>>> +    kfree(mgr);
>>>>>>>   -    return ret;
>>>>>>> +    return ERR_PTR(ret);
>>>>>>>   }
>>>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>>>>>>   @@ -726,14 +654,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>>>>>>   }
>>>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>>>>>>   -static int fpga_mgr_devres_match(struct device *dev, void *res,
>>>>>>> -                 void *match_data)
>>>>>>> -{
>>>>>>> -    struct fpga_mgr_devres *dr = res;
>>>>>>> -
>>>>>>> -    return match_data == dr->mgr;
>>>>>>> -}
>>>>>>> -
>>>>>>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>>>>   {
>>>>>>>       struct fpga_mgr_devres *dr = res;
>>>>>>> @@ -743,44 +663,46 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>>>>     /**
>>>>>>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>>>>>>> - * @dev: managing device for this FPGA manager
>>>>>>> - * @mgr: fpga manager struct
>>>>>>> + * @dev:    fpga manager device from pdev
>>>>>>> + * @name:    fpga manager name
>>>>>>> + * @compat_id:    FPGA manager id for compatibility check.
>>>>>>> + * @mops:    pointer to structure of fpga manager ops
>>>>>>> + * @priv:    fpga manager private data
>>>>>>>    *
>>>>>>>    * This is the devres variant of fpga_mgr_register() for which the unregister
>>>>>>>    * function will be called automatically when the managing device is detached.
>>>>>>>    */
>>>>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>>>>>>> +struct fpga_manager *
>>>>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>>>>> +               struct fpga_compat_id *compat_id,
>>>>>>> +               const struct fpga_manager_ops *mops, void *priv)
>>>>>>>   {
>>>>>>>       struct fpga_mgr_devres *dr;
>>>>>>> -    int ret;
>>>>>>> -
>>>>>>> -    /*
>>>>>>> -     * Make sure that the struct fpga_manager * that is passed in is
>>>>>>> -     * managed itself.
>>>>>>> -     */
>>>>>>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>>>>>>> -                 fpga_mgr_devres_match, mgr)))
>>>>>>> -        return -EINVAL;
>>>>>>> +    struct fpga_manager *mgr;
>>>>>>>         dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>>>>>>       if (!dr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>   -    ret = fpga_mgr_register(mgr);
>>>>>>> -    if (ret) {
>>>>>>> +    mgr = fpga_mgr_register(dev, name, compat_id, mops, priv);
>>>>>>> +    if (IS_ERR(mgr)) {
>>>>>>>           devres_free(dr);
>>>>>>> -        return ret;
>>>>>>> +        return mgr;
>>>>>>>       }
>>>>>>>         dr->mgr = mgr;
>>>>>>>       devres_add(dev, dr);
>>>>>>>   -    return 0;
>>>>>>> +    return mgr;
>>>>>>>   }
>>>>>>>   EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
>>>>>>>     static void fpga_mgr_dev_release(struct device *dev)
>>>>>>>   {
>>>>>>> +    struct fpga_manager *mgr = to_fpga_manager(dev);
>>>>>>> +
>>>>>>> +    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>>>> +    kfree(mgr);
>>>>>>>   }
>>>>>>>     static int __init fpga_mgr_class_init(void)
>>>>>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>>>>>>> index 69dec5af23c3..b1c7c0713e11 100644
>>>>>>> --- a/drivers/fpga/ice40-spi.c
>>>>>>> +++ b/drivers/fpga/ice40-spi.c
>>>>>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>>>>>>           return ret;
>>>>>>>       }
>>>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>>>>>>> -                   &ice40_fpga_ops, priv);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>>>>>>> +                     NULL, &ice40_fpga_ops, priv);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>     static const struct of_device_id ice40_fpga_of_match[] = {
>>>>>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>>>>>>> index 114a64d2b7a4..75cc6efaa1b6 100644
>>>>>>> --- a/drivers/fpga/machxo2-spi.c
>>>>>>> +++ b/drivers/fpga/machxo2-spi.c
>>>>>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>>>>>>           return -EINVAL;
>>>>>>>       }
>>>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>>>>> -                   &machxo2_ops, spi);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>>>>> +                     NULL, &machxo2_ops, spi);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>     static const struct of_device_id of_match[] = {
>>>>>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>>>>>>> index 573d88bdf730..80b8891fb302 100644
>>>>>>> --- a/drivers/fpga/socfpga-a10.c
>>>>>>> +++ b/drivers/fpga/socfpga-a10.c
>>>>>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>>>>>>           return -EBUSY;
>>>>>>>       }
>>>>>>>   -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>>>>>>> -                   &socfpga_a10_fpga_mgr_ops, priv);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> -
>>>>>>> -    platform_set_drvdata(pdev, mgr);
>>>>>>> -
>>>>>>> -    ret = fpga_mgr_register(mgr);
>>>>>>> -    if (ret) {
>>>>>>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>>>>>>> +                NULL, &socfpga_a10_fpga_mgr_ops, priv);
>>>>>>> +    if (IS_ERR(mgr)) {
>>>>>>>           clk_disable_unprepare(priv->clk);
>>>>>>> -        return ret;
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>       }
>>>>>>>   +    platform_set_drvdata(pdev, mgr);
>>>>>>> +
>>>>>>>       return 0;
>>>>>>>   }
>>>>>>>   diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>>>>>>> index 1f467173fc1f..06edfab8a496 100644
>>>>>>> --- a/drivers/fpga/socfpga.c
>>>>>>> +++ b/drivers/fpga/socfpga.c
>>>>>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>>>>>>       if (ret)
>>>>>>>           return ret;
>>>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>>>>>>> -                   &socfpga_fpga_ops, priv);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>>>>>>> +                     NULL, &socfpga_fpga_ops, priv);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>     #ifdef CONFIG_OF
>>>>>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>>>>>>> index 657a70c5fc99..434094c3ac69 100644
>>>>>>> --- a/drivers/fpga/stratix10-soc.c
>>>>>>> +++ b/drivers/fpga/stratix10-soc.c
>>>>>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>>>>>>         init_completion(&priv->status_return_completion);
>>>>>>>   -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>>>>>>> -                  &s10_ops, priv);
>>>>>>> -    if (!mgr) {
>>>>>>> -        dev_err(dev, "unable to create FPGA manager\n");
>>>>>>> -        ret = -ENOMEM;
>>>>>>> -        goto probe_err;
>>>>>>> -    }
>>>>>>> -
>>>>>>> -    ret = fpga_mgr_register(mgr);
>>>>>>> -    if (ret) {
>>>>>>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>>>>>>> +                NULL, &s10_ops, priv);
>>>>>>> +    if (IS_ERR(mgr)) {
>>>>>>>           dev_err(dev, "unable to register FPGA manager\n");
>>>>>>> -        fpga_mgr_free(mgr);
>>>>>>> +        ret = PTR_ERR(mgr);
>>>>>>>           goto probe_err;
>>>>>>>       }
>>>>>>>   diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>>>>>>> index 101f016c6ed8..5efbec2b9f9f 100644
>>>>>>> --- a/drivers/fpga/ts73xx-fpga.c
>>>>>>> +++ b/drivers/fpga/ts73xx-fpga.c
>>>>>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>>>>>>       if (IS_ERR(priv->io_base))
>>>>>>>           return PTR_ERR(priv->io_base);
>>>>>>>   -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>>>>>>> -                   &ts73xx_fpga_ops, priv);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>>>>>> This is missing compat_id param.
>>>>> Thanks - I thought I'll do another pass and make sure I catch them all.
>>>>>
>>>>>> Similar for others.
>>>>>>
>>>>>>> +                     &ts73xx_fpga_ops, priv);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(kdev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>     static struct platform_driver ts73xx_fpga_driver = {
>>>>>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>>>>>>> index fee4d0abf6bf..6f1894929687 100644
>>>>>>> --- a/drivers/fpga/xilinx-spi.c
>>>>>>> +++ b/drivers/fpga/xilinx-spi.c
>>>>>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>>>>>>           return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>>>>>>                        "Failed to get DONE gpio\n");
>>>>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev,
>>>>>>> -                   "Xilinx Slave Serial FPGA Manager",
>>>>>>> -                   &xilinx_spi_ops, conf);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +    mgr = devm_fpga_mgr_register(&spi->dev,
>>>>>>> +                     "Xilinx Slave Serial FPGA Manager",
>>>>>>> +                     NULL, &xilinx_spi_ops, conf);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>     static const struct of_device_id xlnx_spi_of_match[] = {
>>>>>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>>>>>>> index 07fa8d9ec675..74bbb9710f4e 100644
>>>>>>> --- a/drivers/fpga/zynq-fpga.c
>>>>>>> +++ b/drivers/fpga/zynq-fpga.c
>>>>>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>>>>>>         clk_disable(priv->clk);
>>>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>>>>>>> -                   &zynq_fpga_ops, priv);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> -
>>>>>>> -    platform_set_drvdata(pdev, mgr);
>>>>>>> -
>>>>>>> -    err = fpga_mgr_register(mgr);
>>>>>>> -    if (err) {
>>>>>>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>>>>>> This is missing compat_id param as well.
>>>>>>> +                &zynq_fpga_ops, priv);
>>>>>>> +    if (IS_ERR(mgr)) {
>>>>>>>           dev_err(dev, "unable to register FPGA manager\n");
>>>>>>>           clk_unprepare(priv->clk);
>>>>>>> -        return err;
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>       }
>>>>>>>   +    platform_set_drvdata(pdev, mgr);
>>>>>>> +
>>>>>>>       return 0;
>>>>>>>   }
>>>>>>>   diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>>>>>>> index 125743c9797f..1a6d520985be 100644
>>>>>>> --- a/drivers/fpga/zynqmp-fpga.c
>>>>>>> +++ b/drivers/fpga/zynqmp-fpga.c
>>>>>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>>>>>>         priv->dev = dev;
>>>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>>>>>>> -                   &zynqmp_fpga_ops, priv);
>>>>>>> -    if (!mgr)
>>>>>>> -        return -ENOMEM;
>>>>>>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>>>>>>> +                     &zynqmp_fpga_ops, priv);
>>>>>>> +    if (IS_ERR(mgr))
>>>>>>> +        return PTR_ERR(mgr);
>>>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>>>> +    return 0;
>>>>>>>   }
>>>>>>>     static const struct of_device_id zynqmp_fpga_of_match[] = {
>>>>>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>>>>>>> index 2bc3030a69e5..3e1859cc0640 100644
>>>>>>> --- a/include/linux/fpga/fpga-mgr.h
>>>>>>> +++ b/include/linux/fpga/fpga-mgr.h
>>>>>>> @@ -191,17 +191,16 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>>>>>>     void fpga_mgr_put(struct fpga_manager *mgr);
>>>>>>>   -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>>>> -                     const struct fpga_manager_ops *mops,
>>>>>>> -                     void *priv);
>>>>>> My biggest concern is that removing this will break all oot fpga drivers.
>>>>>>
>>>>>> The other patches have similar api changes
>>>>>>
>>>>>> Having a stable api is very important.
>>>>> That is a good point. While Greg did call out the separation of the
>>>>> device_initialize() and device_add() calls, his biggest concern was with
>>>>> the empty dev_release() functions. Maybe it would be best to first submit
>>>>> a patch that only addresses the dev_release() functions? Maybe it would be
>>>>> OK to leave the create and register functions as separate functions in order
>>>>> to maintain a stable API...
>>>> On further investigation, I don't think we have much choice in changing
>>>> the API. We are almost coming full circle here. In 2018, there was only
>>>> an fpga_mgr_register() function. It was returning an errno value or zero.
>>>> It was also setting the drvdata of the parent driver with a pointer to
>>>> the resulting fpga_manager structure.
>>>>
>>>> It looks like the reason the interface was split into two (create and
>>>> register) was so that the fpga_manager pointer could be returned (by the
>>>> create function) without writing the drvdata of the parent driver:
>>>>
>>>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7085e2a94f7df5f419e3cfb2fe809ce6564e9629
>>>>
>>>> When the interfaces were split, fpga_mgr_free() was also created to free
>>>> the memory if the register function was not called. The fpga_mgr_free()
>>>> function was also called by the fpga_mgr_dev_release() function. A later
>>>> patch was submitted to simplify the memory management for the class driver:
>>>>
>>>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=084181fe8cc7472695b907f0018f4cd00934cb12
>>>>
>>>> It is this second commit that empties the fpga_mgr_dev_release() function
>>>> and instead uses devm_fpga_mgr_create() to manage the memory for the class
>>>> driver.
>>>>
>>>> The fix I am proposing essentially takes us back to the 2018 implementation,
>>>> but returns the fpga_manager pointer or ERR_PTR() instead of returning zero or
>>>> an errno.
>>>>
>>>> The definition of the API is integral to the memory management issue. I cannot
>>>> think of an acceptable solution for the upstream that doesn't involve
>>>> changing the API. It could result in some ifdef's in an out-of-tree driver,
>>>> but I don't think we can avoid it in this case.
>>> I agree the stable API is important, and I think it is still possible to fix
>>> the memory management issue while keeping the APIs.
>>>
>>> We should do the actual resource free work in class.dev_release(), and
>>> fpga_mgr_free should call put_device() to trigger it.
>>>
>>> And I see no problem with the current devm APIs.
>> Consider the case where both devm_fpga_mgr_create() and devm_fpga_mgr_register()
>> are called, and both succeed. When the driver is unloaded devm_fpga_mgr_release()
>> is called on behalf of the create and it will call put_device(). Then
>> devm_fpga_mgr_unregister() will be called on behalf of the register, and it
>> will call device_unregister(), which also calls put_device(). In this case, I
>> think there is an extra call to put_device(), right? Or is that not a problem?
> The real problem is the non-devm functions.
>
> fpga_mgr_create() calls device_initialize() so fpga_mgr_free() should call
> put_device() for deinitialization.
Yes - I understand that. The error case also needs to be fixed to call put_device()
on error.
>
> fpga_mgr_register() calls device_add(), so fpga_mgr_unregister() should
> call device_del().

OK - so your answer to my question is that fpga_mgr_unregister() should call
device_del() instead of device_unregister(). I can see how that will work.
I'll try to submit a new patchset today.

Thanks,

- Russ
>
> Thanks,
> Yilun
>
>> The only way I can think to avoid the extra put_device() call for this case
>> would be to set a flag when-and-if fpga_mgr_register() succeeds and then refuse
>> to call put_device() in devm_fpga_mgr_release() in order to avoid the duplicate
>> call to put_device(). But that feels like a hack to me.
>>
>> Am I missing something? Is there a cleaner way to make this work?
>>
>> - Russ
>>> Thanks,
>>> Yilun
>>>
>>>>> - Russ
>>>>>
>>>>>> My recommendation is to keep the public api and hide the change with an exported but not public function.
>>>>>>
>>>>>> Tom
>>>>>>
>>>>>>> -void fpga_mgr_free(struct fpga_manager *mgr);
>>>>>>> -int fpga_mgr_register(struct fpga_manager *mgr);
>>>>>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>>>>>>> +                       struct fpga_compat_id *compat_id,
>>>>>>> +                       const struct fpga_manager_ops *mops,
>>>>>>> +                       void *priv);
>>>>>>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
>>>>>>>   -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>>>>>>> -
>>>>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>>>>> -                      const struct fpga_manager_ops *mops,
>>>>>>> -                      void *priv);
>>>>>>> +struct fpga_manager *
>>>>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>>>>> +               struct fpga_compat_id *compat_id,
>>>>>>> +               const struct fpga_manager_ops *mops,
>>>>>>> +               void *priv);
>>>>>>>     #endif /*_LINUX_FPGA_MGR_H */


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2021-06-08 16:44 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-28 21:57 [PATCH v4 0/3] fpga: Use standard class dev_release function Russ Weight
2021-05-28 21:57 ` [PATCH v4 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
2021-05-31  8:54   ` Xu Yilun
2021-06-03 21:21     ` Russ Weight
2021-06-01 23:24   ` Tom Rix
2021-06-02  0:38     ` Russ Weight
2021-06-03 22:45       ` Russ Weight
2021-06-04 20:56         ` Tom Rix
2021-06-04 21:17           ` Russ Weight
2021-06-05 15:42         ` Xu Yilun
2021-06-07 23:50           ` Russ Weight
2021-06-08  1:40             ` Xu Yilun
2021-06-08 16:44               ` Russ Weight
2021-05-28 21:57 ` [PATCH v4 2/3] fpga: bridge: " Russ Weight
2021-05-28 21:57 ` [PATCH v4 3/3] fpga: region: " Russ Weight
2021-05-31 15:23   ` Xu Yilun
2021-06-03 22:55     ` Russ Weight
2021-05-31 15:24 ` [PATCH v4 0/3] fpga: Use standard class dev_release function Xu Yilun
2021-06-03 22:49   ` Russ Weight

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).