Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ata_piix: parallel scanning on PATA needs an extra locking

Commit log for commit 517d3cc15b36392e518abab6bacbb72089658313
("[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: Alan Cox <alan@linux.intel.com>
Cc: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Bartlomiej Zolnierkiewicz and committed by
Linus Torvalds
60c3be38 b5af7544

+13 -1
+13 -1
drivers/ata/ata_piix.c
··· 664 664 return ata_sff_prereset(link, deadline); 665 665 } 666 666 667 + static DEFINE_SPINLOCK(piix_lock); 668 + 667 669 /** 668 670 * piix_set_piomode - Initialize host controller PATA PIO timings 669 671 * @ap: Port whose timings we are configuring ··· 679 677 680 678 static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev) 681 679 { 682 - unsigned int pio = adev->pio_mode - XFER_PIO_0; 683 680 struct pci_dev *dev = to_pci_dev(ap->host->dev); 681 + unsigned long flags; 682 + unsigned int pio = adev->pio_mode - XFER_PIO_0; 684 683 unsigned int is_slave = (adev->devno != 0); 685 684 unsigned int master_port= ap->port_no ? 0x42 : 0x40; 686 685 unsigned int slave_port = 0x44; ··· 710 707 /* Intel specifies that the PPE functionality is for disk only */ 711 708 if (adev->class == ATA_DEV_ATA) 712 709 control |= 4; /* PPE enable */ 710 + 711 + spin_lock_irqsave(&piix_lock, flags); 713 712 714 713 /* PIO configuration clears DTE unconditionally. It will be 715 714 * programmed in set_dmamode which is guaranteed to be called ··· 752 747 udma_enable &= ~(1 << (2 * ap->port_no + adev->devno)); 753 748 pci_write_config_byte(dev, 0x48, udma_enable); 754 749 } 750 + 751 + spin_unlock_irqrestore(&piix_lock, flags); 755 752 } 756 753 757 754 /** ··· 771 764 static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, int isich) 772 765 { 773 766 struct pci_dev *dev = to_pci_dev(ap->host->dev); 767 + unsigned long flags; 774 768 u8 master_port = ap->port_no ? 0x42 : 0x40; 775 769 u16 master_data; 776 770 u8 speed = adev->dma_mode; ··· 784 776 { 1, 0 }, 785 777 { 2, 1 }, 786 778 { 2, 3 }, }; 779 + 780 + spin_lock_irqsave(&piix_lock, flags); 787 781 788 782 pci_read_config_word(dev, master_port, &master_data); 789 783 if (ap->udma_mask) ··· 877 867 /* Don't scribble on 0x48 if the controller does not support UDMA */ 878 868 if (ap->udma_mask) 879 869 pci_write_config_byte(dev, 0x48, udma_enable); 870 + 871 + spin_unlock_irqrestore(&piix_lock, flags); 880 872 } 881 873 882 874 /**