Skip to content

Commit

Permalink
firmware: raspberrypi: Keep count of all consumers
Browse files Browse the repository at this point in the history
When unbinding the firmware device we need to make sure it has no
consumers left. Otherwise we'd leave them with a firmware handle
pointing at freed memory.

Keep a reference count of all consumers and introduce rpi_firmware_put()
which will permit automatically decrease the reference count upon
unbinding consumer drivers.

Suggested-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
Reviewed-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
  • Loading branch information
Nicolas Saenz Julienne committed Mar 22, 2021
1 parent a38fd87 commit 1e7c573
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 3 deletions.
40 changes: 37 additions & 3 deletions drivers/firmware/raspberrypi.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

#include <linux/dma-mapping.h>
#include <linux/kref.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of_platform.h>
Expand All @@ -27,6 +28,8 @@ struct rpi_firmware {
struct mbox_chan *chan; /* The property channel. */
struct completion c;
u32 enabled;

struct kref consumers;
};

static DEFINE_MUTEX(transaction_lock);
Expand Down Expand Up @@ -225,12 +228,31 @@ static void rpi_register_clk_driver(struct device *dev)
-1, NULL, 0);
}

static void rpi_firmware_delete(struct kref *kref)
{
struct rpi_firmware *fw = container_of(kref, struct rpi_firmware,
consumers);

mbox_free_channel(fw->chan);
kfree(fw);
}

void rpi_firmware_put(struct rpi_firmware *fw)
{
kref_put(&fw->consumers, rpi_firmware_delete);
}
EXPORT_SYMBOL_GPL(rpi_firmware_put);

static int rpi_firmware_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rpi_firmware *fw;

fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL);
/*
* Memory will be freed by rpi_firmware_delete() once all users have
* released their firmware handles. Don't use devm_kzalloc() here.
*/
fw = kzalloc(sizeof(*fw), GFP_KERNEL);
if (!fw)
return -ENOMEM;

Expand All @@ -247,6 +269,7 @@ static int rpi_firmware_probe(struct platform_device *pdev)
}

init_completion(&fw->c);
kref_init(&fw->consumers);

platform_set_drvdata(pdev, fw);

Expand Down Expand Up @@ -275,7 +298,8 @@ static int rpi_firmware_remove(struct platform_device *pdev)
rpi_hwmon = NULL;
platform_device_unregister(rpi_clk);
rpi_clk = NULL;
mbox_free_channel(fw->chan);

rpi_firmware_put(fw);

return 0;
}
Expand All @@ -284,16 +308,26 @@ static int rpi_firmware_remove(struct platform_device *pdev)
* rpi_firmware_get - Get pointer to rpi_firmware structure.
* @firmware_node: Pointer to the firmware Device Tree node.
*
* The reference to rpi_firmware has to be released with rpi_firmware_put().
*
* Returns NULL is the firmware device is not ready.
*/
struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node)
{
struct platform_device *pdev = of_find_device_by_node(firmware_node);
struct rpi_firmware *fw;

if (!pdev)
return NULL;

return platform_get_drvdata(pdev);
fw = platform_get_drvdata(pdev);
if (!fw)
return NULL;

if (!kref_get_unless_zero(&fw->consumers))
return NULL;

return fw;
}
EXPORT_SYMBOL_GPL(rpi_firmware_get);

Expand Down
2 changes: 2 additions & 0 deletions include/soc/bcm2835/raspberrypi-firmware.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ int rpi_firmware_property(struct rpi_firmware *fw,
u32 tag, void *data, size_t len);
int rpi_firmware_property_list(struct rpi_firmware *fw,
void *data, size_t tag_size);
void rpi_firmware_put(struct rpi_firmware *fw);
struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node);
#else
static inline int rpi_firmware_property(struct rpi_firmware *fw, u32 tag,
Expand All @@ -154,6 +155,7 @@ static inline int rpi_firmware_property_list(struct rpi_firmware *fw,
return -ENOSYS;
}

static inline void rpi_firmware_put(struct rpi_firmware *fw) { }
static inline struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node)
{
return NULL;
Expand Down

0 comments on commit 1e7c573

Please sign in to comment.