diff options
author | Tony Battersby <tonyb@cybernetics.com> | 2009-01-08 18:55:52 +0100 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-03-12 18:58:16 +0100 |
commit | a71d035de835caa7d14ef69928e0fde9fc241cc0 (patch) | |
tree | eb44f1a045b1d7d8b762c19520437ce9e3a12179 | |
parent | [SCSI] sym53c8xx: fix bogus free_irq() on error path (diff) | |
download | linux-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.c | 66 |
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; } |