Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88f4ea62 authored by Xiongfeng Wang's avatar Xiongfeng Wang Committed by Greg Kroah-Hartman
Browse files

hwrng: geode - Fix PCI device refcount leak



[ Upstream commit 9f6ec8dc574efb7f4f3d7ee9cd59ae307e78f445 ]

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: ef5d8627 ("[PATCH] Add Geode HW RNG driver")
Signed-off-by: default avatarXiongfeng Wang <wangxiongfeng2@huawei.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent f1c97f72
Loading
Loading
Loading
Loading
+28 −8
Original line number Diff line number Diff line
@@ -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 mod_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 mod_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 @@ static int __init mod_init(void)
		       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 mod_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(mod_init);