Commit 0f3e1d27 authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Grant Likely

spi/pxa2xx pci: fix the release - remove race

Right now the platform device and its platform data is included in one big
struct which requires its custom ->release function. The problem with the
release function within the driver is that it might be called after the
driver was removed because someone was holding a reference to it and it
was not called right after platform_device_unregister(). So we also free
the platform device memory to which one might hold a reference.

This patch uses the normal pdev functions so this kind of race does not
occur.
Signed-off-by: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarGrant Likely <grant.likely@secretlab.ca>
parent c170093d
...@@ -7,10 +7,9 @@ ...@@ -7,10 +7,9 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/spi/pxa2xx_spi.h> #include <linux/spi/pxa2xx_spi.h>
struct awesome_struct { struct ce4100_info {
struct ssp_device ssp; struct ssp_device ssp;
struct platform_device spi_pdev; struct platform_device *spi_pdev;
struct pxa2xx_spi_master spi_pdata;
}; };
static DEFINE_MUTEX(ssp_lock); static DEFINE_MUTEX(ssp_lock);
...@@ -51,23 +50,15 @@ void pxa_ssp_free(struct ssp_device *ssp) ...@@ -51,23 +50,15 @@ void pxa_ssp_free(struct ssp_device *ssp)
} }
EXPORT_SYMBOL_GPL(pxa_ssp_free); EXPORT_SYMBOL_GPL(pxa_ssp_free);
static void plat_dev_release(struct device *dev)
{
struct awesome_struct *as = container_of(dev,
struct awesome_struct, spi_pdev.dev);
of_device_node_put(&as->spi_pdev.dev);
}
static int __devinit ce4100_spi_probe(struct pci_dev *dev, static int __devinit ce4100_spi_probe(struct pci_dev *dev,
const struct pci_device_id *ent) const struct pci_device_id *ent)
{ {
int ret; int ret;
resource_size_t phys_beg; resource_size_t phys_beg;
resource_size_t phys_len; resource_size_t phys_len;
struct awesome_struct *spi_info; struct ce4100_info *spi_info;
struct platform_device *pdev; struct platform_device *pdev;
struct pxa2xx_spi_master *spi_pdata; struct pxa2xx_spi_master spi_pdata;
struct ssp_device *ssp; struct ssp_device *ssp;
ret = pci_enable_device(dev); ret = pci_enable_device(dev);
...@@ -84,33 +75,30 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev, ...@@ -84,33 +75,30 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev,
return ret; return ret;
} }
pdev = platform_device_alloc("pxa2xx-spi", dev->devfn);
spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL); spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL);
if (!spi_info) { if (!pdev || !spi_info ) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_kz; goto err_nomem;
} }
ssp = &spi_info->ssp; memset(&spi_pdata, 0, sizeof(spi_pdata));
pdev = &spi_info->spi_pdev; spi_pdata.num_chipselect = dev->devfn;
spi_pdata = &spi_info->spi_pdata;
pdev->name = "pxa2xx-spi"; ret = platform_device_add_data(pdev, &spi_pdata, sizeof(spi_pdata));
pdev->id = dev->devfn; if (ret)
pdev->dev.parent = &dev->dev; goto err_nomem;
pdev->dev.platform_data = &spi_info->spi_pdata;
pdev->dev.parent = &dev->dev;
#ifdef CONFIG_OF #ifdef CONFIG_OF
pdev->dev.of_node = dev->dev.of_node; pdev->dev.of_node = dev->dev.of_node;
#endif #endif
pdev->dev.release = plat_dev_release; ssp = &spi_info->ssp;
spi_pdata->num_chipselect = dev->devfn;
ssp->phys_base = pci_resource_start(dev, 0); ssp->phys_base = pci_resource_start(dev, 0);
ssp->mmio_base = ioremap(phys_beg, phys_len); ssp->mmio_base = ioremap(phys_beg, phys_len);
if (!ssp->mmio_base) { if (!ssp->mmio_base) {
dev_err(&pdev->dev, "failed to ioremap() registers\n"); dev_err(&pdev->dev, "failed to ioremap() registers\n");
ret = -EIO; ret = -EIO;
goto err_remap; goto err_nomem;
} }
ssp->irq = dev->irq; ssp->irq = dev->irq;
ssp->port_id = pdev->id; ssp->port_id = pdev->id;
...@@ -122,7 +110,7 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev, ...@@ -122,7 +110,7 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev,
pci_set_drvdata(dev, spi_info); pci_set_drvdata(dev, spi_info);
ret = platform_device_register(pdev); ret = platform_device_add(pdev);
if (ret) if (ret)
goto err_dev_add; goto err_dev_add;
...@@ -135,27 +123,21 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev, ...@@ -135,27 +123,21 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev,
mutex_unlock(&ssp_lock); mutex_unlock(&ssp_lock);
iounmap(ssp->mmio_base); iounmap(ssp->mmio_base);
err_remap: err_nomem:
kfree(spi_info);
err_kz:
release_mem_region(phys_beg, phys_len); release_mem_region(phys_beg, phys_len);
platform_device_put(pdev);
kfree(spi_info);
return ret; return ret;
} }
static void __devexit ce4100_spi_remove(struct pci_dev *dev) static void __devexit ce4100_spi_remove(struct pci_dev *dev)
{ {
struct awesome_struct *spi_info; struct ce4100_info *spi_info;
struct platform_device *pdev;
struct ssp_device *ssp; struct ssp_device *ssp;
spi_info = pci_get_drvdata(dev); spi_info = pci_get_drvdata(dev);
ssp = &spi_info->ssp; ssp = &spi_info->ssp;
pdev = &spi_info->spi_pdev; platform_device_unregister(spi_info->spi_pdev);
platform_device_unregister(pdev);
iounmap(ssp->mmio_base); iounmap(ssp->mmio_base);
release_mem_region(pci_resource_start(dev, 0), release_mem_region(pci_resource_start(dev, 0),
...@@ -171,7 +153,6 @@ static void __devexit ce4100_spi_remove(struct pci_dev *dev) ...@@ -171,7 +153,6 @@ static void __devexit ce4100_spi_remove(struct pci_dev *dev)
} }
static struct pci_device_id ce4100_spi_devices[] __devinitdata = { static struct pci_device_id ce4100_spi_devices[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) },
{ }, { },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment