Q: I'd like to use secondary interrupts in my SCSI
Interface Module (SIM) but doing so causes strange deadlocks.
Is it legal to use secondary interrupts in a SIM?
A: In general, using secondary interrupts on the page
fault path is not legal. There are, however, circumstances
under which it works. As with most of my Q&A, this
takes some explanation.
Note:
The page fault path is the set
system of system software and device drivers that
are required to resolve a virtual memory page
fault. The page fault path is an interesting
concept in operating system design, because most
operating systems require that all entities on the
page fault path not cause a page fault. Double page
faults will commonly cause an operating system to
"panic".
On Mac OS, double page faults are always fatal.
Mac OS prevents double page faults by disabling
"user code" while page fault path entities are
executing. In addition, all entities on the page
fault path must be capable of performing an I/O
request with interrupts disabled.
For more information about the virtual memory
implementation on Mac OS, see Technote 1094
Virtual
Memory Application Compatibility.
|
IMPORTANT:
While this Q&A is couched in terms of SCSI
Manager SIMs, the same logic applies to any entity
on the page fault path. Third-party developer
opportunities on the page fault path currently
include SIMs, AIMs (ATA Interface Modules), and
disk drivers. This list will grow as new
technologies, like FireWire, evolve to support disk
devices.
|
The two common reasons for using secondary interrupts in
a device driver are:
Reducing interrupt latency -- By deferring complex
work to a secondary interrupt, you can reduce the amount
of time your driver spends at hardware interrupt time,
and thereby reduce the overall interrupt latency of the
system.
Concurrency guarantee -- Secondary interrupts are
guaranteed to be serialized; only one secondary interrupt
handler can be running at any point in time. This is a
very useful concurrency control mechanism. If you always
access global data structures from your secondary
interrupt handler, you can be assured that only one
thread of execution is reading or writing those global
data structures at a time.
However, secondary interrupts can cause problems for
devices on the page fault path. Imagine the following
scenario:
Your SIM takes a hardware interrupt, schedules a
secondary interrupt (SIH A), and then returns from its
hardware interrupt handler. For your SIM to make forward
progress, its secondary interrupt handler must execute to
completion.
The interrupt systems notices that it is returning to
interrupt level 0 and begins processing secondary
interrupts. It enables interrupts and starts running
secondary interrupt handlers.
While SIH A is running, a Time Manager interrupt
occurs. The timer task executes, schedules a deferred
task, and then returns.
The interrupt system notices that it is returning
from the Time Manager interrupt to interrupt level 0.
There are deferred tasks to run, so it enables interrupts
and starts running deferred tasks.
One of the deferred tasks causes a page fault.
The Virtual Memory Manager intercepts the page fault,
and makes a synchronous request to the disk device driver
to read the page contents from the backing store.
The disk device driver calls SCSI Manager to execute
a SCSI command, which in turn calls your SIM. Your SIM
starts the command and returns, expecting a hardware
interrupt to complete the command.
Your SIM's hardware interrupt fires and schedules a
secondary interrupt (SIH B) to complete the command.
The system is now deadlocked. The SIH B cannot run
because secondary interrupt handlers must be single-threaded
and SIH A is already running. But the SIH A cannot run
because it has been interrupted by a page fault, which is
sitting in a synchronous wait loop waiting for SIH B to run.
The solution is to not use secondary interrupt handlers
in your SIM, or any software entity on the page fault path.
Newer versions of Mac OS provide another workaround for
this problem. On such systems, the OS detects these
potential deadlock cases (waiting for a synchronous device
request with an interrupt mask of 0 and with secondary
interrupt handlers queued) and calls your
SIMInterruptPoll
routine. Your interrupt poll routine can detect that it has
queued a secondary interrupt that has not yet run, and
perform the appropriate action directly. Typically this
involves calling your secondary interrupt handler routine
directly.
This approach has a number of important caveats:
It does not work on older systems. You can check for
the presence of secondary interrupt polling using the
code in Listing 1. You should find it available on Mac OS
8.5 and later.
Secondary interrupt polling may undermine the
concurrency guarantees provided by secondary interrupts.
In the above example, look carefully at what happens during
the deadlock recovery process. The system calls your
interrupt poll routine and your interrupt poll routine
calls SIH B directly. At this point, you have two threads
of execution inside secondary interrupt handlers, SIH A
and SIH B. This could cause problems if you designed your
SIM to rely on the concurrency guarantees provided by
secondary interrupts. If you want to use secondary
interrupts in your SIM, you must take this potential
reentrancy into account.
Secondary interrupt polling is only useful for
devices with an interrupt poll routine, such as SIMs and
AIMs. Other native drivers, such as disk drivers, do not
have an interrupt poll routine and cannot use secondary
interrupts if they are on the page fault path.
Because of these caveats, it may just be easier to do
everything in your SIMs hardware interrupt handler. SIMs are
mostly I/O bound, so the interrupt latency increase of doing
everything in your hardware interrupt handler is minimal.
You can detect whether secondary interrupt polling is
implemented using Gestalt , as shown in Listing
1.
Listing 1. Determining whether secondary interrupt
polling is available
static Boolean HasSecondaryInterruptPolling(void)
{
UInt32 response;
return (Gestalt(gestaltSCSI, (SInt32 *) &response) == noErr) &&
((response & (1 << gestaltSCSIPollSIH)) != 0);
}
|
In summary:
Prior to Mac OS 8.5, you must never use secondary
interrupts on the page fault path.
With Mac OS 8.5 and beyond, SIMs and AIMs may use
secondary interrupts, but they must take special action
in their interrupt poll routine.
Despite the difficulties using secondary interrupts
in a SIM, DTS recommends that SIM developers seriously
consider adopting this technique. The primary advantage,
lower system interrupt latency, is a significant benefit
to real-time applications such as QuickTime.
For more information about this mind-manglingly complex
part of the Mac OS I/O subsystem, see:
|