ide: pci: free PCI BARs on initialization failure
authorBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Tue, 3 Oct 2017 12:17:13 +0000 (14:17 +0200)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 3 Oct 2017 19:02:57 +0000 (14:02 -0500)
Recent pci_assign_irq() changes uncovered a problem with missing freeing of
PCI BARs on PCI IDE host initialization failure:

  ide0: disabled, no IRQ
  ide0: failed to initialize IDE interface
  ide0: disabling port
  cmd64x 0000:00:02.0: IDE controller (0x1095:0x0646 rev 0x07)
  CMD64x_IDE 0000:00:02.0: BAR 0: can't reserve [io  0x8050-0x8057]
  cmd64x 0000:00:02.0: can't reserve resources
  CMD64x_IDE: probe of 0000:00:02.0 failed with error -16

Fix the problem by adding missing freeing of PCI BARs to
ide_setup_pci_controller() and ide_pci_init_two().

Fixes: 30fdfb929e82 ("PCI: Add a call to pci_assign_irq() in pci_device_probe()")
Fixes: 0e4c2eeb758a ("alpha/PCI: Replace pci_fixup_irqs() call with host bridge IRQ mapping hooks")
Link: http://lkml.kernel.org/r/32ec730f-c1b0-5584-cd35-f8a809122b96@roeck-us.net
Reported-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
[bhelgaas: add Fixes:]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Matt Turner <mattst88@gmail.com>
drivers/ide/setup-pci.c

index 112d2fe1bcdbc8b3acb45d613901afa06798d556..fdc8e813170c32b4df99417c37d4940e0abbbc78 100644 (file)
@@ -179,6 +179,7 @@ EXPORT_SYMBOL_GPL(ide_setup_pci_noise);
 /**
  *     ide_pci_enable  -       do PCI enables
  *     @dev: PCI device
+ *     @bars: PCI BARs mask
  *     @d: IDE port info
  *
  *     Enable the IDE PCI device. We attempt to enable the device in full
@@ -189,9 +190,10 @@ EXPORT_SYMBOL_GPL(ide_setup_pci_noise);
  *     Returns zero on success or an error code
  */
 
-static int ide_pci_enable(struct pci_dev *dev, const struct ide_port_info *d)
+static int ide_pci_enable(struct pci_dev *dev, int bars,
+                         const struct ide_port_info *d)
 {
-       int ret, bars;
+       int ret;
 
        if (pci_enable_device(dev)) {
                ret = pci_enable_device_io(dev);
@@ -216,18 +218,6 @@ static int ide_pci_enable(struct pci_dev *dev, const struct ide_port_info *d)
                goto out;
        }
 
-       if (d->host_flags & IDE_HFLAG_SINGLE)
-               bars = (1 << 2) - 1;
-       else
-               bars = (1 << 4) - 1;
-
-       if ((d->host_flags & IDE_HFLAG_NO_DMA) == 0) {
-               if (d->host_flags & IDE_HFLAG_CS5520)
-                       bars |= (1 << 2);
-               else
-                       bars |= (1 << 4);
-       }
-
        ret = pci_request_selected_regions(dev, bars, d->name);
        if (ret < 0)
                printk(KERN_ERR "%s %s: can't reserve resources\n",
@@ -403,6 +393,7 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
 /**
  *     ide_setup_pci_controller        -       set up IDE PCI
  *     @dev: PCI device
+ *     @bars: PCI BARs mask
  *     @d: IDE port info
  *     @noisy: verbose flag
  *
@@ -411,7 +402,7 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
  *     and enables it if need be
  */
 
-static int ide_setup_pci_controller(struct pci_dev *dev,
+static int ide_setup_pci_controller(struct pci_dev *dev, int bars,
                                    const struct ide_port_info *d, int noisy)
 {
        int ret;
@@ -420,7 +411,7 @@ static int ide_setup_pci_controller(struct pci_dev *dev,
        if (noisy)
                ide_setup_pci_noise(dev, d);
 
-       ret = ide_pci_enable(dev, d);
+       ret = ide_pci_enable(dev, bars, d);
        if (ret < 0)
                goto out;
 
@@ -428,16 +419,20 @@ static int ide_setup_pci_controller(struct pci_dev *dev,
        if (ret < 0) {
                printk(KERN_ERR "%s %s: error accessing PCI regs\n",
                        d->name, pci_name(dev));
-               goto out;
+               goto out_free_bars;
        }
        if (!(pcicmd & PCI_COMMAND_IO)) {       /* is device disabled? */
                ret = ide_pci_configure(dev, d);
                if (ret < 0)
-                       goto out;
+                       goto out_free_bars;
                printk(KERN_INFO "%s %s: device enabled (Linux)\n",
                        d->name, pci_name(dev));
        }
 
+       goto out;
+
+out_free_bars:
+       pci_release_selected_regions(dev, bars);
 out:
        return ret;
 }
@@ -540,13 +535,28 @@ int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
 {
        struct pci_dev *pdev[] = { dev1, dev2 };
        struct ide_host *host;
-       int ret, i, n_ports = dev2 ? 4 : 2;
+       int ret, i, n_ports = dev2 ? 4 : 2, bars;
        struct ide_hw hw[4], *hws[] = { NULL, NULL, NULL, NULL };
 
+       if (d->host_flags & IDE_HFLAG_SINGLE)
+               bars = (1 << 2) - 1;
+       else
+               bars = (1 << 4) - 1;
+
+       if ((d->host_flags & IDE_HFLAG_NO_DMA) == 0) {
+               if (d->host_flags & IDE_HFLAG_CS5520)
+                       bars |= (1 << 2);
+               else
+                       bars |= (1 << 4);
+       }
+
        for (i = 0; i < n_ports / 2; i++) {
-               ret = ide_setup_pci_controller(pdev[i], d, !i);
-               if (ret < 0)
+               ret = ide_setup_pci_controller(pdev[i], bars, d, !i);
+               if (ret < 0) {
+                       if (i == 1)
+                               pci_release_selected_regions(pdev[0], bars);
                        goto out;
+               }
 
                ide_pci_setup_ports(pdev[i], d, &hw[i*2], &hws[i*2]);
        }
@@ -554,7 +564,7 @@ int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
        host = ide_host_alloc(d, hws, n_ports);
        if (host == NULL) {
                ret = -ENOMEM;
-               goto out;
+               goto out_free_bars;
        }
 
        host->dev[0] = &dev1->dev;
@@ -576,7 +586,7 @@ int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
                 * do_ide_setup_pci_device() on the first device!
                 */
                if (ret < 0)
-                       goto out;
+                       goto out_free_bars;
 
                /* fixup IRQ */
                if (ide_pci_is_in_compatibility_mode(pdev[i])) {
@@ -589,6 +599,13 @@ int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
        ret = ide_host_register(host, d, hws);
        if (ret)
                ide_host_free(host);
+       else
+               goto out;
+
+out_free_bars:
+       i = n_ports / 2;
+       while (i--)
+               pci_release_selected_regions(pdev[i], bars);
 out:
        return ret;
 }