[PATCH can-next 2/2] net: m_can: Fix freeing of can device from peripherials

From: Dan Murphy
Date: Thu Feb 27 2020 - 13:43:55 EST


Fix leaking netdev device from peripherial devices. The call to
allocate the netdev device is made from and managed by the peripherial.

Create a common function that peripherials can call to free the netdev
device when failures occur.

Fixes: d42f4e1d06d9 ("can: m_can: Create a m_can platform framework")
Reported-by: Marc Kleine-Budde <mkl@xxxxxxxxxxxxxx>
Signed-off-by: Dan Murphy <dmurphy@xxxxxx>
---
drivers/net/can/m_can/m_can.c | 9 ++++---
drivers/net/can/m_can/m_can.h | 2 ++
drivers/net/can/m_can/m_can_platform.c | 21 ++++++++++------
drivers/net/can/m_can/tcan4x5x.c | 35 ++++++++++++++++----------
4 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 5794be1ef3ef..b1eca4aa59f8 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -1796,6 +1796,12 @@ struct m_can_classdev *m_can_class_allocate_dev(struct device *dev)
}
EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);

+void m_can_class_free_dev(struct net_device *net)
+{
+ free_candev(net);
+}
+EXPORT_SYMBOL_GPL(m_can_class_free_dev);
+
int m_can_class_register(struct m_can_classdev *m_can_dev)
{
int ret;
@@ -1834,7 +1840,6 @@ int m_can_class_register(struct m_can_classdev *m_can_dev)
if (ret) {
if (m_can_dev->pm_clock_support)
pm_runtime_disable(m_can_dev->dev);
- free_candev(m_can_dev->net);
}

return ret;
@@ -1892,8 +1897,6 @@ void m_can_class_unregister(struct m_can_classdev *m_can_dev)
unregister_candev(m_can_dev->net);

m_can_clk_stop(m_can_dev);
-
- free_candev(m_can_dev->net);
}
EXPORT_SYMBOL_GPL(m_can_class_unregister);

diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index c20a716b14cc..fa6dba3a893c 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -97,6 +97,7 @@ struct m_can_classdev {
};

struct m_can_classdev *m_can_class_allocate_dev(struct device *dev);
+void m_can_class_free_dev(struct net_device *net);
int m_can_class_register(struct m_can_classdev *cdev);
void m_can_class_unregister(struct m_can_classdev *cdev);
void m_can_init_ram(struct m_can_classdev *priv);
@@ -104,4 +105,5 @@ void m_can_config_endisable(struct m_can_classdev *priv, bool enable);

int m_can_class_suspend(struct device *dev);
int m_can_class_resume(struct device *dev);
+
#endif /* _CAN_M_H_ */
diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
index 8bd459317eba..275f87931529 100644
--- a/drivers/net/can/m_can/m_can_platform.c
+++ b/drivers/net/can/m_can/m_can_platform.c
@@ -86,34 +86,36 @@ static int m_can_plat_probe(struct platform_device *pdev)
return -ENOMEM;

priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
+ if (!priv) {
+ ret = -ENOMEM;
+ goto probe_fail;
+ }

mcan_class->device_data = priv;

ret = m_can_plat_get_clocks(priv, mcan_class);
if (ret)
- return ret;
+ goto probe_fail;

res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
addr = devm_ioremap_resource(&pdev->dev, res);
irq = platform_get_irq_byname(pdev, "int0");
if (IS_ERR(addr) || irq < 0) {
ret = -EINVAL;
- goto failed_ret;
+ goto probe_fail;
}

/* message ram could be shared */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
if (!res) {
ret = -ENODEV;
- goto failed_ret;
+ goto probe_fail;
}

mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!mram_addr) {
ret = -ENOMEM;
- goto failed_ret;
+ goto probe_fail;
}

priv->base = addr;
@@ -132,9 +134,10 @@ static int m_can_plat_probe(struct platform_device *pdev)

m_can_init_ram(mcan_class);

- ret = m_can_class_register(mcan_class);
+ return m_can_class_register(mcan_class);

-failed_ret:
+probe_fail:
+ m_can_class_free_dev(mcan_class->net);
return ret;
}

@@ -155,6 +158,8 @@ static int m_can_plat_remove(struct platform_device *pdev)

m_can_class_unregister(mcan_class);

+ m_can_class_free_dev(mcan_class->net);
+
platform_set_drvdata(pdev, NULL);

return 0;
diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c
index 37d53ecc560b..0b6acc3caf25 100644
--- a/drivers/net/can/m_can/tcan4x5x.c
+++ b/drivers/net/can/m_can/tcan4x5x.c
@@ -463,20 +463,26 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
return -ENOMEM;

priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
+ if (!priv) {
+ ret = -ENOMEM;
+ goto probe_fail;
+ }

priv->power = devm_regulator_get_optional(&spi->dev, "vsup");
- if (PTR_ERR(priv->power) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- else
+ if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto probe_fail;
+ } else {
priv->power = NULL;
+ }

mcan_class->device_data = priv;

freq = tcan4x5x_get_clock(priv, mcan_class);
- if (freq < 0)
- return freq;
+ if (freq < 0) {
+ ret = -EINVAL;
+ goto probe_fail;
+ }

priv->reg_offset = TCAN4X5X_MCAN_OFFSET;
priv->mram_start = TCAN4X5X_MRAM_START;
@@ -498,32 +504,33 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
spi->bits_per_word = 32;
ret = spi_setup(spi);
if (ret)
- return ret;
+ goto probe_fail;

priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
&spi->dev, &tcan4x5x_regmap);

ret = tcan4x5x_power_enable(priv->power, 1);
if (ret)
- return ret;
+ goto probe_fail;

ret = tcan4x5x_get_gpios(mcan_class);
if (ret)
- goto out_power;
+ goto probe_fail;

ret = tcan4x5x_init(mcan_class);
if (ret)
- goto out_power;
+ goto probe_fail;

ret = m_can_class_register(mcan_class);
if (ret)
- goto out_power;
+ goto probe_fail;

netdev_info(mcan_class->net, "TCAN4X5X successfully initialized.\n");
return 0;

-out_power:
+probe_fail:
tcan4x5x_power_enable(priv->power, 0);
+ m_can_class_free_dev(mcan_class->net);
dev_err(&spi->dev, "Probe failed, err=%d\n", ret);
return ret;
}
@@ -536,6 +543,8 @@ static int tcan4x5x_can_remove(struct spi_device *spi)

m_can_class_unregister(priv->mcan_dev);

+ m_can_class_free_dev(priv->mcan_dev->net);
+
return 0;
}

--
2.25.0