Skip to content

Commit

Permalink
spi/pxa2xx pci: fix the release - remove race
Browse files Browse the repository at this point in the history
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: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
  • Loading branch information
Sebastian Andrzej Siewior authored and Grant Likely committed Feb 15, 2011
1 parent c170093 commit 0f3e1d2
Showing 1 changed file with 21 additions and 40 deletions.
61 changes: 21 additions & 40 deletions drivers/spi/pxa2xx_spi_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
#include <linux/of_device.h>
#include <linux/spi/pxa2xx_spi.h>

struct awesome_struct {
struct ce4100_info {
struct ssp_device ssp;
struct platform_device spi_pdev;
struct pxa2xx_spi_master spi_pdata;
struct platform_device *spi_pdev;
};

static DEFINE_MUTEX(ssp_lock);
Expand Down Expand Up @@ -51,23 +50,15 @@ void pxa_ssp_free(struct ssp_device *ssp)
}
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,
const struct pci_device_id *ent)
{
int ret;
resource_size_t phys_beg;
resource_size_t phys_len;
struct awesome_struct *spi_info;
struct ce4100_info *spi_info;
struct platform_device *pdev;
struct pxa2xx_spi_master *spi_pdata;
struct pxa2xx_spi_master spi_pdata;
struct ssp_device *ssp;

ret = pci_enable_device(dev);
Expand All @@ -84,33 +75,30 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev,
return ret;
}

pdev = platform_device_alloc("pxa2xx-spi", dev->devfn);
spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL);
if (!spi_info) {
if (!pdev || !spi_info ) {
ret = -ENOMEM;
goto err_kz;
goto err_nomem;
}
ssp = &spi_info->ssp;
pdev = &spi_info->spi_pdev;
spi_pdata = &spi_info->spi_pdata;
memset(&spi_pdata, 0, sizeof(spi_pdata));
spi_pdata.num_chipselect = dev->devfn;

pdev->name = "pxa2xx-spi";
pdev->id = dev->devfn;
pdev->dev.parent = &dev->dev;
pdev->dev.platform_data = &spi_info->spi_pdata;
ret = platform_device_add_data(pdev, &spi_pdata, sizeof(spi_pdata));
if (ret)
goto err_nomem;

pdev->dev.parent = &dev->dev;
#ifdef CONFIG_OF
pdev->dev.of_node = dev->dev.of_node;
#endif
pdev->dev.release = plat_dev_release;

spi_pdata->num_chipselect = dev->devfn;

ssp = &spi_info->ssp;
ssp->phys_base = pci_resource_start(dev, 0);
ssp->mmio_base = ioremap(phys_beg, phys_len);
if (!ssp->mmio_base) {
dev_err(&pdev->dev, "failed to ioremap() registers\n");
ret = -EIO;
goto err_remap;
goto err_nomem;
}
ssp->irq = dev->irq;
ssp->port_id = pdev->id;
Expand All @@ -122,7 +110,7 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev,

pci_set_drvdata(dev, spi_info);

ret = platform_device_register(pdev);
ret = platform_device_add(pdev);
if (ret)
goto err_dev_add;

Expand All @@ -135,27 +123,21 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev,
mutex_unlock(&ssp_lock);
iounmap(ssp->mmio_base);

err_remap:
kfree(spi_info);

err_kz:
err_nomem:
release_mem_region(phys_beg, phys_len);

platform_device_put(pdev);
kfree(spi_info);
return ret;
}

static void __devexit ce4100_spi_remove(struct pci_dev *dev)
{
struct awesome_struct *spi_info;
struct platform_device *pdev;
struct ce4100_info *spi_info;
struct ssp_device *ssp;

spi_info = pci_get_drvdata(dev);

ssp = &spi_info->ssp;
pdev = &spi_info->spi_pdev;

platform_device_unregister(pdev);
platform_device_unregister(spi_info->spi_pdev);

iounmap(ssp->mmio_base);
release_mem_region(pci_resource_start(dev, 0),
Expand All @@ -171,7 +153,6 @@ static void __devexit ce4100_spi_remove(struct pci_dev *dev)
}

static struct pci_device_id ce4100_spi_devices[] __devinitdata = {

{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) },
{ },
};
Expand Down

0 comments on commit 0f3e1d2

Please sign in to comment.