Skip to content

Commit

Permalink
ide: pci: free PCI BARs on initialization failure
Browse files Browse the repository at this point in the history
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: 30fdfb9 ("PCI: Add a call to pci_assign_irq() in pci_device_probe()")
Fixes: 0e4c2ee ("alpha/PCI: Replace pci_fixup_irqs() call with host bridge IRQ mapping hooks")
Link: http://lkml.kernel.org/r/[email protected]
Reported-by: Guenter Roeck <[email protected]>
Tested-by: Guenter Roeck <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
[bhelgaas: add Fixes:]
Signed-off-by: Bjorn Helgaas <[email protected]>
Cc: Richard Henderson <[email protected]>
Cc: Ivan Kokshaysky <[email protected]>
Cc: Matt Turner <[email protected]>
  • Loading branch information
bzolnier authored and bjorn-helgaas committed Oct 3, 2017
1 parent d410a64 commit a06876a
Showing 1 changed file with 40 additions and 23 deletions.
63 changes: 40 additions & 23 deletions drivers/ide/setup-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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",
Expand Down Expand Up @@ -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
*
Expand All @@ -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;
Expand All @@ -420,24 +411,28 @@ 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;

ret = pci_read_config_word(dev, PCI_COMMAND, &pcicmd);
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;
}
Expand Down Expand Up @@ -540,21 +535,36 @@ 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]);
}

host = ide_host_alloc(d, hws, n_ports);
if (host == NULL) {
ret = -ENOMEM;
goto out;
goto out_free_bars;
}

host->dev[0] = &dev1->dev;
Expand All @@ -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])) {
Expand All @@ -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;
}
Expand Down

0 comments on commit a06876a

Please sign in to comment.