You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
389 lines
7.9 KiB
389 lines
7.9 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
gateway.c
|
|
|
|
Abstract:
|
|
|
|
Declaration of a gateway class that manages multiple interrelated
|
|
sub-devices on a device.
|
|
|
|
The IO gateway keeps track of elements queued to a device. The
|
|
gateway is only necessary for device/driver pairs that have multiple
|
|
independent device queues per physical device. A SCSI port driver,
|
|
for example, can queue items on a per-logical-unit basis instead of
|
|
per-HBA basis. The advantage of a per-logical-unit queue is that if a
|
|
logical-unit becomes busy, requests for different logical units can
|
|
be submitted to the adapter while the first logical unit is frozen.
|
|
|
|
The gateway object is the object that coordinates the communication
|
|
to the physical HBA.
|
|
|
|
---
|
|
------------- | H |
|
|
| LUN 1 Queue | --> | B |
|
|
------------- | A |
|
|
| | ----------
|
|
------------- | G | | |
|
|
| LUN 2 Queue | --> | a | --> | HBA ----
|
|
------------- | t | | |
|
|
| e | ------
|
|
------------- | w |
|
|
| LUN 1 Queue | --> | a |
|
|
------------- | y |
|
|
---
|
|
|
|
The gateway keeps track of whether the HBA is busy or frozen, how
|
|
many outstanding requests are on the HBA, and, when the HBA is busy,
|
|
the algorithm it uses to clear it's busy state.
|
|
|
|
Author:
|
|
|
|
Matthew D Hendel (math) 15-June-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
INLINE
|
|
VOID
|
|
ASSERT_GATEWAY(
|
|
IN PSTOR_IO_GATEWAY Gateway
|
|
)
|
|
{
|
|
#if DBG
|
|
ASSERT (Gateway->BusyRoutine != NULL);
|
|
ASSERT (Gateway->BusyCount >= 0);
|
|
ASSERT (Gateway->PauseCount >= 0);
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
StorCreateIoGateway(
|
|
IN PSTOR_IO_GATEWAY Gateway,
|
|
IN PSTOR_IO_GATEWAY_BUSY_ROUTINE BusyRoutine,
|
|
IN PVOID BusyContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create an IO gateway.
|
|
|
|
Arguments:
|
|
|
|
Gateway - IO Gateway to create.
|
|
|
|
BusyAlgorithm - Description of the algorithm to use and associated
|
|
parameters when the gatway is busy.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ASSERT (BusyRoutine != NULL);
|
|
|
|
RtlZeroMemory (Gateway, sizeof (STOR_IO_GATEWAY));
|
|
|
|
//
|
|
// The initial high and low water marks are somewhat irrelevant since
|
|
// we will define these when we get busied.
|
|
//
|
|
|
|
Gateway->HighWaterMark = MAXLONG;
|
|
Gateway->LowWaterMark = MAXLONG;
|
|
|
|
Gateway->BusyRoutine = BusyRoutine;
|
|
Gateway->BusyContext = BusyContext;
|
|
|
|
KeInitializeSpinLock (&Gateway->Lock);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
StorSubmitIoGatewayItem(
|
|
IN PSTOR_IO_GATEWAY Gateway
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempt to submit an item to the gateway.
|
|
|
|
Arguments:
|
|
|
|
Gateway - Gateway to submit the item to.
|
|
|
|
Return Value:
|
|
|
|
TRUE - If the item can be submitted to the underlying hardware.
|
|
|
|
FALSE - If the underlying hardware is currently busy with other
|
|
requests and the request should be held until the hardware is
|
|
ready to process more requets.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN Ready;
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
|
|
//
|
|
// PERF NOTE: This is the only adapter-wide lock aquisition done
|
|
// for an IO. Therefore, we can suppose it is the hottest lock
|
|
// in raidport (this remains to be seen from performance data).
|
|
// We should seriously investigate a way to either eliminate this
|
|
// lock or to turn it into a series of interlocked operations.
|
|
// Do not do any significant processing while this lock is held.
|
|
//
|
|
|
|
KeAcquireInStackQueuedSpinLockAtDpcLevel (&Gateway->Lock, &LockHandle);
|
|
|
|
//
|
|
// If the gateway is busy or paused, do not submit it.
|
|
//
|
|
|
|
if (Gateway->BusyCount > 0 ||
|
|
Gateway->PauseCount > 0 ||
|
|
Gateway->Outstanding >= Gateway->HighWaterMark) {
|
|
|
|
Ready = FALSE;
|
|
|
|
} else {
|
|
|
|
Gateway->Outstanding++;
|
|
|
|
if (Gateway->Outstanding >= Gateway->HighWaterMark) {
|
|
Gateway->BusyCount = TRUE;
|
|
}
|
|
|
|
Ready = TRUE;
|
|
}
|
|
|
|
KeReleaseInStackQueuedSpinLockFromDpcLevel (&LockHandle);
|
|
|
|
return Ready;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
StorIsIoGatewayBusy(
|
|
IN PSTOR_IO_GATEWAY Gateway
|
|
)
|
|
{
|
|
return (Gateway->BusyCount >= 1);
|
|
}
|
|
|
|
BOOLEAN
|
|
StorRemoveIoGatewayItem(
|
|
IN PSTOR_IO_GATEWAY Gateway
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify the gateway that an item has been completed.
|
|
|
|
Arguments:
|
|
|
|
Gateway - Gateway to submit notification to.
|
|
|
|
Return Value:
|
|
|
|
TRUE - If the completion of this item transitions the gateway from a
|
|
busy state to a non-busy state. In this case, the unit queues
|
|
that submit items to the gateway need to be restarted.
|
|
|
|
FALSE - If this completion did not change the busy state of the
|
|
gateway.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN Restart;
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
|
|
//
|
|
// PERF NOTE: This is the only adapter-wide lock used by the system
|
|
// in the IO path. See perf note in RaidAdapterGatewaySubmitItem.
|
|
//
|
|
|
|
KeAcquireInStackQueuedSpinLockAtDpcLevel (&Gateway->Lock, &LockHandle);
|
|
|
|
Gateway->Outstanding--;
|
|
ASSERT (Gateway->Outstanding >= 0);
|
|
|
|
if ((Gateway->BusyCount > 0) &&
|
|
(Gateway->Outstanding <= Gateway->LowWaterMark)) {
|
|
|
|
Gateway->BusyCount = FALSE;
|
|
Restart = TRUE; // (Gateway->BusyCount == 0) ? TRUE : FALSE;
|
|
|
|
} else {
|
|
Restart = FALSE;
|
|
}
|
|
|
|
//
|
|
// There are no more outstanding requests, so clear the event.
|
|
//
|
|
|
|
if (Gateway->EmptyEvent && Gateway->Outstanding == 0) {
|
|
KeSetEvent (Gateway->EmptyEvent, IO_NO_INCREMENT, FALSE);
|
|
Gateway->EmptyEvent = NULL;
|
|
}
|
|
|
|
KeReleaseInStackQueuedSpinLockFromDpcLevel (&LockHandle);
|
|
|
|
return Restart;
|
|
}
|
|
|
|
VOID
|
|
StorBusyIoGateway(
|
|
IN PSTOR_IO_GATEWAY Gateway
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Place the gateway into the busy state. The gateway will stay busy
|
|
until the number of requests has drained to a specific level.
|
|
|
|
Arguments:
|
|
|
|
Gateway - The gateway to make busy.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// The adapter MUST have some outstanding requests if it's claiming
|
|
// to be busy.
|
|
//
|
|
|
|
//
|
|
// Invoke the supplied busy routine to modify the high/low-water marks.
|
|
//
|
|
|
|
if (Gateway->BusyCount) {
|
|
return ;
|
|
}
|
|
|
|
Gateway->BusyRoutine (Gateway->BusyContext,
|
|
Gateway->Outstanding - 1,
|
|
&Gateway->HighWaterMark,
|
|
&Gateway->LowWaterMark);
|
|
|
|
Gateway->BusyCount = TRUE;
|
|
}
|
|
|
|
LONG
|
|
StorPauseIoGateway(
|
|
IN PSTOR_IO_GATEWAY Gateway
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Place the gateway into the paused state.
|
|
|
|
Arguments:
|
|
|
|
Gateway - Supplies the gateway to pause.
|
|
|
|
Return Value:
|
|
|
|
Pause count for the gateway.
|
|
|
|
--*/
|
|
{
|
|
return InterlockedIncrement (&Gateway->PauseCount);
|
|
}
|
|
|
|
LONG
|
|
StorResumeIoGateway(
|
|
IN OUT PSTOR_IO_GATEWAY Gateway
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resume the gateway.
|
|
|
|
Arguments:
|
|
|
|
Gateway - Supplies the gateway to resume.
|
|
|
|
Return Value:
|
|
|
|
Current pause count for the gateway.
|
|
|
|
--*/
|
|
{
|
|
LONG Count;
|
|
|
|
Count = InterlockedDecrement (&Gateway->PauseCount);
|
|
ASSERT (Count >= 0);
|
|
|
|
return Count;
|
|
}
|
|
|
|
BOOLEAN
|
|
StorIsIoGatewayPaused(
|
|
IN PSTOR_IO_GATEWAY Gateway
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns TRUE if the gateway is currently paused, else FALSE.
|
|
|
|
Arguments:
|
|
|
|
Gateway - Supplies the gateway to check.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ASSERT (Gateway->PauseCount >= 0);
|
|
return (Gateway->PauseCount != 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
StorSetIoGatewayEmptyEvent(
|
|
IN PSTOR_IO_GATEWAY Gateway,
|
|
IN PKEVENT Event
|
|
)
|
|
{
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
|
|
//
|
|
// BUGBUG: This is bad. Instead, the event should be owned by the gateway,
|
|
// we should give it out (and reference count it), so that multiple
|
|
// clients can use it. Need to figure out how to do this so we don't
|
|
// lock the dispatcher database twice per I/O.
|
|
//
|
|
|
|
KeAcquireInStackQueuedSpinLock (&Gateway->Lock, &LockHandle);
|
|
if (Gateway->Outstanding == 0) {
|
|
KeSetEvent (Event, IO_NO_INCREMENT, FALSE);
|
|
} else {
|
|
Gateway->EmptyEvent = Event;
|
|
}
|
|
KeReleaseInStackQueuedSpinLock (&LockHandle);
|
|
}
|
|
|
|
|