⬢ DragonFlyBSD Kernel Audit
DF-4693 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/sili/sili_pm.c
+++ b/sys/dev/disk/sili/sili_pm.c
@@ -654,8 +654,23 @@ sili_pm_read(struct sili_port *ap, int target, int which, u_int32_t *datap)
 	xa->flags = ATA_F_POLL | ATA_F_AUTOSENSE;
 	xa->timeout = 1000;
 
-	if (sili_ata_cmd(xa) == ATA_S_COMPLETE) {
-		*datap = xa->rfis->sector_count | (xa->rfis->lba_low << 8) |
+	switch (sili_ata_cmd(xa)) {
+	case ATA_S_COMPLETE:
+		*datap = xa->rfis->sector_count | (xa->rfis->lba_low << 8) |
 		       (xa->rfis->lba_mid << 16) | (xa->rfis->lba_high << 24);
 		error = 0;
+		break;
+	case ATA_S_ONCHIP:
+		/*
+		 * Deferred timeout: the slot is still programmed on the
+		 * chip and ap_expired processing is pending.  We must NOT
+		 * recycle the xa until the expiry processing (triggered by
+		 * the remaining commands' completions -> sili_port_reinit)
+		 * has completed this xa.
+		 */
+		while (xa->state == ATA_S_ONCHIP) {
+			sili_port_intr(ap, 1);
+			sili_os_softsleep();
+		}
+		/* FALL THROUGH */
+	default:
 		kprintf("%s.%d pm_read SCA[%d] failed\n",
 			PORTNAME(ap), target, which);
 		*datap = 0;
@@ -695,8 +710,21 @@ sili_pm_write(struct sili_port *ap, int target, int which, u_int32_t data)
 	xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE;
 	xa->timeout = 1000;
 
-	if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
+	switch (sili_ata_cmd(xa)) {
+	case ATA_S_COMPLETE:
 		error = 0;
-	else
+		break;
+	case ATA_S_ONCHIP:
+		/* same deferred-timeout hazard as sili_pm_read() */
+		while (xa->state == ATA_S_ONCHIP) {
+			sili_port_intr(ap, 1);
+			sili_os_softsleep();
+		}
+		/* FALL THROUGH */
+	default:
 		error = EIO;
+		break;
+	}
 	sili_ata_put_xfer(xa);
 	return(error);
 }