hwrng: geode - Fix PCI device refcount leak

[ Upstream commit 9f6ec8dc57 ]

for_each_pci_dev() is implemented by pci_get_device(). The comment of
pci_get_device() says that it will increase the reference count for the
returned pci_dev and also decrease the reference count for the input
pci_dev @from if it is not NULL.

If we break for_each_pci_dev() loop with pdev not NULL, we need to call
pci_dev_put() to decrease the reference count. We add a new struct
'amd_geode_priv' to record pointer of the pci_dev and membase, and then
add missing pci_dev_put() for the normal and error path.

Fixes: ef5d862734 ("[PATCH] Add Geode HW RNG driver")
Signed-off-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Xiongfeng Wang 2022-12-02 21:22:34 +08:00 committed by Greg Kroah-Hartman
parent 2b79a5e560
commit 82bd423ed9

View File

@ -51,6 +51,10 @@ static const struct pci_device_id pci_tbl[] = {
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
struct amd_geode_priv {
struct pci_dev *pcidev;
void __iomem *membase;
};
static int geode_rng_data_read(struct hwrng *rng, u32 *data)
{
@ -90,6 +94,7 @@ static int __init geode_rng_init(void)
const struct pci_device_id *ent;
void __iomem *mem;
unsigned long rng_base;
struct amd_geode_priv *priv;
for_each_pci_dev(pdev) {
ent = pci_match_id(pci_tbl, pdev);
@ -97,17 +102,26 @@ static int __init geode_rng_init(void)
goto found;
}
/* Device not found. */
goto out;
return err;
found:
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
err = -ENOMEM;
goto put_dev;
}
rng_base = pci_resource_start(pdev, 0);
if (rng_base == 0)
goto out;
goto free_priv;
err = -ENOMEM;
mem = ioremap(rng_base, 0x58);
if (!mem)
goto out;
geode_rng.priv = (unsigned long)mem;
goto free_priv;
geode_rng.priv = (unsigned long)priv;
priv->membase = mem;
priv->pcidev = pdev;
pr_info("AMD Geode RNG detected\n");
err = hwrng_register(&geode_rng);
@ -116,20 +130,26 @@ found:
err);
goto err_unmap;
}
out:
return err;
err_unmap:
iounmap(mem);
goto out;
free_priv:
kfree(priv);
put_dev:
pci_dev_put(pdev);
return err;
}
static void __exit geode_rng_exit(void)
{
void __iomem *mem = (void __iomem *)geode_rng.priv;
struct amd_geode_priv *priv;
priv = (struct amd_geode_priv *)geode_rng.priv;
hwrng_unregister(&geode_rng);
iounmap(mem);
iounmap(priv->membase);
pci_dev_put(priv->pcidev);
kfree(priv);
}
module_init(geode_rng_init);