summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Battersby <tonyb@cybernetics.com>2009-01-08 18:55:52 +0100
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-03-12 18:58:16 +0100
commita71d035de835caa7d14ef69928e0fde9fc241cc0 (patch)
treeeb44f1a045b1d7d8b762c19520437ce9e3a12179
parent[SCSI] sym53c8xx: fix bogus free_irq() on error path (diff)
downloadlinux-a71d035de835caa7d14ef69928e0fde9fc241cc0.tar.xz
linux-a71d035de835caa7d14ef69928e0fde9fc241cc0.zip
[SCSI] sym53c8xx: unmap pci memory after probe errors
During sym2_probe(), sym_init_device() does pci_iomap(), but there is no corresponding pci_iounmap() if an error occurs before sym_attach() copies sym_device::s.{ioaddr,ramaddr} to np. 1) Add the sym_iounmap_device() function. 2) Call sym_iounmap_device() if an error occurs between sym_init_device() and the time sym_attach() allocates np. 3) Make sym_attach() copy sym_device::s.{ioaddr,ramaddr} to np before calling any function that can fail so that sym_free_resources() will do the unmap instead of sym_iounmap_device(). Also fixed by this patch: During sym2_probe(), if sym_check_raid() returns nonzero, then pci_release_regions() is never called. Signed-off-by: Tony Battersby <tonyb@cybernetics.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c66
1 files changed, 41 insertions, 25 deletions
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
index a8ac60caadc0..8e69b5c35f58 100644
--- a/drivers/scsi/sym53c8xx_2/sym_glue.c
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -1236,6 +1236,20 @@ static int sym53c8xx_proc_info(struct Scsi_Host *shost, char *buffer,
#endif /* SYM_LINUX_PROC_INFO_SUPPORT */
/*
+ * Free resources claimed by sym_init_device(). Note that
+ * sym_free_resources() should be used instead of this function after calling
+ * sym_attach().
+ */
+static void __devinit
+sym_iounmap_device(struct sym_device *device)
+{
+ if (device->s.ioaddr)
+ pci_iounmap(device->pdev, device->s.ioaddr);
+ if (device->s.ramaddr)
+ pci_iounmap(device->pdev, device->s.ramaddr);
+}
+
+/*
* Free controller resources.
*/
static void sym_free_resources(struct sym_hcb *np, struct pci_dev *pdev,
@@ -1272,7 +1286,7 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
{
struct sym_data *sym_data;
struct sym_hcb *np = NULL;
- struct Scsi_Host *shost;
+ struct Scsi_Host *shost = NULL;
struct pci_dev *pdev = dev->pdev;
unsigned long flags;
struct sym_fw *fw;
@@ -1287,11 +1301,11 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
*/
fw = sym_find_firmware(&dev->chip);
if (!fw)
- return NULL;
+ goto attach_failed;
shost = scsi_host_alloc(tpnt, sizeof(*sym_data));
if (!shost)
- return NULL;
+ goto attach_failed;
sym_data = shost_priv(shost);
/*
@@ -1321,6 +1335,13 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
np->maxoffs = dev->chip.offset_max;
np->maxburst = dev->chip.burst_max;
np->myaddr = dev->host_id;
+ np->mmio_ba = (u32)dev->mmio_base;
+ np->s.ioaddr = dev->s.ioaddr;
+ np->s.ramaddr = dev->s.ramaddr;
+ if (!(np->features & FE_RAM))
+ dev->ram_base = 0;
+ if (dev->ram_base)
+ np->ram_ba = (u32)dev->ram_base;
/*
* Edit its name.
@@ -1336,22 +1357,6 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
goto attach_failed;
}
- /*
- * Try to map the controller chip to
- * virtual and physical memory.
- */
- np->mmio_ba = (u32)dev->mmio_base;
- np->s.ioaddr = dev->s.ioaddr;
- np->s.ramaddr = dev->s.ramaddr;
-
- /*
- * Map on-chip RAM if present and supported.
- */
- if (!(np->features & FE_RAM))
- dev->ram_base = 0;
- if (dev->ram_base)
- np->ram_ba = (u32)dev->ram_base;
-
if (sym_hcb_attach(shost, fw, dev->nvram))
goto attach_failed;
@@ -1419,12 +1424,13 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
"TERMINATION, DEVICE POWER etc.!\n", sym_name(np));
spin_unlock_irqrestore(shost->host_lock, flags);
attach_failed:
- if (!shost)
- return NULL;
printf_info("sym%d: giving up ...\n", unit);
if (np)
sym_free_resources(np, pdev, do_free_irq);
- scsi_host_put(shost);
+ else
+ sym_iounmap_device(dev);
+ if (shost)
+ scsi_host_put(shost);
return NULL;
}
@@ -1700,6 +1706,8 @@ static int __devinit sym2_probe(struct pci_dev *pdev,
struct sym_device sym_dev;
struct sym_nvram nvram;
struct Scsi_Host *shost;
+ int do_iounmap = 0;
+ int do_disable_device = 1;
memset(&sym_dev, 0, sizeof(sym_dev));
memset(&nvram, 0, sizeof(nvram));
@@ -1713,11 +1721,15 @@ static int __devinit sym2_probe(struct pci_dev *pdev,
goto disable;
sym_init_device(pdev, &sym_dev);
+ do_iounmap = 1;
+
if (sym_check_supported(&sym_dev))
goto free;
- if (sym_check_raid(&sym_dev))
- goto leave; /* Don't disable the device */
+ if (sym_check_raid(&sym_dev)) {
+ do_disable_device = 0; /* Don't disable the device */
+ goto free;
+ }
if (sym_set_workarounds(&sym_dev))
goto free;
@@ -1726,6 +1738,7 @@ static int __devinit sym2_probe(struct pci_dev *pdev,
sym_get_nvram(&sym_dev, &nvram);
+ do_iounmap = 0; /* Don't sym_iounmap_device() after sym_attach(). */
shost = sym_attach(&sym2_template, attach_count, &sym_dev);
if (!shost)
goto free;
@@ -1741,9 +1754,12 @@ static int __devinit sym2_probe(struct pci_dev *pdev,
detach:
sym_detach(pci_get_drvdata(pdev), pdev);
free:
+ if (do_iounmap)
+ sym_iounmap_device(&sym_dev);
pci_release_regions(pdev);
disable:
- pci_disable_device(pdev);
+ if (do_disable_device)
+ pci_disable_device(pdev);
leave:
return -ENODEV;
}