ATA driver races with interrupts
Ville-Pertti Keinonen
will+freebsd-current at will.iki.fi
Mon Aug 2 09:15:24 PDT 2004
I've finally had time to track down a long-standing, easily repeatable
(on my system, anyhow) lockup.
When accessing two SATA disks on different channels of the same
controller simultaneously (both channels sharing the same interrupt), an
interrupt on one channel could retire the request on the other channel
if the interrupt occurred during ata_generic_transaction (after
ch->running is set, before the actual transaction begins), leaving the
device stuck (with ch->dma->flags set as ATA_DMA_ACTIVE but ch->running
== NULL), often with a message of "already active DMA on this device".
Attached is a patch against -current as of a couple of days ago (before
the #if 0 of PREEMPTION). I'm not sure whether this is the right way to
fix this (e.g. disabling interrupts altogether might be safer), but it
seems to work for me.
-------------- next part --------------
Index: ata-all.h
===================================================================
RCS file: /data/freebsd/src/sys/dev/ata/ata-all.h,v
retrieving revision 1.79
diff -u -r1.79 ata-all.h
--- ata-all.h 30 Apr 2004 16:21:34 -0000 1.79
+++ ata-all.h 2 Aug 2004 15:54:50 -0000
@@ -339,6 +339,7 @@
#define ATA_48BIT_ACTIVE 0x10
#define ATA_IMMEDIATE_MODE 0x20
#define ATA_HWGONE 0x40
+#define ATA_EXPECT_INTR 0x80
struct ata_device device[2]; /* devices on this channel */
#define MASTER 0x00
Index: ata-lowlevel.c
===================================================================
RCS file: /data/freebsd/src/sys/dev/ata/ata-lowlevel.c,v
retrieving revision 1.40
diff -u -r1.40 ata-lowlevel.c
--- ata-lowlevel.c 24 Jul 2004 19:03:28 -0000 1.40
+++ ata-lowlevel.c 2 Aug 2004 16:11:23 -0000
@@ -81,6 +81,7 @@
}
/* record the request as running */
+ ch->flags &= ~ATA_EXPECT_INTR;
ch->running = request;
ATA_DEBUG_RQ(request, "transaction");
@@ -140,6 +141,7 @@
}
/* return and wait for interrupt */
+ ch->flags |= ATA_EXPECT_INTR;
return ATA_OP_CONTINUES;
/* ATA DMA data transfer commands */
@@ -169,6 +171,7 @@
}
/* return and wait for interrupt */
+ ch->flags |= ATA_EXPECT_INTR;
return ATA_OP_CONTINUES;
/* ATAPI PIO commands */
@@ -225,6 +228,7 @@
ATA_PROTO_ATAPI_12 ? 6 : 8);
/* return and wait for interrupt */
+ ch->flags |= ATA_EXPECT_INTR;
return ATA_OP_CONTINUES;
case ATA_R_ATAPI|ATA_R_DMA:
@@ -290,6 +294,7 @@
}
/* return and wait for interrupt */
+ ch->flags |= ATA_EXPECT_INTR;
return ATA_OP_CONTINUES;
}
@@ -308,7 +313,7 @@
int length;
/* ignore this interrupt if there is no running request */
- if (!request)
+ if (!request || !(ch->flags & ATA_EXPECT_INTR))
return;
ATA_DEBUG_RQ(request, "interrupt");
More information about the freebsd-current
mailing list