Skip to content

Commit

Permalink
firmware: raspberrypi: Introduce rpi_firmware_put()
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 make sure they all finished
unbinding before we do.

Suggested-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
  • Loading branch information
Nicolas Saenz Julienne authored and intel-lab-lkp committed Oct 22, 2020
1 parent 37b992c commit 7a5de55
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 1 deletion.
30 changes: 29 additions & 1 deletion drivers/firmware/raspberrypi.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <soc/bcm2835/raspberrypi-firmware.h>

#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf))
Expand All @@ -27,6 +29,9 @@ struct rpi_firmware {
struct mbox_chan *chan; /* The property channel. */
struct completion c;
u32 enabled;

refcount_t consumers;
wait_queue_head_t wait;
};

static DEFINE_MUTEX(transaction_lock);
Expand Down Expand Up @@ -247,6 +252,8 @@ static int rpi_firmware_probe(struct platform_device *pdev)
}

init_completion(&fw->c);
refcount_set(&fw->consumers, 1);
init_waitqueue_head(&fw->wait);

platform_set_drvdata(pdev, fw);

Expand Down Expand Up @@ -275,6 +282,8 @@ static int rpi_firmware_remove(struct platform_device *pdev)
rpi_hwmon = NULL;
platform_device_unregister(rpi_clk);
rpi_clk = NULL;

wait_event(fw->wait, refcount_dec_if_one(&fw->consumers));
mbox_free_channel(fw->chan);

return 0;
Expand All @@ -289,14 +298,33 @@ static int rpi_firmware_remove(struct platform_device *pdev)
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 (!refcount_inc_not_zero(&fw->consumers))
return NULL;

return fw;
}
EXPORT_SYMBOL_GPL(rpi_firmware_get);

/**
* rpi_firmware_put - Put pointer to rpi_firmware structure.
* @rpi_firmware: Pointer to struct rpi_firmware
*/
void rpi_firmware_put(struct rpi_firmware *fw)
{
refcount_dec(&fw->consumers);
wake_up(&fw->wait);
}
EXPORT_SYMBOL_GPL(rpi_firmware_put);

static const struct of_device_id rpi_firmware_of_match[] = {
{ .compatible = "raspberrypi,bcm2835-firmware", },
{},
Expand Down
3 changes: 3 additions & 0 deletions include/soc/bcm2835/raspberrypi-firmware.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ int rpi_firmware_property(struct rpi_firmware *fw,
int rpi_firmware_property_list(struct rpi_firmware *fw,
void *data, size_t tag_size);
struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node);
void rpi_firmware_put(struct rpi_firmware *fw);
#else
static inline int rpi_firmware_property(struct rpi_firmware *fw, u32 tag,
void *data, size_t len)
Expand All @@ -158,6 +159,8 @@ static inline struct rpi_firmware *rpi_firmware_get(struct device_node *firmware
{
return NULL;
}

void rpi_firmware_put(struct rpi_firmware *fw) { }
#endif

#endif /* __SOC_RASPBERRY_FIRMWARE_H__ */

0 comments on commit 7a5de55

Please sign in to comment.