|
|
/*++
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); }
|