From ca0a66e567cbeb1b974a2feb7c2176464d7fb6b1 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Mon, 23 Jan 2012 12:55:33 -0800 Subject: [PATCH] kernel - Fix SILI callout timer race * Do similar fix for SILI that we did for AHCI. * callout_stop_sync() can block. If this occurs a race can cause a CCB to be processed for completion twice. * Add a serial number to detect this situation. * If the serial number does not match after the timeout is stopped we don't want to mess with the xa at all. Tested-by: lentferj --- sys/dev/disk/sili/atascsi.h | 1 + sys/dev/disk/sili/sili.c | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/sys/dev/disk/sili/atascsi.h b/sys/dev/disk/sili/atascsi.h index d4b195950d..a2b7f006cd 100644 --- a/sys/dev/disk/sili/atascsi.h +++ b/sys/dev/disk/sili/atascsi.h @@ -309,6 +309,7 @@ struct ata_xfer { void (*complete)(struct ata_xfer *); u_int timeout; + int serial; /* detect timeout races */ int flags; #define ATA_F_READ (1<<0) diff --git a/sys/dev/disk/sili/sili.c b/sys/dev/disk/sili/sili.c index cec896db42..22cb218fa8 100644 --- a/sys/dev/disk/sili/sili.c +++ b/sys/dev/disk/sili/sili.c @@ -1050,6 +1050,7 @@ sili_port_hardstop(struct sili_port *ap) struct ata_port *at; int i; int slot; + int serial; ap->ap_state = AP_S_FATAL_ERROR; ap->ap_probe = ATA_PROBE_FAILED; @@ -1085,6 +1086,7 @@ sili_port_hardstop(struct sili_port *ap) /* * Clean up the command list. */ +restart: while (ap->ap_active) { slot = ffs(ap->ap_active) - 1; ap->ap_active &= ~(1 << slot); @@ -1092,7 +1094,13 @@ sili_port_hardstop(struct sili_port *ap) --ap->ap_active_cnt; ccb = &ap->ap_ccbs[slot]; if (ccb->ccb_xa.flags & ATA_F_TIMEOUT_RUNNING) { - callout_stop(&ccb->ccb_timeout); + serial = ccb->ccb_xa.serial; + callout_stop_sync(&ccb->ccb_timeout); + if (serial != ccb->ccb_xa.serial) { + kprintf("%s: Warning: timeout race ccb %p\n", + PORTNAME(ap), ccb); + goto restart; + } ccb->ccb_xa.flags &= ~ATA_F_TIMEOUT_RUNNING; } ccb->ccb_xa.flags &= ~(ATA_F_TIMEOUT_DESIRED | @@ -2024,6 +2032,7 @@ sili_put_ccb(struct sili_ccb *ccb) lockmgr(&ap->ap_ccb_lock, LK_EXCLUSIVE); ccb->ccb_xa.state = ATA_S_PUT; + ++ccb->ccb_xa.serial; TAILQ_INSERT_TAIL(&ap->ap_ccb_free, ccb, ccb_entry); lockmgr(&ap->ap_ccb_lock, LK_RELEASE); } @@ -2392,13 +2401,20 @@ static void sili_ata_cmd_done(struct sili_ccb *ccb) { struct ata_xfer *xa = &ccb->ccb_xa; + int serial; /* * NOTE: callout does not lock port and may race us modifying * the flags, so make sure its stopped. */ if (xa->flags & ATA_F_TIMEOUT_RUNNING) { - callout_stop(&ccb->ccb_timeout); + serial = ccb->ccb_xa.serial; + callout_stop_sync(&ccb->ccb_timeout); + if (serial != ccb->ccb_xa.serial) { + kprintf("%s: Warning: timeout race ccb %p\n", + PORTNAME(ccb->ccb_port), ccb); + return; + } xa->flags &= ~ATA_F_TIMEOUT_RUNNING; } xa->flags &= ~(ATA_F_TIMEOUT_DESIRED | ATA_F_TIMEOUT_EXPIRED); -- 2.41.0