⬢ DragonFlyBSD Kernel Audit
DF-4503 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/ahci/ahci_pm.c
+++ b/sys/dev/disk/ahci/ahci_pm.c
@@ -37,6 +37,7 @@
 #include "ahci.h"
 
 static void ahci_pm_dummy_done(struct ata_xfer *xa);
+static int ahci_pm_cmd(struct ata_xfer *xa);
 
 int
 ahci_pm_port_init(struct ahci_port *ap, struct ata_port *at)
@@ -987,15 +988,14 @@
 	xa->flags = ATA_F_POLL | ATA_F_AUTOSENSE;
 	xa->timeout = 1000;
 
-	if (ahci_ata_cmd(xa) == ATA_S_COMPLETE) {
+	error = ahci_pm_cmd(xa);
+	if (error == 0) {
 		*datap = xa->rfis.sector_count | (xa->rfis.lba_low << 8) |
 		       (xa->rfis.lba_mid << 16) | (xa->rfis.lba_high << 24);
-		error = 0;
 	} else {
 		kprintf("%s.%d pm_read SCA[%d] failed\n",
 			PORTNAME(ap), target, which);
 		*datap = 0;
-		error = EIO;
 	}
 	ahci_ata_put_xfer(xa);
 	return (error);
@@ -1028,15 +1028,41 @@
 	xa->flags = ATA_F_POLL;
 	xa->timeout = 1000;
 
-	if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
-		error = 0;
-	else
-		error = EIO;
+	error = ahci_pm_cmd(xa);
 	ahci_ata_put_xfer(xa);
 	return(error);
 }
 
 /*
+ * Run a PM register command to completion and return 0 on success, EIO
+ * on failure.
+ *
+ * A deferred timeout (ahci_ata_cmd_timeout(), "Deferred timeout until
+ * its safe") leaves the command ON THE CHIP with xa->state still
+ * ATA_S_ONCHIP while other commands on the port are active.  The CCB
+ * must NOT be recycled in that state: the chip (or the deferred-expiry
+ * machinery) still owns the slot and would later operate on a CCB that
+ * is on the free list or has been re-issued.  Drive the timeout
+ * machinery (bounded) until the command comes off the chip before the
+ * caller puts the xfer back.
+ */
+static int
+ahci_pm_cmd(struct ata_xfer *xa)
+{
+	struct ahci_port *ap = ((struct ahci_ccb *)xa)->ccb_port;
+	int guard = 31000;		/* ~31s worst case */
+	int rc;
+
+	rc = ahci_ata_cmd(xa);
+	while (rc != ATA_S_COMPLETE && xa->state == ATA_S_ONCHIP && --guard) {
+		ahci_ata_cmd_timeout((struct ahci_ccb *)xa);
+		ahci_port_intr(ap, 1);
+		ahci_os_softsleep();
+	}
+	return (rc == ATA_S_COMPLETE ? 0 : EIO);
+}
+
+/*
  * Dummy done callback for xa.
  */
 static void