Commit 60c3be38 authored by Bartlomiej Zolnierkiewicz's avatar Bartlomiej Zolnierkiewicz Committed by Linus Torvalds

ata_piix: parallel scanning on PATA needs an extra locking

Commit log for commit 517d3cc1
("[libata] ata_piix: Enable parallel scan") says:

    This patch turns on parallel scanning for the ata_piix driver.
    This driver is used on most netbooks (no AHCI for cheap storage it seems).
    The scan is the dominating time factor in the kernel boot for these
    devices; with this flag it gets cut in half for the device I used
    for testing (eeepc).
    Alan took a look at the driver source and concluded that it ought to be safe
    to do for this driver.  Alan has also checked with the hardware team.

and it is all true but once we put all things together additional
constraints for PATA controllers show up (some hardware registers
have per-host not per-port atomicity) and we risk misprogramming
the controller.

I used the following test to check whether the issue is real:

  @@ -736,8 +736,20 @@ static void piix_set_piomode(struct ata_
   			(timings[pio][1] << 8);
   	}
   	pci_write_config_word(dev, master_port, master_data);
  -	if (is_slave)
  +	if (is_slave) {
  +		if (ap->port_no == 0) {
  +			u8 tmp = slave_data;
  +
  +			while (slave_data == tmp) {
  +				pci_read_config_byte(dev, slave_port, &tmp);
  +				msleep(50);
  +			}
  +
  +			dev_printk(KERN_ERR, &dev->dev, "PATA parallel scan "
  +				   "race detected\n");
  +		}
   		pci_write_config_byte(dev, slave_port, slave_data);
  +	}

   	/* Ensure the UDMA bit is off - it will be turned back on if
   	   UDMA is selected */

and it indeed triggered the error message.

Lets fix all such races by adding an extra locking to ->set_piomode
and ->set_dmamode methods for PATA controllers.

[ Alan: would be better to take the host lock in libata-core for these
  cases so that we fix all the adapters in one swoop.  "Looks fine as a
  temproary quickfix tho" ]

Cc: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: default avatarAlan Cox <alan@linux.intel.com>
Cc: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: default avatarBartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b5af7544
...@@ -664,6 +664,8 @@ static int piix_pata_prereset(struct ata_link *link, unsigned long deadline) ...@@ -664,6 +664,8 @@ static int piix_pata_prereset(struct ata_link *link, unsigned long deadline)
return ata_sff_prereset(link, deadline); return ata_sff_prereset(link, deadline);
} }
static DEFINE_SPINLOCK(piix_lock);
/** /**
* piix_set_piomode - Initialize host controller PATA PIO timings * piix_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring * @ap: Port whose timings we are configuring
...@@ -677,8 +679,9 @@ static int piix_pata_prereset(struct ata_link *link, unsigned long deadline) ...@@ -677,8 +679,9 @@ static int piix_pata_prereset(struct ata_link *link, unsigned long deadline)
static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev) static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
{ {
unsigned int pio = adev->pio_mode - XFER_PIO_0;
struct pci_dev *dev = to_pci_dev(ap->host->dev); struct pci_dev *dev = to_pci_dev(ap->host->dev);
unsigned long flags;
unsigned int pio = adev->pio_mode - XFER_PIO_0;
unsigned int is_slave = (adev->devno != 0); unsigned int is_slave = (adev->devno != 0);
unsigned int master_port= ap->port_no ? 0x42 : 0x40; unsigned int master_port= ap->port_no ? 0x42 : 0x40;
unsigned int slave_port = 0x44; unsigned int slave_port = 0x44;
...@@ -708,6 +711,8 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev) ...@@ -708,6 +711,8 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
if (adev->class == ATA_DEV_ATA) if (adev->class == ATA_DEV_ATA)
control |= 4; /* PPE enable */ control |= 4; /* PPE enable */
spin_lock_irqsave(&piix_lock, flags);
/* PIO configuration clears DTE unconditionally. It will be /* PIO configuration clears DTE unconditionally. It will be
* programmed in set_dmamode which is guaranteed to be called * programmed in set_dmamode which is guaranteed to be called
* after set_piomode if any DMA mode is available. * after set_piomode if any DMA mode is available.
...@@ -747,6 +752,8 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev) ...@@ -747,6 +752,8 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
udma_enable &= ~(1 << (2 * ap->port_no + adev->devno)); udma_enable &= ~(1 << (2 * ap->port_no + adev->devno));
pci_write_config_byte(dev, 0x48, udma_enable); pci_write_config_byte(dev, 0x48, udma_enable);
} }
spin_unlock_irqrestore(&piix_lock, flags);
} }
/** /**
...@@ -764,6 +771,7 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev) ...@@ -764,6 +771,7 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, int isich) static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, int isich)
{ {
struct pci_dev *dev = to_pci_dev(ap->host->dev); struct pci_dev *dev = to_pci_dev(ap->host->dev);
unsigned long flags;
u8 master_port = ap->port_no ? 0x42 : 0x40; u8 master_port = ap->port_no ? 0x42 : 0x40;
u16 master_data; u16 master_data;
u8 speed = adev->dma_mode; u8 speed = adev->dma_mode;
...@@ -777,6 +785,8 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in ...@@ -777,6 +785,8 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in
{ 2, 1 }, { 2, 1 },
{ 2, 3 }, }; { 2, 3 }, };
spin_lock_irqsave(&piix_lock, flags);
pci_read_config_word(dev, master_port, &master_data); pci_read_config_word(dev, master_port, &master_data);
if (ap->udma_mask) if (ap->udma_mask)
pci_read_config_byte(dev, 0x48, &udma_enable); pci_read_config_byte(dev, 0x48, &udma_enable);
...@@ -867,6 +877,8 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in ...@@ -867,6 +877,8 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in
/* Don't scribble on 0x48 if the controller does not support UDMA */ /* Don't scribble on 0x48 if the controller does not support UDMA */
if (ap->udma_mask) if (ap->udma_mask)
pci_write_config_byte(dev, 0x48, udma_enable); pci_write_config_byte(dev, 0x48, udma_enable);
spin_unlock_irqrestore(&piix_lock, flags);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment