353 lines
8.6 KiB
C++
353 lines
8.6 KiB
C++
/*++
|
||
|
||
Copyright (C) 1991-5 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
overlap.cxx
|
||
|
||
Abstract:
|
||
|
||
This module contains code specific to the overlapped io manager.
|
||
|
||
The purpose of this module is to help serialize io that overlaps with
|
||
each other. This class is used by stripes with parity to help prevent
|
||
corruption caused by race conditions when computing parity.
|
||
|
||
Author:
|
||
|
||
Norbert Kusters 2-Feb-1995
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
extern "C" {
|
||
#include <ntddk.h>
|
||
}
|
||
|
||
#include <ftdisk.h>
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma code_seg("PAGE")
|
||
#endif
|
||
|
||
NTSTATUS
|
||
OVERLAPPED_IO_MANAGER::Initialize(
|
||
IN ULONG BucketSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes an overlapped io manager.
|
||
|
||
Arguments:
|
||
|
||
BucketSize - Supplies the bucket size. Any I/O to this class may
|
||
not span more than one bucket. In the case of stripes
|
||
with parity, the bucket size is the stripe size. A
|
||
bucket size of 0 means that there are no buckets by
|
||
which requests would be partitioned (alt. an "infinite"
|
||
bucket size).
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
|
||
_numQueues = 256;
|
||
_bucketSize = BucketSize;
|
||
if (!_bucketSize) {
|
||
_bucketSize = STRIPE_SIZE;
|
||
_numQueues = 1;
|
||
}
|
||
_spinLock = (PKSPIN_LOCK)
|
||
ExAllocatePool(NonPagedPool, _numQueues*sizeof(KSPIN_LOCK));
|
||
if (!_spinLock) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
_ioQueue = (PLIST_ENTRY)
|
||
ExAllocatePool(NonPagedPool, _numQueues*sizeof(LIST_ENTRY));
|
||
if (!_ioQueue) {
|
||
ExFreePool(_spinLock);
|
||
_spinLock = NULL;
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
for (i = 0; i < _numQueues; i++) {
|
||
KeInitializeSpinLock(&_spinLock[i]);
|
||
InitializeListHead(&_ioQueue[i]);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma code_seg("PAGELK")
|
||
#endif
|
||
|
||
VOID
|
||
OVERLAPPED_IO_MANAGER::AcquireIoRegion(
|
||
IN OUT POVERLAP_TP TransferPacket,
|
||
IN BOOLEAN AllMembers
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queues the given transfer packet for its desired region.
|
||
The transfer packet completion routine will be called when the region
|
||
is free. 'ReleaseIoRegion' must be called to allow other transfer packets
|
||
to pass for a region that overlaps with one that is acquired.
|
||
|
||
Arguments:
|
||
|
||
TransferPacket - Supplies the IO region and completion routine.
|
||
|
||
AllMembers - Supplies whether or not to allocate this region
|
||
on all members.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG queueNumber;
|
||
PLIST_ENTRY q;
|
||
PKSPIN_LOCK spin;
|
||
KIRQL irql;
|
||
PLIST_ENTRY l;
|
||
POVERLAP_TP p;
|
||
|
||
ASSERT(!TransferPacket->InQueue);
|
||
|
||
TransferPacket->AllMembers = AllMembers;
|
||
TransferPacket->OverlappedIoManager = this;
|
||
|
||
// Search the queue for a request that overlaps with this one.
|
||
// If there is no overlap then call the completion routine
|
||
// for this transfer packet. Either way, queue this transfer
|
||
// packet at the end of the queue.
|
||
|
||
queueNumber = (ULONG) ((TransferPacket->Offset/_bucketSize)%_numQueues);
|
||
q = &_ioQueue[queueNumber];
|
||
spin = &_spinLock[queueNumber];
|
||
KeAcquireSpinLock(spin, &irql);
|
||
for (l = q->Blink; l != q; l = l->Blink) {
|
||
|
||
p = CONTAINING_RECORD(l, OVERLAP_TP, OverlapQueue);
|
||
|
||
if ((TransferPacket->AllMembers || p->AllMembers ||
|
||
p->TargetVolume == TransferPacket->TargetVolume) &&
|
||
p->Offset < TransferPacket->Offset + TransferPacket->Length &&
|
||
TransferPacket->Offset < p->Offset + p->Length) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
InsertTailList(q, &TransferPacket->OverlapQueue);
|
||
TransferPacket->InQueue = TRUE;
|
||
KeReleaseSpinLock(spin, irql);
|
||
|
||
if (l == q) {
|
||
TransferPacket->CompletionRoutine(TransferPacket);
|
||
} else {
|
||
// DbgPrint("Overlap: Transfer packet %x stuck in behind %x\n", TransferPacket, p);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
OVERLAPPED_IO_MANAGER::ReleaseIoRegion(
|
||
IN OUT POVERLAP_TP TransferPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases the IO region held by this packet and
|
||
wakes up waiting transfer packets.
|
||
|
||
Arguments:
|
||
|
||
TransferPacket - Supplies the TransferPacket whose region is to be
|
||
released.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG queueNumber;
|
||
PLIST_ENTRY q;
|
||
PKSPIN_LOCK spin;
|
||
LIST_ENTRY completionList;
|
||
KIRQL irql;
|
||
PLIST_ENTRY l, ll;
|
||
POVERLAP_TP p, pp;
|
||
|
||
if (!TransferPacket->InQueue) {
|
||
return;
|
||
}
|
||
|
||
queueNumber = (ULONG) ((TransferPacket->Offset/_bucketSize)%_numQueues);
|
||
q = &_ioQueue[queueNumber];
|
||
spin = &_spinLock[queueNumber];
|
||
InitializeListHead(&completionList);
|
||
|
||
KeAcquireSpinLock(spin, &irql);
|
||
l = TransferPacket->OverlapQueue.Flink;
|
||
RemoveEntryList(&TransferPacket->OverlapQueue);
|
||
TransferPacket->InQueue = FALSE;
|
||
for (; l != q; l = l->Flink) {
|
||
|
||
p = CONTAINING_RECORD(l, OVERLAP_TP, OverlapQueue);
|
||
|
||
if ((TransferPacket->AllMembers || p->AllMembers ||
|
||
p->TargetVolume == TransferPacket->TargetVolume) &&
|
||
TransferPacket->Offset < p->Offset + p->Length &&
|
||
p->Offset < TransferPacket->Offset + TransferPacket->Length) {
|
||
|
||
// This is a candidate for allocation, make sure that it
|
||
// is clear to run by checking for any other contention.
|
||
|
||
for (ll = p->OverlapQueue.Blink; ll != q; ll = ll->Blink) {
|
||
|
||
pp = CONTAINING_RECORD(ll, OVERLAP_TP, OverlapQueue);
|
||
|
||
if ((p->AllMembers || pp->AllMembers ||
|
||
p->TargetVolume == pp->TargetVolume) &&
|
||
pp->Offset < p->Offset + p->Length &&
|
||
p->Offset < pp->Offset + pp->Length) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (ll == q) {
|
||
InsertTailList(&completionList, &p->CompletionList);
|
||
// DbgPrint("Overlap: Releasing packet %x that was behind %x\n", p, TransferPacket);
|
||
}
|
||
}
|
||
}
|
||
KeReleaseSpinLock(spin, irql);
|
||
|
||
while (!IsListEmpty(&completionList)) {
|
||
l = RemoveHeadList(&completionList);
|
||
p = CONTAINING_RECORD(l, OVERLAP_TP, CompletionList);
|
||
p->CompletionRoutine(p);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
OVERLAPPED_IO_MANAGER::PromoteToAllMembers(
|
||
IN OUT POVERLAP_TP TransferPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine promotes an already allocated transfer packet to
|
||
all members.
|
||
|
||
Arguments:
|
||
|
||
TransferPacket - Supplies a transfer packet that is already in
|
||
the overlapped io queue.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG queueNumber;
|
||
PLIST_ENTRY q;
|
||
PKSPIN_LOCK spin;
|
||
KIRQL irql;
|
||
PLIST_ENTRY l;
|
||
POVERLAP_TP p;
|
||
|
||
if (TransferPacket->AllMembers) {
|
||
TransferPacket->CompletionRoutine(TransferPacket);
|
||
return;
|
||
}
|
||
|
||
// DbgPrint("Overlap: Promoting %x to all members.\n", TransferPacket);
|
||
|
||
queueNumber = (ULONG) ((TransferPacket->Offset/_bucketSize)%_numQueues);
|
||
q = &_ioQueue[queueNumber];
|
||
spin = &_spinLock[queueNumber];
|
||
KeAcquireSpinLock(spin, &irql);
|
||
ASSERT(!TransferPacket->AllMembers);
|
||
TransferPacket->AllMembers = TRUE;
|
||
for (l = q->Blink; l != &TransferPacket->OverlapQueue; l = l->Blink) {
|
||
|
||
p = CONTAINING_RECORD(l, OVERLAP_TP, OverlapQueue);
|
||
|
||
if (!p->AllMembers &&
|
||
p->TargetVolume != TransferPacket->TargetVolume &&
|
||
TransferPacket->Offset < p->Offset + p->Length &&
|
||
p->Offset < TransferPacket->Offset + TransferPacket->Length) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
if (l != &TransferPacket->OverlapQueue) {
|
||
RemoveEntryList(&TransferPacket->OverlapQueue);
|
||
InsertHeadList(l, &TransferPacket->OverlapQueue);
|
||
// DbgPrint("Moving behind %x.\n", p);
|
||
}
|
||
for (l = TransferPacket->OverlapQueue.Blink; l != q; l = l->Blink) {
|
||
|
||
p = CONTAINING_RECORD(l, OVERLAP_TP, OverlapQueue);
|
||
|
||
if (TransferPacket->Offset < p->Offset + p->Length &&
|
||
p->Offset < TransferPacket->Offset + TransferPacket->Length) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
KeReleaseSpinLock(spin, irql);
|
||
|
||
if (l == q) {
|
||
TransferPacket->CompletionRoutine(TransferPacket);
|
||
} else {
|
||
// DbgPrint("Overlap: Waiting for %x.\n", p);
|
||
}
|
||
}
|
||
|
||
OVERLAPPED_IO_MANAGER::~OVERLAPPED_IO_MANAGER(
|
||
)
|
||
|
||
{
|
||
if (_spinLock) {
|
||
ExFreePool(_spinLock);
|
||
_spinLock = NULL;
|
||
}
|
||
if (_ioQueue) {
|
||
ExFreePool(_ioQueue);
|
||
_ioQueue = NULL;
|
||
}
|
||
}
|