Skip to content

Commit

Permalink
sata_promise: fix hardreset lockdep error
Browse files Browse the repository at this point in the history
sata_promise's pdc_hard_reset_port() needs to serialize because it
flips a port-specific bit in controller register that's shared by
all ports. The code takes the ata host lock for this, but that's
broken because an interrupt may arrive on our irq during the hard
reset sequence, and that too will take the ata host lock. With
lockdep enabled a big nasty warning is seen.

Fixed by adding private state to the ata host structure, containing
a second lock used only for serializing the hard reset sequences.
This eliminated the lockdep warnings both on my test rig and on
the original reporter's machine.

Signed-off-by: Mikael Pettersson <mikpe@it.uu.se>
Tested-by: Adko Branil <adkobranil@yahoo.com>
Cc: stable@vger.kernel.org
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Mikael Pettersson authored and Jeff Garzik committed Nov 28, 2012
1 parent 2844a48 commit 3100d49
Showing 1 changed file with 13 additions and 2 deletions.
15 changes: 13 additions & 2 deletions drivers/ata/sata_promise.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ struct pdc_port_priv {
dma_addr_t pkt_dma;
};

struct pdc_host_priv {
spinlock_t hard_reset_lock;
};

static int pdc_sata_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int pdc_sata_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
static int pdc_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
Expand Down Expand Up @@ -801,9 +805,10 @@ static void pdc_hard_reset_port(struct ata_port *ap)
void __iomem *host_mmio = ap->host->iomap[PDC_MMIO_BAR];
void __iomem *pcictl_b1_mmio = host_mmio + PDC_PCI_CTL + 1;
unsigned int ata_no = pdc_ata_port_to_ata_no(ap);
struct pdc_host_priv *hpriv = ap->host->private_data;
u8 tmp;

spin_lock(&ap->host->lock);
spin_lock(&hpriv->hard_reset_lock);

tmp = readb(pcictl_b1_mmio);
tmp &= ~(0x10 << ata_no);
Expand All @@ -814,7 +819,7 @@ static void pdc_hard_reset_port(struct ata_port *ap)
writeb(tmp, pcictl_b1_mmio);
readb(pcictl_b1_mmio); /* flush */

spin_unlock(&ap->host->lock);
spin_unlock(&hpriv->hard_reset_lock);
}

static int pdc_sata_hardreset(struct ata_link *link, unsigned int *class,
Expand Down Expand Up @@ -1182,6 +1187,7 @@ static int pdc_ata_init_one(struct pci_dev *pdev,
const struct ata_port_info *pi = &pdc_port_info[ent->driver_data];
const struct ata_port_info *ppi[PDC_MAX_PORTS];
struct ata_host *host;
struct pdc_host_priv *hpriv;
void __iomem *host_mmio;
int n_ports, i, rc;
int is_sataii_tx4;
Expand Down Expand Up @@ -1218,6 +1224,11 @@ static int pdc_ata_init_one(struct pci_dev *pdev,
dev_err(&pdev->dev, "failed to allocate host\n");
return -ENOMEM;
}
hpriv = devm_kzalloc(&pdev->dev, sizeof *hpriv, GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
spin_lock_init(&hpriv->hard_reset_lock);
host->private_data = hpriv;
host->iomap = pcim_iomap_table(pdev);

is_sataii_tx4 = pdc_is_sataii_tx4(pi->flags);
Expand Down

0 comments on commit 3100d49

Please sign in to comment.