NT4/private/ntos/dd/newft/stripewp.cxx
2020-09-30 17:12:29 +02:00

3343 lines
86 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991-5 Microsoft Corporation
Module Name:
stripewp.cxx
Abstract:
This module contains the code specific to stripes with parity for the fault
tolerance driver.
Author:
Bob Rinne (bobri) 2-Feb-1992
Mike Glass (mglass)
Norbert Kusters 2-Feb-1995
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "ftdisk.h"
typedef struct _START_REGENERATION_CONTEXT {
PSTRIPE_WP StripeWp;
PFT_VOLUME SpareVolume;
FT_COMPLETION_ROUTINE CompletionRoutine;
PVOID Context;
PSWP_REBUILD_TP Packet;
} START_REGENERATION_CONTEXT, *PSTART_REGENERATION_CONTEXT;
NTSTATUS
STRIPE_WP::Initialize(
IN OUT PFT_VOLUME* VolumeArray,
IN ULONG ArraySize
)
/*++
Routine Description:
Initialize routine for FT_VOLUME of type STRIPE.
Arguments:
VolumeArray - Supplies the array of volumes for this volume set.
Return Value:
None.
--*/
{
NTSTATUS status;
ULONG i, j;
LONGLONG newSize;
if (ArraySize < 3) {
return STATUS_INVALID_PARAMETER;
}
status = COMPOSITE_FT_VOLUME::Initialize(VolumeArray, ArraySize);
if (!NT_SUCCESS(status)) {
return status;
}
_memberSize = VolumeArray[0]->QueryVolumeSize();
for (i = 1; i < ArraySize; i++) {
newSize = VolumeArray[i]->QueryVolumeSize();
if (_memberSize > newSize) {
_memberSize = newSize;
}
}
_memberSize = _memberSize/_stripeSize*_stripeSize;
_volumeSize = _memberSize*(ArraySize - 1);
_initializing = FALSE;
_syncExpected = TRUE;
_requestCount = (PLONG) ExAllocatePool(NonPagedPool, ArraySize*sizeof(LONG));
if (!_requestCount) {
return STATUS_INSUFFICIENT_RESOURCES;
}
for (i = 0; i < ArraySize; i++) {
_requestCount[i] = 0;
}
_waitingForOrphanIdle = NULL;
status = _overlappedIoManager.Initialize(_stripeSize);
if (!NT_SUCCESS(status)) {
return status;
}
status = _parityIoManager.Initialize(_stripeSize);
if (!NT_SUCCESS(status)) {
return status;
}
_ePacket = new SWP_WRITE_TP;
if (!_ePacket ||
!_ePacket->AllocateMdls(_stripeSize) ||
!_ePacket->AllocateMdl((PVOID) 1, _stripeSize)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
_ePacketInUse = FALSE;
_ePacketQueueBeingServiced = FALSE;
InitializeListHead(&_ePacketQueue);
_eRegeneratePacket = new SWP_REGENERATE_TP;
if (!_eRegeneratePacket ||
!_eRegeneratePacket->AllocateMdl(_stripeSize)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
_eRegeneratePacketInUse = FALSE;
InitializeListHead(&_eRegeneratePacketQueue);
_eRecoverPacket = new SWP_RECOVER_TP;
if (!_eRecoverPacket ||
!_eRecoverPacket->AllocateMdls(QuerySectorSize())) {
return STATUS_INSUFFICIENT_RESOURCES;
}
_eRecoverPacketInUse = FALSE;
InitializeListHead(&_eRecoverPacketQueue);
return STATUS_SUCCESS;
}
VOID
STRIPE_WP::Transfer(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
Transfer routine for STRIPE_WP type FT_VOLUME. Figure out
which volumes this request needs to be dispatched to.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
KIRQL irql;
if (TransferPacket->Offset + TransferPacket->Length > _volumeSize) {
TransferPacket->IoStatus.Status = STATUS_INVALID_PARAMETER;
TransferPacket->IoStatus.Information = 0;
TransferPacket->CompletionRoutine(TransferPacket);
return;
}
KeAcquireSpinLock(&_spinLock, &irql);
if ((_ePacketInUse || _ePacketQueueBeingServiced) &&
TransferPacket->Mdl) {
InsertTailList(&_ePacketQueue, &TransferPacket->QueueEntry);
KeReleaseSpinLock(&_spinLock, irql);
return;
}
KeReleaseSpinLock(&_spinLock, irql);
if (!TransferPacket->Mdl) {
TransferPacket->ReadPacket = TRUE;
}
if (!LaunchParallel(TransferPacket)) {
if (!TransferPacket->Mdl) {
TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
TransferPacket->IoStatus.Information = 0;
TransferPacket->CompletionRoutine(TransferPacket);
return;
}
KeAcquireSpinLock(&_spinLock, &irql);
if (_ePacketInUse || _ePacketQueueBeingServiced) {
InsertTailList(&_ePacketQueue, &TransferPacket->QueueEntry);
KeReleaseSpinLock(&_spinLock, irql);
return;
}
_ePacketInUse = TRUE;
KeReleaseSpinLock(&_spinLock, irql);
LaunchSequential(TransferPacket);
}
}
VOID
STRIPE_WP::ReplaceBadSector(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is a no-op since replacing bad sectors doesn't make sense
on an FT component with redundancy built in to it.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
TransferPacket->IoStatus.Status = STATUS_UNSUCCESSFUL;
TransferPacket->IoStatus.Information = 0;
TransferPacket->CompletionRoutine(TransferPacket);
}
VOID
StripeWpCompositeVolumeCompletionRoutine(
IN PVOID Context,
IN NTSTATUS Status
)
{
PFT_COMPLETION_ROUTINE_CONTEXT context;
KIRQL irql;
LONG count;
context = (PFT_COMPLETION_ROUTINE_CONTEXT) Context;
KeAcquireSpinLock(&context->SpinLock, &irql);
if (!NT_SUCCESS(Status) &&
FtpIsWorseStatus(Status, context->Status)) {
context->Status = Status;
}
count = --context->RefCount;
KeReleaseSpinLock(&context->SpinLock, irql);
if (!count) {
context->CompletionRoutine(context->Context, STATUS_SUCCESS);
ExFreePool(context);
}
}
VOID
StripeWpSyncFinalCompletion(
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description:
This is the final completion routine for the initialize check data
routine.
Arguments:
Context - Supplies the context.
Status - Supplies the status.
Return Value:
None.
--*/
{
PFT_COMPLETION_ROUTINE_CONTEXT context;
KIRQL irql;
LONG count;
PSTRIPE_WP t;
ULONG i, n;
BOOLEAN b;
context = (PFT_COMPLETION_ROUTINE_CONTEXT) Context;
t = (PSTRIPE_WP) context->ParentVolume;
StripeWpCompositeVolumeCompletionRoutine(context, Status);
n = t->QueryNumMembers();
b = FALSE;
KeAcquireSpinLock(&t->_spinLock, &irql);
for (i = 0; i < n; i++) {
b = t->DecrementRequestCount(i) || b;
}
KeReleaseSpinLock(&t->_spinLock, irql);
if (b) {
t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext,
STATUS_SUCCESS);
}
}
VOID
StripeWpSyncCleanup(
IN PSWP_REBUILD_TP TransferPacket
)
/*++
Routine Description:
This is the cleanup routine for the initialize check data process.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PFT_COMPLETION_ROUTINE_CONTEXT context;
context = TransferPacket->Context;
delete TransferPacket;
StripeWpSyncFinalCompletion(context, STATUS_SUCCESS);
}
VOID
StripeWpSyncCompletionRoutine(
IN PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for an initialize check data request.
This routine is called over and over again until the volume
is completely initialized.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_REBUILD_TP transferPacket = (PSWP_REBUILD_TP) TransferPacket;
PSTRIPE_WP t = transferPacket->StripeWithParity;
NTSTATUS status = transferPacket->IoStatus.Status;
KIRQL irql;
ULONG parityMember;
if (!NT_SUCCESS(status)) {
// We can't get a VERIFY_REQUIRED because we put IrpFlags equal
// to SL_OVERRIDE_VERIFY_VOLUME.
ASSERT(status != STATUS_VERIFY_REQUIRED);
if (FsRtlIsTotalDeviceFailure(status)) {
if (!transferPacket->ReadPacket) {
KeAcquireSpinLock(&t->_spinLock, &irql);
transferPacket->TargetVolume->SetMemberState(Orphaned);
KeReleaseSpinLock(&t->_spinLock, irql);
}
// The initialize cannot continue.
t->_overlappedIoManager.ReleaseIoRegion(transferPacket);
StripeWpSyncCleanup(transferPacket);
return;
}
// Transfer the maximum amount that we can. This will always
// complete successfully and log bad sector errors for
// those sectors that it could not transfer.
t->MaxTransfer(transferPacket);
return;
}
transferPacket->Thread = PsGetCurrentThread();
if (transferPacket->ReadPacket) {
transferPacket->ReadPacket = FALSE;
TRANSFER(transferPacket);
return;
}
t->_overlappedIoManager.ReleaseIoRegion(transferPacket);
transferPacket->ReadPacket = TRUE;
transferPacket->Offset += t->_stripeSize;
if (transferPacket->Initialize) {
transferPacket->WhichMember = (transferPacket->WhichMember + 1)%
t->QueryNumMembers();
transferPacket->TargetVolume = t->GetMemberUnprotected(
transferPacket->WhichMember);
}
if (transferPacket->Offset < t->_memberSize) {
t->RegeneratePacket(transferPacket, TRUE);
return;
}
if (transferPacket->Initialize) {
KeAcquireSpinLock(&t->_spinLock, &irql);
t->_initializing = FALSE;
KeReleaseSpinLock(&t->_spinLock, irql);
t->MemberStateChangeNotification(t->GetMember(0));
} else {
KeAcquireSpinLock(&t->_spinLock, &irql);
transferPacket->TargetVolume->SetMemberState(Healthy);
KeReleaseSpinLock(&t->_spinLock, irql);
}
StripeWpSyncCleanup(transferPacket);
}
VOID
STRIPE_WP::StartSyncOperations(
IN FT_COMPLETION_ROUTINE CompletionRoutine,
IN PVOID Context
)
/*++
Routine Description:
This aroutine restarts any regenerate or initialize requests that
were suspended because of a reboot. The volume examines the member
state of all of its constituents and restarts any regenerations pending.
Arguments:
CompletionRoutine - Supplies the completion routine.
Context - Supplies the context for the completion routine.
Return Value:
None.
--*/
{
PFT_COMPLETION_ROUTINE_CONTEXT context;
ULONG i, n, numOrphans, numRegen;
KIRQL irql;
PFT_VOLUME vol;
PSWP_REBUILD_TP packet;
PVOID buffer;
LONG regenMember;
context = (PFT_COMPLETION_ROUTINE_CONTEXT)
ExAllocatePool(NonPagedPool,
sizeof(FT_COMPLETION_ROUTINE_CONTEXT));
if (!context) {
CompletionRoutine(Context, STATUS_INSUFFICIENT_RESOURCES);
return;
}
KeInitializeSpinLock(&context->SpinLock);
context->Status = STATUS_SUCCESS;
context->RefCount = 2;
context->CompletionRoutine = CompletionRoutine;
context->Context = Context;
context->ParentVolume = this;
// Kick off the recursive initialize.
COMPOSITE_FT_VOLUME::StartSyncOperations(
StripeWpCompositeVolumeCompletionRoutine, context);
// Make sure that all member are healthy.
numOrphans = numRegen = 0;
n = QueryNumMembers();
KeAcquireSpinLock(&_spinLock, &irql);
if (_syncExpected) {
_syncExpected = FALSE;
} else {
KeReleaseSpinLock(&_spinLock, irql);
StripeWpCompositeVolumeCompletionRoutine(context, STATUS_SUCCESS);
return;
}
for (i = 0; i < n; i++) {
vol = GetMemberUnprotected(i);
switch (vol->QueryMemberStateUnprotected()) {
case Orphaned:
numOrphans++;
break;
case Regenerating:
regenMember = i;
numRegen++;
break;
}
IncrementRequestCount(i);
}
if (_initializing) {
numRegen++;
regenMember = -1;
}
KeReleaseSpinLock(&_spinLock, irql);
if (numRegen != 1 || numOrphans > 0) {
StripeWpSyncFinalCompletion(context, STATUS_SUCCESS);
return;
}
packet = new SWP_REBUILD_TP;
if (packet && !packet->AllocateMdl(_stripeSize)) {
delete packet;
packet = NULL;
}
if (!packet) {
StripeWpSyncFinalCompletion(context, STATUS_INSUFFICIENT_RESOURCES);
return;
}
packet->Length = _stripeSize;
packet->Offset = 0;
packet->CompletionRoutine = StripeWpSyncCompletionRoutine;
packet->Thread = PsGetCurrentThread();
packet->IrpFlags = SL_OVERRIDE_VERIFY_VOLUME;
packet->ReadPacket = TRUE;
packet->MasterPacket = NULL;
packet->StripeWithParity = this;
packet->Context = context;
if (regenMember >= 0) {
packet->WhichMember = (ULONG) regenMember;
packet->Initialize = FALSE;
} else {
packet->WhichMember = 0;
packet->Initialize = TRUE;
}
packet->TargetVolume = GetMemberUnprotected(packet->WhichMember);
RegeneratePacket(packet, TRUE);
}
VOID
StartStripeRegeneration(
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description:
This routine is registered as a 'WaitingForOrphanIdle' routine or just
called outright when a regeneration is poised to take place which is
when there is a single orphan and there are no outstanding requests
on that orphan.
Arguments:
Context - Supplies the context.
Status - Ignored.
Return Value:
None.
--*/
{
PSTART_REGENERATION_CONTEXT context = (PSTART_REGENERATION_CONTEXT) Context;
PSTRIPE_WP stripeWp = context->StripeWp;
ULONG orphanNumber = stripeWp->_waitingOrphanNumber;
KIRQL oldIrql;
PFT_VOLUME vol;
BOOLEAN b;
KeAcquireSpinLock(&stripeWp->_spinLock, &oldIrql);
vol = stripeWp->GetMemberUnprotected(orphanNumber);
stripeWp->SetMemberUnprotected(orphanNumber, context->SpareVolume);
context->SpareVolume->SetMemberState(Regenerating);
stripeWp->IncrementRequestCount(orphanNumber);
stripeWp->_waitingForOrphanIdle = NULL;
KeReleaseSpinLock(&stripeWp->_spinLock, oldIrql);
if (vol != context->SpareVolume) {
FtpDisolveVolume(stripeWp->_extension, vol);
}
stripeWp->RegeneratePacket(context->Packet, TRUE);
ExFreePool(context);
}
BOOLEAN
STRIPE_WP::Regenerate(
IN OUT PFT_VOLUME SpareVolume,
IN FT_COMPLETION_ROUTINE CompletionRoutine,
IN PVOID Context
)
/*++
Routine Description:
This routine uses the given SpareVolume to rebuild after a
device failure. This routine returns FALSE if the SpareVolume
is not large enough or if this volume does not support
any redundancy. Returning TRUE from this routine implies that
the CompletionRoutine will be called when the operation is
complete.
Arguments:
SpareVolume - Supplies a spare volume onto which to rebuild.
CompletionRoutine - Supplies the completion routine.
Context - Supplies the context for the completion routine.
Return Value:
FALSE - Rebuild is not appropriate for this volume or the given
SpareVolume is not appropriate for a rebuild.
TRUE - The rebuild operation has been kicked off, the completion
routine will be called when the operation is complete.
--*/
{
KIRQL oldIrql;
ULONG i, n, numUnhealthy, orphanNumber;
PFT_VOLUME vol;
PSTART_REGENERATION_CONTEXT startContext;
PFT_COMPLETION_ROUTINE_CONTEXT context;
BOOLEAN b;
PSWP_REBUILD_TP packet;
if (SpareVolume->QueryVolumeSize() < _memberSize) {
return COMPOSITE_FT_VOLUME::Regenerate(SpareVolume,
CompletionRoutine,
Context);
}
startContext = (PSTART_REGENERATION_CONTEXT)
ExAllocatePool(NonPagedPool,
sizeof(START_REGENERATION_CONTEXT));
context = (PFT_COMPLETION_ROUTINE_CONTEXT)
ExAllocatePool(NonPagedPool,
sizeof(FT_COMPLETION_ROUTINE_CONTEXT));
packet = new SWP_REBUILD_TP;
if (packet && !packet->AllocateMdl(_stripeSize)) {
delete packet;
packet = NULL;
}
if (!context || !startContext || !packet) {
if (context) {
ExFreePool(context);
}
if (startContext) {
ExFreePool(startContext);
}
if (packet) {
delete packet;
}
return COMPOSITE_FT_VOLUME::Regenerate(SpareVolume,
CompletionRoutine,
Context);
}
startContext->StripeWp = this;
startContext->SpareVolume = SpareVolume;
startContext->CompletionRoutine = CompletionRoutine;
startContext->Context = Context;
startContext->Packet = packet;
KeInitializeSpinLock(&context->SpinLock);
context->Status = STATUS_SUCCESS;
context->RefCount = 1;
context->CompletionRoutine = CompletionRoutine;
context->Context = Context;
context->ParentVolume = this;
packet->Length = _stripeSize;
packet->Offset = 0;
packet->CompletionRoutine = StripeWpSyncCompletionRoutine;
packet->Thread = PsGetCurrentThread();
packet->IrpFlags = SL_OVERRIDE_VERIFY_VOLUME;
packet->ReadPacket = TRUE;
packet->StripeWithParity = this;
packet->Context = context;
packet->Initialize = FALSE;
numUnhealthy = 0;
orphanNumber = 0;
n = QueryNumMembers();
KeAcquireSpinLock(&_spinLock, &oldIrql);
for (i = 0; i < n; i++) {
vol = GetMemberUnprotected(i);
if (vol->QueryMemberStateUnprotected() != Healthy) {
numUnhealthy++;
orphanNumber = i;
}
}
vol = GetMemberUnprotected(orphanNumber);
if (numUnhealthy != 1 ||
vol->QueryMemberStateUnprotected() != Orphaned ||
_waitingForOrphanIdle ||
_initializing ||
_syncExpected) {
KeReleaseSpinLock(&_spinLock, oldIrql);
ExFreePool(context);
ExFreePool(startContext);
delete packet;
return COMPOSITE_FT_VOLUME::Regenerate(SpareVolume,
CompletionRoutine,
Context);
}
packet->WhichMember = orphanNumber;
packet->TargetVolume = SpareVolume;
for (i = 0; i < n; i++) {
if (i != orphanNumber) {
IncrementRequestCount(i);
}
}
if (_requestCount[orphanNumber] != 0) {
_waitingForOrphanIdle = StartStripeRegeneration;
_waitingForOrphanIdleContext = startContext;
_waitingOrphanNumber = orphanNumber;
KeReleaseSpinLock(&_spinLock, oldIrql);
return TRUE;
}
SetMemberUnprotected(orphanNumber, SpareVolume);
SpareVolume->SetMemberState(Regenerating);
IncrementRequestCount(orphanNumber);
KeReleaseSpinLock(&_spinLock, oldIrql);
ExFreePool(startContext);
if (vol != SpareVolume) {
FtpDisolveVolume(_extension, vol);
}
RegeneratePacket(packet, TRUE);
return TRUE;
}
BOOLEAN
STRIPE_WP::IsCreatingCheckData(
)
/*++
Routine Description:
This routine states whether or not this VOLUME is currently creating
check data. The state refers to this volume and does not reflect the
state of volumes contained within this volume.
Arguments:
None.
Return Value:
FALSE - This volume is not creating check data (although a child volume
may be).
TRUE - This volume is creating check data.
--*/
{
KIRQL irql;
ULONG r;
KeAcquireSpinLock(&_spinLock, &irql);
r = _initializing;
KeReleaseSpinLock(&_spinLock, irql);
return (r > 0) ? TRUE : FALSE;
}
VOID
STRIPE_WP::SetCheckDataDirty(
)
/*++
Routine Description:
This routine marks the check data as dirty so that when
'StartSyncOperations' is called, the check data will be
initialized.
Arguments:
None.
Return Value:
None.
--*/
{
KIRQL irql;
PFT_VOLUME mem;
KeAcquireSpinLock(&_spinLock, &irql);
if (_syncExpected) {
mem = GetMemberUnprotected(0);
if (mem->QueryMemberState() == Healthy) {
mem->SetMemberState(Initializing);
mem->SetMemberStateWithoutNotification(Healthy);
_initializing = TRUE;
}
} else {
ASSERT(0);
}
KeReleaseSpinLock(&_spinLock, irql);
}
LONGLONG
STRIPE_WP::QueryVolumeSize(
)
/*++
Routine Description:
Returns the number of bytes on the entire volume.
Arguments:
None.
Return Value:
The volume size in bytes.
--*/
{
return _volumeSize;
}
FT_TYPE
STRIPE_WP::QueryVolumeType(
)
/*++
Routine Description:
Returns the volume type.
Arguments:
None.
Return Value:
StripeWithParity - A stripe set with parity.
--*/
{
return StripeWithParity;
}
FT_STATE
STRIPE_WP::QueryVolumeState(
)
/*++
Routine Description:
Returns the state of the volume.
Arguments:
None.
Return Value:
FT_STATE
--*/
{
FT_STATE state;
ULONG n, i;
KIRQL irql;
FT_PARTITION_STATE partState;
state = FtStateOk;
n = QueryNumMembers();
KeAcquireSpinLock(&_spinLock, &irql);
if (_initializing) {
state = FtInitializing;
}
for (i = 0; i < n; i++) {
partState = GetMemberUnprotected(i)->QueryMemberStateUnprotected();
switch (partState) {
case Healthy:
break;
case Orphaned:
if (state == FtStateOk) {
state = FtHasOrphan;
} else {
state = FtDisabled;
}
break;
case Regenerating:
if (state == FtStateOk) {
state = FtRegenerating;
} else {
state = FtDisabled;
}
break;
}
}
KeReleaseSpinLock(&_spinLock, irql);
return state;
}
BOOLEAN
STRIPE_WP::OrphanPartition(
IN PPARTITION Partition
)
/*++
Routine Description:
This routine orphans the given partition if possible. The partition is
not orphaned unless the FT_VOLUME has redundancy built in to it.
If the partition cannot be orphaned then this routine returns FALSE.
Arguments:
Partition - Supplies the partition to orphan.
Return Value:
FALSE - The given partition was not orphaned.
TRUE - The given partition was orphaned.
--*/
{
ULONG i, n;
KIRQL irql;
PFT_VOLUME vol;
n = QueryNumMembers();
KeAcquireSpinLock(&_spinLock, &irql);
for (i = 0; i < n; i++) {
vol = GetMemberUnprotected(i);
if (vol->FindPartition(Partition->QueryDiskNumber(),
Partition->QueryPartitionNumber()) &&
!vol->OrphanPartition(Partition)) {
vol->SetMemberState(Orphaned);
KeReleaseSpinLock(&_spinLock, irql);
return TRUE;
}
}
return FALSE;
}
STRIPE_WP::STRIPE_WP(
IN PDEVICE_EXTENSION Extension,
IN ULONG StripeSize
)
/*++
Routine Description:
Constructor.
Arguments:
None.
Return Value:
None.
--*/
{
_extension = Extension;
_stripeSize = StripeSize;
_requestCount = NULL;
_ePacket = NULL;
_eRegeneratePacket = NULL;
_eRecoverPacket = NULL;
}
STRIPE_WP::~STRIPE_WP(
)
/*++
Routine Description:
Routine called to cleanup resources being used by the object.
Arguments:
None.
Return Value:
None.
--*/
{
if (_requestCount) {
ExFreePool(_requestCount);
_requestCount = NULL;
}
if (_ePacket) {
delete _ePacket;
_ePacket = NULL;
}
if (_eRegeneratePacket) {
delete _eRegeneratePacket;
_eRegeneratePacket = NULL;
}
if (_eRecoverPacket) {
delete _eRecoverPacket;
_eRecoverPacket = NULL;
}
}
BOOLEAN
STRIPE_WP::DecrementRequestCount(
IN ULONG MemberNumber
)
/*++
Routine Description:
This routine decrements the request count and indicated that
the _waitingForOrphanIdle routine should be called if the request count
goes to zero and the member is orphaned.
Arguments:
MemberNumber - Supplies the member number.
Return Value:
FALSE - The caller should not call the _waitingForOrphanIdle routine.
TRUE - The caller needs to call the _waitingForOrphanIdle routine.
--*/
{
if (--_requestCount[MemberNumber] == 0 &&
GetMemberUnprotected(MemberNumber)->QueryMemberStateUnprotected() ==
Orphaned &&
_waitingForOrphanIdle &&
_waitingOrphanNumber == MemberNumber) {
return TRUE;
}
if (_requestCount[MemberNumber] < 0) {
DbgBreakPoint();
}
return FALSE;
}
VOID
StripeWpParallelTransferCompletionRoutine(
IN PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
Completion routine for STRIPE_WP::Transfer function.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_TP transferPacket = (PSWP_TP) TransferPacket;
PSTRIPE_WP t = (PSTRIPE_WP) transferPacket->StripeWithParity;
PTRANSFER_PACKET masterPacket = transferPacket->MasterPacket;
NTSTATUS status = transferPacket->IoStatus.Status;
KIRQL irql;
PLIST_ENTRY l;
PTRANSFER_PACKET p;
LONG count;
BOOLEAN b, serviceQueue;
PSWP_WRITE_TP writePacket;
if (NT_SUCCESS(status)) {
KeAcquireSpinLock(&masterPacket->SpinLock, &irql);
if (NT_SUCCESS(masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Information +=
transferPacket->IoStatus.Information;
}
if (transferPacket->OneReadFailed &&
FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Status = status;
}
} else {
// Should we orphan the drive?
if (transferPacket->ReadPacket &&
!transferPacket->OneReadFailed &&
status != STATUS_VERIFY_REQUIRED) {
if (FsRtlIsTotalDeviceFailure(status)) {
KeAcquireSpinLock(&t->_spinLock, &irql);
transferPacket->TargetVolume->SetMemberState(Orphaned);
KeReleaseSpinLock(&t->_spinLock, irql);
t->RegeneratePacket(transferPacket, TRUE);
return;
}
// Is this something that we should retry for bad sectors?
if (transferPacket->Mdl) {
transferPacket->OneReadFailed = TRUE;
t->Recover(transferPacket);
return;
}
}
KeAcquireSpinLock(&masterPacket->SpinLock, &irql);
if (FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Status = status;
masterPacket->IoStatus.Information = 0;
}
}
count = --masterPacket->RefCount;
KeReleaseSpinLock(&masterPacket->SpinLock, irql);
serviceQueue = FALSE;
KeAcquireSpinLock(&t->_spinLock, &irql);
b = t->DecrementRequestCount(transferPacket->WhichMember);
if (!transferPacket->ReadPacket) {
writePacket = (PSWP_WRITE_TP) transferPacket;
if (writePacket->ParityPacket.TargetVolume) {
b = t->DecrementRequestCount(writePacket->ParityMember) || b;
}
}
if (t->_ePacketInUse && !t->_ePacketQueueBeingServiced) {
t->_ePacketQueueBeingServiced = TRUE;
serviceQueue = TRUE;
}
KeReleaseSpinLock(&t->_spinLock, irql);
if (b) {
t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext,
STATUS_SUCCESS);
}
delete transferPacket;
if (!count) {
masterPacket->CompletionRoutine(masterPacket);
}
if (serviceQueue) {
for (;;) {
KeAcquireSpinLock(&t->_spinLock, &irql);
if (IsListEmpty(&t->_ePacketQueue)) {
t->_ePacketQueueBeingServiced = FALSE;
KeReleaseSpinLock(&t->_spinLock, irql);
break;
}
l = RemoveHeadList(&t->_ePacketQueue);
KeReleaseSpinLock(&t->_spinLock, irql);
p = CONTAINING_RECORD(l, TRANSFER_PACKET, QueueEntry);
if (!t->LaunchParallel(p)) {
KeAcquireSpinLock(&t->_spinLock, &irql);
if (t->_ePacketInUse) {
InsertHeadList(&t->_ePacketQueue, l);
t->_ePacketQueueBeingServiced = FALSE;
KeReleaseSpinLock(&t->_spinLock, irql);
} else {
t->_ePacketInUse = TRUE;
KeReleaseSpinLock(&t->_spinLock, irql);
t->LaunchSequential(p);
KeAcquireSpinLock(&t->_spinLock, &irql);
if (!t->_ePacketInUse) {
KeReleaseSpinLock(&t->_spinLock, irql);
continue;
}
t->_ePacketQueueBeingServiced = FALSE;
KeReleaseSpinLock(&t->_spinLock, irql);
}
break;
}
}
}
}
BOOLEAN
STRIPE_WP::LaunchParallel(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This routine launches a transfer packet in parallel accross the
stripe members.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
FALSE - Insufficient resources.
TRUE - Success.
--*/
{
LONGLONG offset, whichStripe, whichRow, off;
ULONG length, stripeRemainder, numRequests, arraySize;
ULONG whichMember, parityStripe, len;
PSWP_TP p;
ULONG i;
PCHAR vp;
LIST_ENTRY q;
PLIST_ENTRY l;
// Compute the number of pieces for this transfer.
offset = TransferPacket->Offset;
length = TransferPacket->Length;
stripeRemainder = _stripeSize - (ULONG) (offset%_stripeSize);
if (length > stripeRemainder) {
length -= stripeRemainder;
numRequests = length/_stripeSize;
length -= numRequests*_stripeSize;
if (length) {
numRequests += 2;
} else {
numRequests++;
}
} else {
numRequests = 1;
}
KeInitializeSpinLock(&TransferPacket->SpinLock);
TransferPacket->IoStatus.Status = STATUS_SUCCESS;
TransferPacket->IoStatus.Information = 0;
TransferPacket->RefCount = numRequests;
length = TransferPacket->Length;
if (TransferPacket->Mdl && numRequests > 1) {
vp = (PCHAR) MmGetMdlVirtualAddress(TransferPacket->Mdl);
}
whichStripe = offset/_stripeSize;
arraySize = QueryNumMembers();
InitializeListHead(&q);
for (i = 0; i < numRequests; i++, whichStripe++) {
whichRow = whichStripe/(arraySize - 1);
whichMember = (ULONG) (whichStripe%(arraySize - 1));
parityStripe = (ULONG) (whichRow%arraySize);
if (whichMember >= parityStripe) {
whichMember++;
}
if (i == 0) {
off = whichRow*_stripeSize + offset%_stripeSize;
len = stripeRemainder > length ? length : stripeRemainder;
} else if (i == numRequests - 1) {
off = whichRow*_stripeSize;
len = length;
} else {
off = whichRow*_stripeSize;
len = _stripeSize;
}
length -= len;
if (TransferPacket->ReadPacket) {
p = new SWP_TP;
} else {
p = new SWP_WRITE_TP;
if (p && !((PSWP_WRITE_TP) p)->AllocateMdls(len)) {
delete p;
p = NULL;
}
}
if (p) {
if (TransferPacket->Mdl && numRequests > 1) {
if (p->AllocateMdl(vp, len)) {
IoBuildPartialMdl(TransferPacket->Mdl, p->Mdl, vp, len);
} else {
delete p;
p = NULL;
}
vp += len;
} else {
p->Mdl = TransferPacket->Mdl;
}
}
if (!p) {
while (!IsListEmpty(&q)) {
l = RemoveHeadList(&q);
p = CONTAINING_RECORD(l, SWP_TP, QueueEntry);
delete p;
}
return FALSE;
}
p->Length = len;
p->Offset = off;
p->CompletionRoutine = StripeWpParallelTransferCompletionRoutine;
p->Thread = TransferPacket->Thread;
p->IrpFlags = TransferPacket->IrpFlags;
p->ReadPacket = TransferPacket->ReadPacket;
p->MasterPacket = TransferPacket;
p->StripeWithParity = this;
p->WhichMember = whichMember;
p->SavedCompletionRoutine = StripeWpParallelTransferCompletionRoutine;
p->OneReadFailed = FALSE;
InsertTailList(&q, &p->QueueEntry);
}
while (!IsListEmpty(&q)) {
l = RemoveHeadList(&q);
p = CONTAINING_RECORD(l, SWP_TP, QueueEntry);
ASSERT(p->ReadPacket == TransferPacket->ReadPacket);
if (p->ReadPacket) {
ReadPacket(p);
} else {
WritePacket((PSWP_WRITE_TP) p);
}
}
return TRUE;
}
VOID
StripeWpSequentialTransferCompletionRoutine(
IN PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
Completion routine for STRIPE::Transfer function.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_TP transferPacket = (PSWP_TP) TransferPacket;
PTRANSFER_PACKET masterPacket = transferPacket->MasterPacket;
NTSTATUS status = transferPacket->IoStatus.Status;
PSTRIPE_WP t = transferPacket->StripeWithParity;
LONGLONG rowNumber, stripeNumber, masterOffset;
KIRQL irql;
PLIST_ENTRY l;
PTRANSFER_PACKET p;
ULONG parityStripe;
BOOLEAN b;
PSWP_WRITE_TP writePacket;
if (NT_SUCCESS(status)) {
if (NT_SUCCESS(masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Information +=
transferPacket->IoStatus.Information;
}
if (transferPacket->OneReadFailed &&
FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Status = status;
}
} else {
// Should we orphan the drive?
if (transferPacket->ReadPacket &&
!transferPacket->OneReadFailed &&
status != STATUS_VERIFY_REQUIRED) {
if (FsRtlIsTotalDeviceFailure(status)) {
KeAcquireSpinLock(&t->_spinLock, &irql);
transferPacket->TargetVolume->SetMemberState(Orphaned);
KeReleaseSpinLock(&t->_spinLock, irql);
t->RegeneratePacket(transferPacket, TRUE);
return;
}
// Is this something that we should retry for bad sectors.
if (transferPacket->Mdl) {
transferPacket->OneReadFailed = TRUE;
t->Recover(transferPacket);
return;
}
}
if (FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Status = status;
masterPacket->IoStatus.Information = 0;
}
}
MmPrepareMdlForReuse(transferPacket->Mdl);
KeAcquireSpinLock(&t->_spinLock, &irql);
b = t->DecrementRequestCount(transferPacket->WhichMember);
if (!transferPacket->ReadPacket) {
writePacket = (PSWP_WRITE_TP) transferPacket;
if (writePacket->ParityPacket.TargetVolume) {
b = t->DecrementRequestCount(writePacket->ParityMember) || b;
}
}
KeReleaseSpinLock(&t->_spinLock, irql);
if (b) {
t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext,
STATUS_SUCCESS);
}
t->_overlappedIoManager.ReleaseIoRegion(transferPacket);
rowNumber = transferPacket->Offset/t->_stripeSize;
parityStripe = (ULONG) rowNumber%t->QueryNumMembers();
stripeNumber = rowNumber*(t->QueryNumMembers() - 1) +
transferPacket->WhichMember;
if (transferPacket->WhichMember > parityStripe) {
stripeNumber--;
}
masterOffset = stripeNumber*t->_stripeSize +
transferPacket->Offset%t->_stripeSize +
transferPacket->Length;
if (masterOffset == masterPacket->Offset + masterPacket->Length) {
masterPacket->CompletionRoutine(masterPacket);
KeAcquireSpinLock(&t->_spinLock, &irql);
if (t->_ePacketQueueBeingServiced) {
t->_ePacketInUse = FALSE;
KeReleaseSpinLock(&t->_spinLock, irql);
return;
}
t->_ePacketQueueBeingServiced = TRUE;
KeReleaseSpinLock(&t->_spinLock, irql);
for (;;) {
KeAcquireSpinLock(&t->_spinLock, &irql);
if (IsListEmpty(&t->_ePacketQueue)) {
t->_ePacketInUse = FALSE;
t->_ePacketQueueBeingServiced = FALSE;
KeReleaseSpinLock(&t->_spinLock, irql);
break;
}
l = RemoveHeadList(&t->_ePacketQueue);
KeReleaseSpinLock(&t->_spinLock, irql);
p = CONTAINING_RECORD(l, TRANSFER_PACKET, QueueEntry);
if (!t->LaunchParallel(p)) {
t->LaunchSequential(p);
KeAcquireSpinLock(&t->_spinLock, &irql);
if (!t->_ePacketInUse) {
KeReleaseSpinLock(&t->_spinLock, irql);
continue;
}
t->_ePacketQueueBeingServiced = FALSE;
KeReleaseSpinLock(&t->_spinLock, irql);
break;
}
}
return;
}
transferPacket->WhichMember++;
if (transferPacket->WhichMember == t->QueryNumMembers()) {
transferPacket->WhichMember = 0;
rowNumber++;
} else if (transferPacket->WhichMember == parityStripe) {
transferPacket->WhichMember++;
if (transferPacket->WhichMember == t->QueryNumMembers()) {
transferPacket->WhichMember = 1;
rowNumber++;
}
}
transferPacket->Offset = rowNumber*t->_stripeSize;
transferPacket->Length = t->_stripeSize;
if (masterOffset + transferPacket->Length >
masterPacket->Offset + masterPacket->Length) {
transferPacket->Length = (ULONG) (masterPacket->Offset +
masterPacket->Length - masterOffset);
}
IoBuildPartialMdl(masterPacket->Mdl, transferPacket->Mdl,
(PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) +
(ULONG) (masterOffset - masterPacket->Offset),
transferPacket->Length);
if (transferPacket->ReadPacket) {
t->ReadPacket(transferPacket);
} else {
t->WritePacket((PSWP_WRITE_TP) transferPacket);
}
}
VOID
STRIPE_WP::LaunchSequential(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This routine launches a transfer packet sequentially accross the
stripe members using the emergency packet.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
FALSE - Insufficient resources.
TRUE - Success.
--*/
{
PSWP_WRITE_TP p;
LONGLONG offset, whichStripe, whichRow, o;
ULONG whichMember, l, stripeRemainder, arraySize, parityStripe;
TransferPacket->IoStatus.Status = STATUS_SUCCESS;
TransferPacket->IoStatus.Information = 0;
offset = TransferPacket->Offset;
p = _ePacket;
arraySize = QueryNumMembers();
stripeRemainder = _stripeSize - (ULONG) (offset%_stripeSize);
whichStripe = offset/_stripeSize;
whichRow = whichStripe/(arraySize - 1);
whichMember = (ULONG) (whichStripe%(arraySize - 1));
parityStripe = (ULONG) (whichRow%arraySize);
if (whichMember >= parityStripe) {
whichMember++;
}
o = whichRow*_stripeSize + offset%_stripeSize;
l = stripeRemainder;
if (l > TransferPacket->Length) {
l = TransferPacket->Length;
}
IoBuildPartialMdl(TransferPacket->Mdl, p->Mdl,
MmGetMdlVirtualAddress(TransferPacket->Mdl), l);
p->Length = l;
p->Offset = o;
p->CompletionRoutine = StripeWpSequentialTransferCompletionRoutine;
p->Thread = TransferPacket->Thread;
p->IrpFlags = TransferPacket->IrpFlags;
p->ReadPacket = TransferPacket->ReadPacket;
p->MasterPacket = TransferPacket;
p->StripeWithParity = this;
p->WhichMember = whichMember;
p->SavedCompletionRoutine = StripeWpSequentialTransferCompletionRoutine;
p->OneReadFailed = FALSE;
if (p->ReadPacket) {
ReadPacket(p);
} else {
WritePacket(p);
}
}
VOID
STRIPE_WP::ReadPacket(
IN OUT PSWP_TP TransferPacket
)
/*++
Routine Description:
This routine takes a packet that is restricted to a single
stripe region and reads that data.
Arguments:
TransferPacket - Supplies the main read packet.
Return Value:
None.
--*/
{
PTRANSFER_PACKET masterPacket = TransferPacket->MasterPacket;
KIRQL irql;
KeAcquireSpinLock(&_spinLock, &irql);
IncrementRequestCount(TransferPacket->WhichMember);
TransferPacket->TargetVolume = GetMemberUnprotected(TransferPacket->WhichMember);
if (TransferPacket->TargetVolume->QueryMemberStateUnprotected() != Healthy ||
masterPacket->SpecialRead == TP_SPECIAL_READ_SECONDARY) {
KeReleaseSpinLock(&_spinLock, irql);
RegeneratePacket(TransferPacket, TRUE);
} else {
KeReleaseSpinLock(&_spinLock, irql);
TRANSFER(TransferPacket);
}
}
VOID
StripeWpWritePhase31(
IN OUT PTRANSFER_PACKET Packet
)
/*++
Routine Description:
This is the completion routine for the final data write and the
final parity write of the write process. This packet's master packet
is the original write packet. This write packet exists because the data
has to be copied from the original write packet so that parity
may be correctly computed.
Arguments:
Packet - Supplies either the write or update parity packet.
Return Value:
None.
--*/
{
PSWP_WRITE_TP masterPacket;
PSTRIPE_WP t;
NTSTATUS status;
KIRQL irql;
LONG count;
BOOLEAN b;
masterPacket = CONTAINING_RECORD(Packet, SWP_WRITE_TP, ParityPacket);
t = masterPacket->StripeWithParity;
status = Packet->IoStatus.Status;
KeAcquireSpinLock(&masterPacket->SpinLock, &irql);
if (NT_SUCCESS(status)) {
if (NT_SUCCESS(masterPacket->IoStatus.Status)) {
masterPacket->IoStatus = Packet->IoStatus;
}
} else {
if (FsRtlIsTotalDeviceFailure(status) &&
status != STATUS_VERIFY_REQUIRED) {
KeAcquireSpinLock(&t->_spinLock, &irql);
Packet->TargetVolume->SetMemberState(Orphaned);
KeReleaseSpinLock(&t->_spinLock, irql);
} else if (FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Status = status;
masterPacket->IoStatus.Information = 0;
}
}
count = --masterPacket->RefCount;
KeReleaseSpinLock(&masterPacket->SpinLock, irql);
if (!count) {
masterPacket->CompletionRoutine(masterPacket);
}
}
VOID
StripeWpWritePhase30(
IN OUT PTRANSFER_PACKET Packet
)
/*++
Routine Description:
This is the completion routine for the final data write and the
final parity write of the write process. This packet's master packet
is the original write packet. This write packet exists because the data
has to be copied from the original write packet so that parity
may be correctly computed.
Arguments:
Packet - Supplies either the write or update parity packet.
Return Value:
None.
--*/
{
PSWP_TP writePacket = (PSWP_TP) Packet;
PSTRIPE_WP t = writePacket->StripeWithParity;
PSWP_WRITE_TP masterPacket = (PSWP_WRITE_TP) writePacket->MasterPacket;
NTSTATUS status = writePacket->IoStatus.Status;
KIRQL irql;
LONG count;
KeAcquireSpinLock(&masterPacket->SpinLock, &irql);
if (NT_SUCCESS(status)) {
if (NT_SUCCESS(masterPacket->IoStatus.Status)) {
masterPacket->IoStatus = Packet->IoStatus;
}
} else {
if (FsRtlIsTotalDeviceFailure(status) &&
status != STATUS_VERIFY_REQUIRED) {
KeAcquireSpinLock(&t->_spinLock, &irql);
writePacket->TargetVolume->SetMemberState(Orphaned);
KeReleaseSpinLock(&t->_spinLock, irql);
} else if (FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Status = status;
masterPacket->IoStatus.Information = 0;
}
}
count = --masterPacket->RefCount;
KeReleaseSpinLock(&masterPacket->SpinLock, irql);
if (!count) {
masterPacket->CompletionRoutine(masterPacket);
}
}
VOID
StripeWpWritePhase2(
IN OUT PTRANSFER_PACKET ReadPacket
)
/*++
Routine Description:
This routine describes phase 3 of the write process. The region
that we are about to write has been preread. If the read was
successful then queue write and parity requests. If the read
was not successful then propogate the error and cleanup.
Arguments:
TransferPacket - Supplies the read packet.
Return Value:
None.
--*/
{
PSWP_TP readPacket = (PSWP_TP) ReadPacket;
PSTRIPE_WP t = readPacket->StripeWithParity;
PSWP_WRITE_TP masterPacket = (PSWP_WRITE_TP) readPacket->MasterPacket;
PPARITY_TP parityPacket = &masterPacket->ParityPacket;
PSWP_TP writePacket = &masterPacket->ReadWritePacket;
NTSTATUS status;
KIRQL irql;
FT_PARTITION_STATE state;
status = readPacket->IoStatus.Status;
if (!NT_SUCCESS(status)) {
if (!readPacket->OneReadFailed &&
FsRtlIsTotalDeviceFailure(status) &&
status != STATUS_VERIFY_REQUIRED) {
// Orphan this unit and then try again with a regenerate.
KeAcquireSpinLock(&t->_spinLock, &irql);
readPacket->TargetVolume->SetMemberState(Orphaned);
KeReleaseSpinLock(&t->_spinLock, irql);
readPacket->OneReadFailed = TRUE;
masterPacket->CompletionRoutine = StripeWpWritePhase1;
t->_overlappedIoManager.PromoteToAllMembers(masterPacket);
return;
}
masterPacket->IoStatus = readPacket->IoStatus;
masterPacket->CompletionRoutine(masterPacket);
return;
}
KeInitializeSpinLock(&masterPacket->SpinLock);
masterPacket->IoStatus.Status = STATUS_SUCCESS;
masterPacket->IoStatus.Information = 0;
writePacket->Mdl = masterPacket->WriteMdl;
writePacket->CompletionRoutine = StripeWpWritePhase30;
writePacket->ReadPacket = FALSE;
parityPacket->Mdl = masterPacket->ReadAndParityMdl;
parityPacket->CompletionRoutine = StripeWpWritePhase31;
if (masterPacket->TargetState != Orphaned) {
RtlCopyMemory(MmGetSystemAddressForMdl(writePacket->Mdl),
MmGetSystemAddressForMdl(masterPacket->Mdl),
writePacket->Length);
if (parityPacket->TargetVolume) {
FtpComputeParity(MmGetSystemAddressForMdl(parityPacket->Mdl),
MmGetSystemAddressForMdl(writePacket->Mdl),
parityPacket->Length);
masterPacket->RefCount = 2;
t->_parityIoManager.UpdateParity(parityPacket);
} else {
masterPacket->RefCount = 1;
}
TRANSFER(writePacket);
} else if (parityPacket->TargetVolume) {
FtpComputeParity(MmGetSystemAddressForMdl(parityPacket->Mdl),
MmGetSystemAddressForMdl(masterPacket->Mdl),
readPacket->Length);
masterPacket->RefCount = 1;
t->_parityIoManager.UpdateParity(parityPacket);
} else {
masterPacket->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
masterPacket->IoStatus.Information = 0;
masterPacket->CompletionRoutine(masterPacket);
}
}
VOID
StripeWpWritePhase1(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This routine describes phase 2 of the write process. This io
region has been acquired. We now send out the read packet and
wait until it completes.
Arguments:
TransferPacket - Supplies the main write packet.
Return Value:
None.
--*/
{
PSWP_WRITE_TP transferPacket = (PSWP_WRITE_TP) TransferPacket;
PSTRIPE_WP t = transferPacket->StripeWithParity;
PSWP_TP readPacket;
PPARITY_TP parityPacket;
transferPacket->CompletionRoutine = transferPacket->SavedCompletionRoutine;
parityPacket = &transferPacket->ParityPacket;
if (parityPacket->TargetVolume) {
t->_parityIoManager.StartReadForUpdateParity(
parityPacket->Offset, parityPacket->Length,
parityPacket->TargetVolume, parityPacket->Thread,
parityPacket->IrpFlags);
}
readPacket = &transferPacket->ReadWritePacket;
readPacket->CompletionRoutine = StripeWpWritePhase2;
if (readPacket->OneReadFailed) {
t->RegeneratePacket(readPacket, FALSE);
} else {
TRANSFER(readPacket);
}
}
VOID
STRIPE_WP::WritePacket(
IN OUT PSWP_WRITE_TP TransferPacket
)
/*++
Routine Description:
This routine takes a packet that is restricted to a single
stripe region and writes out that data along with the parity.
Arguments:
TransferPacket - Supplies the main write packet.
Return Value:
None.
--*/
{
ULONG parityMember;
KIRQL irql;
FT_PARTITION_STATE state, parityState;
PSWP_TP readPacket;
PPARITY_TP parityPacket;
parityMember = (ULONG) ((TransferPacket->Offset/_stripeSize)%
QueryNumMembers());
KeAcquireSpinLock(&_spinLock, &irql);
TransferPacket->TargetVolume =
GetMemberUnprotected(TransferPacket->WhichMember);
state = TransferPacket->TargetVolume->QueryMemberStateUnprotected();
IncrementRequestCount(TransferPacket->WhichMember);
parityState = GetMemberUnprotected(parityMember)->
QueryMemberStateUnprotected();
if (parityState != Orphaned) {
IncrementRequestCount(parityMember);
}
KeReleaseSpinLock(&_spinLock, irql);
readPacket = &TransferPacket->ReadWritePacket;
readPacket->Mdl = TransferPacket->ReadAndParityMdl;
readPacket->Length = TransferPacket->Length;
readPacket->Offset = TransferPacket->Offset;
readPacket->TargetVolume = TransferPacket->TargetVolume;
readPacket->Thread = TransferPacket->Thread;
readPacket->IrpFlags = TransferPacket->IrpFlags;
readPacket->ReadPacket = TRUE;
readPacket->MasterPacket = TransferPacket;
readPacket->StripeWithParity = this;
readPacket->WhichMember = TransferPacket->WhichMember;
readPacket->OneReadFailed = FALSE;
parityPacket = &TransferPacket->ParityPacket;
parityPacket->Length = TransferPacket->Length;
parityPacket->Offset = TransferPacket->Offset;
if (parityState != Orphaned) {
parityPacket->TargetVolume = GetMemberUnprotected(parityMember);
} else {
parityPacket->TargetVolume = NULL;
}
parityPacket->Thread = TransferPacket->Thread;
parityPacket->IrpFlags = TransferPacket->IrpFlags;
parityPacket->ReadPacket = FALSE;
TransferPacket->CompletionRoutine = StripeWpWritePhase1;
TransferPacket->TargetState = state;
TransferPacket->ParityMember = parityMember;
if (state == Healthy) {
_overlappedIoManager.AcquireIoRegion(TransferPacket, FALSE);
} else {
readPacket->OneReadFailed = TRUE;
_overlappedIoManager.AcquireIoRegion(TransferPacket, TRUE);
}
}
VOID
StripeWpSequentialRegenerateCompletion(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine a regenerate operation where all of
the reads are being performed sequentially.
Arguments:
TransferPacket - Supplies the completed transfer packet.
Return Value:
None.
--*/
{
PSWP_REGENERATE_TP transferPacket = (PSWP_REGENERATE_TP) TransferPacket;
PSWP_TP masterPacket = transferPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = transferPacket->IoStatus.Status;
KIRQL irql;
ULONG count;
PLIST_ENTRY l;
PTRANSFER_PACKET packet;
ULONG i, n;
BOOLEAN b;
ULONG parityMember;
if (NT_SUCCESS(status)) {
if (NT_SUCCESS(masterPacket->IoStatus.Status)) {
masterPacket->IoStatus = transferPacket->IoStatus;
}
} else {
if (FsRtlIsTotalDeviceFailure(status) &&
status != STATUS_VERIFY_REQUIRED) {
KeAcquireSpinLock(&t->_spinLock, &irql);
transferPacket->TargetVolume->SetMemberState(Orphaned);
KeReleaseSpinLock(&t->_spinLock, irql);
}
if (FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Status = status;
masterPacket->IoStatus.Information = 0;
}
}
count = (ULONG) (--masterPacket->RefCount);
if (masterPacket->Mdl) {
FtpComputeParity(MmGetSystemAddressForMdl(masterPacket->Mdl),
MmGetSystemAddressForMdl(transferPacket->Mdl),
masterPacket->Length);
}
n = t->QueryNumMembers();
if (count) {
transferPacket->WhichMember++;
if (transferPacket->WhichMember == masterPacket->WhichMember) {
transferPacket->WhichMember++;
}
transferPacket->TargetVolume = t->GetMemberUnprotected(
transferPacket->WhichMember);
TRANSFER(transferPacket);
return;
}
masterPacket->CompletionRoutine(masterPacket);
b = FALSE;
KeAcquireSpinLock(&t->_spinLock, &irql);
for (i = 0; i < n; i++) {
b = t->DecrementRequestCount(i) || b;
}
if (IsListEmpty(&t->_eRegeneratePacketQueue)) {
t->_eRegeneratePacketInUse = FALSE;
packet = NULL;
} else {
l = RemoveHeadList(&t->_eRegeneratePacketQueue);
packet = CONTAINING_RECORD(l, SWP_TP, QueueEntry);
}
KeReleaseSpinLock(&t->_spinLock, irql);
if (b) {
t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext,
STATUS_SUCCESS);
}
if (packet) {
packet->CompletionRoutine(packet);
}
}
VOID
StripeWpSequentialEmergencyCompletion(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine after waiting for an emergency
regenerate buffer.
Arguments:
TransferPacket - Supplies the completed transfer packet.
Return Value:
None.
--*/
{
PSWP_TP transferPacket = (PSWP_TP) TransferPacket;
PSTRIPE_WP t = transferPacket->StripeWithParity;
PSWP_REGENERATE_TP p = t->_eRegeneratePacket;
transferPacket->CompletionRoutine = transferPacket->SavedCompletionRoutine;
p->Length = transferPacket->Length;
p->Offset = transferPacket->Offset;
p->CompletionRoutine = StripeWpSequentialRegenerateCompletion;
p->TargetVolume = t->GetMemberUnprotected(0);
p->Thread = transferPacket->Thread;
p->IrpFlags = transferPacket->IrpFlags;
p->ReadPacket = TRUE;
p->MasterPacket = transferPacket;
p->WhichMember = 0;
if (transferPacket->TargetVolume == p->TargetVolume) {
p->WhichMember = 1;
p->TargetVolume = t->GetMemberUnprotected(1);
}
TRANSFER(p);
}
VOID
StripeWpParallelRegenerateCompletion(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine a regenerate operation where all of
the reads are being performed in parallel.
Arguments:
TransferPacket - Supplies the completed transfer packet.
Return Value:
None.
--*/
{
PSWP_REGENERATE_TP transferPacket = (PSWP_REGENERATE_TP) TransferPacket;
PSWP_TP masterPacket = transferPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = transferPacket->IoStatus.Status;
KIRQL irql;
LONG count;
PLIST_ENTRY l, s;
PTRANSFER_PACKET packet;
PVOID target, source;
BOOLEAN b;
ULONG i, n;
if (NT_SUCCESS(status)) {
KeAcquireSpinLock(&masterPacket->SpinLock, &irql);
if (NT_SUCCESS(masterPacket->IoStatus.Status)) {
masterPacket->IoStatus = transferPacket->IoStatus;
}
} else {
if (FsRtlIsTotalDeviceFailure(status) &&
status != STATUS_VERIFY_REQUIRED) {
KeAcquireSpinLock(&t->_spinLock, &irql);
transferPacket->TargetVolume->SetMemberState(Orphaned);
KeReleaseSpinLock(&t->_spinLock, irql);
}
KeAcquireSpinLock(&masterPacket->SpinLock, &irql);
if (FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) {
masterPacket->IoStatus.Status = status;
masterPacket->IoStatus.Information = 0;
}
}
count = --masterPacket->RefCount;
KeReleaseSpinLock(&masterPacket->SpinLock, irql);
if (count) {
return;
}
s = &masterPacket->QueueEntry;
l = RemoveHeadList(s);
packet = CONTAINING_RECORD(l, SWP_REGENERATE_TP, RegenPacketList);
if (masterPacket->Mdl) {
target = MmGetSystemAddressForMdl(masterPacket->Mdl);
source = MmGetSystemAddressForMdl(packet->Mdl);
RtlCopyMemory(target, source, masterPacket->Length);
}
for (;;) {
delete packet;
if (IsListEmpty(s)) {
break;
}
l = RemoveHeadList(s);
packet = CONTAINING_RECORD(l, SWP_REGENERATE_TP, RegenPacketList);
if (masterPacket->Mdl) {
source = MmGetSystemAddressForMdl(packet->Mdl);
FtpComputeParity(target, source, masterPacket->Length);
}
}
masterPacket->CompletionRoutine(masterPacket);
b = FALSE;
n = t->QueryNumMembers();
KeAcquireSpinLock(&t->_spinLock, &irql);
for (i = 0; i < n; i++) {
b = t->DecrementRequestCount(i) || b;
}
KeReleaseSpinLock(&t->_spinLock, irql);
if (b) {
t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext,
STATUS_SUCCESS);
}
}
VOID
StripeWpRegeneratePacketPhase1(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This routine is called after the io regions necessary for a regenerate
have been allocated. This routine spawns the reads necessary for
regeneration.
Arguments:
TransferPacket - Supplies the main write packet.
Return Value:
None.
--*/
{
PSWP_TP transferPacket = (PSWP_TP) TransferPacket;
PSTRIPE_WP t = transferPacket->StripeWithParity;
ULONG i, n;
PSWP_REGENERATE_TP packet;
BOOLEAN sequential;
PLIST_ENTRY l, s;
ULONG r;
ULONG parityMember;
KIRQL irql;
transferPacket->CompletionRoutine = transferPacket->SavedCompletionRoutine;
// Determine whether we're going to do this in parallel or
// sequentially by trying to allocate the memory.
n = t->QueryNumMembers();
InitializeListHead(&transferPacket->QueueEntry);
for (i = 0; i < n; i++) {
if (i == transferPacket->WhichMember) {
continue;
}
packet = new SWP_REGENERATE_TP;
if (packet && !packet->AllocateMdl(transferPacket->Length)) {
delete packet;
packet = NULL;
}
if (!packet) {
break;
}
packet->Length = transferPacket->Length;
packet->Offset = transferPacket->Offset;
packet->CompletionRoutine = StripeWpParallelRegenerateCompletion;
packet->TargetVolume = t->GetMemberUnprotected(i);
packet->Thread = transferPacket->Thread;
packet->IrpFlags = transferPacket->IrpFlags;
packet->ReadPacket = TRUE;
packet->MasterPacket = transferPacket;
InsertTailList(&transferPacket->QueueEntry, &packet->RegenPacketList);
}
if (i < n) {
sequential = TRUE;
s = &transferPacket->QueueEntry;
while (!IsListEmpty(s)) {
l = RemoveHeadList(s);
packet = CONTAINING_RECORD(l, SWP_REGENERATE_TP, RegenPacketList);
delete packet;
}
} else {
sequential = FALSE;
}
KeInitializeSpinLock(&transferPacket->SpinLock);
transferPacket->IoStatus.Status = STATUS_SUCCESS;
transferPacket->IoStatus.Information = 0;
transferPacket->RefCount = n - 1;
if (sequential) {
transferPacket->CompletionRoutine = StripeWpSequentialEmergencyCompletion;
RtlZeroMemory(MmGetSystemAddressForMdl(transferPacket->Mdl),
transferPacket->Length);
KeAcquireSpinLock(&t->_spinLock, &irql);
if (t->_eRegeneratePacketInUse) {
InsertTailList(&t->_eRegeneratePacketQueue, &transferPacket->QueueEntry);
KeReleaseSpinLock(&t->_spinLock, irql);
return;
}
t->_eRegeneratePacketInUse = TRUE;
KeReleaseSpinLock(&t->_spinLock, irql);
transferPacket->CompletionRoutine(transferPacket);
} else {
s = &transferPacket->QueueEntry;
for (l = s->Flink; l != s; l = l->Flink) {
packet = CONTAINING_RECORD(l, SWP_REGENERATE_TP, RegenPacketList);
TRANSFER(packet);
}
}
}
VOID
STRIPE_WP::RegeneratePacket(
IN OUT PSWP_TP TransferPacket,
IN BOOLEAN AllocateRegion
)
/*++
Routine Description:
This routine regenerate the given transfer packet by reading
from the other drives and performing the xor. This routine first
attempts to do all of the read concurently but if the memory is
not available then the reads are done sequentially.
Arguments:
TransferPacket - Supplies the transfer packet to regenerate.
AllocateRegion - Supplies whether or not we need to acquire the
io region via the overlapped io manager before
starting the regenerate operation. This should
usually be set to TRUE unless the region has
already been allocated.
Return Value:
None.
--*/
{
ULONG i, n, parityMember;
KIRQL irql;
PFT_VOLUME vol;
TransferPacket->OneReadFailed = TRUE;
// First make sure that all of the members are healthy.
n = QueryNumMembers();
parityMember = (ULONG) ((TransferPacket->Offset/_stripeSize)%n);
KeAcquireSpinLock(&_spinLock, &irql);
if (parityMember != TransferPacket->WhichMember && _initializing) {
i = 0;
} else {
for (i = 0; i < n; i++) {
if (i == TransferPacket->WhichMember) {
continue;
}
vol = GetMemberUnprotected(i);
if (vol->QueryMemberStateUnprotected() != Healthy) {
break;
}
}
}
if (i == n) {
for (i = 0; i < n; i++) {
IncrementRequestCount(i);
}
}
KeReleaseSpinLock(&_spinLock, irql);
if (i < n) {
TransferPacket->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
TransferPacket->IoStatus.Information = 0;
TransferPacket->CompletionRoutine(TransferPacket);
return;
}
TransferPacket->SavedCompletionRoutine = TransferPacket->CompletionRoutine;
TransferPacket->CompletionRoutine = StripeWpRegeneratePacketPhase1;
if (AllocateRegion) {
_overlappedIoManager.AcquireIoRegion(TransferPacket, TRUE);
} else {
TransferPacket->CompletionRoutine(TransferPacket);
}
}
VOID
StripeWpRecoverPhase8(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for a single sector read
of the main member after a write was done to check for
data integrity.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket = (PSWP_RECOVER_TP) TransferPacket;
PSWP_TP masterPacket = (PSWP_TP) subPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = subPacket->IoStatus.Status;
if (FsRtlIsTotalDeviceFailure(status)) {
masterPacket->OneReadFailed = FALSE;
masterPacket->IoStatus = subPacket->IoStatus;
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
if (!NT_SUCCESS(status) ||
RtlCompareMemory(MmGetSystemAddressForMdl(subPacket->PartialMdl),
MmGetSystemAddressForMdl(subPacket->VerifyMdl),
subPacket->Length) != subPacket->Length) {
masterPacket->IoStatus.Status = STATUS_FT_READ_RECOVERY_FROM_BACKUP;
FtpLogError(subPacket->TargetVolume->GetMemberExtension(),
FT_SECTOR_FAILURE, status,
(ULONG) (subPacket->Offset/t->QuerySectorSize()), NULL);
}
if (subPacket->Offset + subPacket->Length ==
masterPacket->Offset + masterPacket->Length) {
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
subPacket->Mdl = subPacket->PartialMdl;
subPacket->Offset += subPacket->Length;
subPacket->CompletionRoutine = StripeWpRecoverPhase2;
subPacket->ReadPacket = TRUE;
MmPrepareMdlForReuse(subPacket->Mdl);
IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl,
(PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) +
(ULONG) (subPacket->Offset - masterPacket->Offset),
subPacket->Length);
TRANSFER(subPacket);
}
VOID
StripeWpRecoverPhase7(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for a single sector write
of the main member after a replace sector was done.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket = (PSWP_RECOVER_TP) TransferPacket;
PSWP_TP masterPacket = (PSWP_TP) subPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = subPacket->IoStatus.Status;
if (FsRtlIsTotalDeviceFailure(status)) {
masterPacket->OneReadFailed = FALSE;
masterPacket->IoStatus = subPacket->IoStatus;
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
if (!NT_SUCCESS(status)) {
masterPacket->IoStatus.Status = STATUS_FT_READ_RECOVERY_FROM_BACKUP;
FtpLogError(subPacket->TargetVolume->GetMemberExtension(),
FT_SECTOR_FAILURE, status,
(ULONG) (subPacket->Offset/t->QuerySectorSize()), NULL);
if (subPacket->Offset + subPacket->Length ==
masterPacket->Offset + masterPacket->Length) {
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
subPacket->Mdl = subPacket->PartialMdl;
subPacket->Offset += subPacket->Length;
subPacket->CompletionRoutine = StripeWpRecoverPhase2;
subPacket->ReadPacket = TRUE;
MmPrepareMdlForReuse(subPacket->Mdl);
IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl,
(PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) +
(ULONG) (subPacket->Offset - masterPacket->Offset),
subPacket->Length);
TRANSFER(subPacket);
return;
}
subPacket->Mdl = subPacket->VerifyMdl;
subPacket->CompletionRoutine = StripeWpRecoverPhase8;
subPacket->ReadPacket = TRUE;
TRANSFER(subPacket);
}
VOID
StripeWpRecoverPhase6(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for a single sector replace
of the main member.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket = (PSWP_RECOVER_TP) TransferPacket;
PSWP_TP masterPacket = (PSWP_TP) subPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = subPacket->IoStatus.Status;
if (!NT_SUCCESS(status)) {
masterPacket->IoStatus.Status = STATUS_FT_READ_RECOVERY_FROM_BACKUP;
FtpLogError(subPacket->TargetVolume->GetMemberExtension(),
FT_SECTOR_FAILURE, status,
(ULONG) (subPacket->Offset/t->QuerySectorSize()), NULL);
if (subPacket->Offset + subPacket->Length ==
masterPacket->Offset + masterPacket->Length) {
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
subPacket->Mdl = subPacket->PartialMdl;
subPacket->Offset += subPacket->Length;
subPacket->CompletionRoutine = StripeWpRecoverPhase2;
subPacket->ReadPacket = TRUE;
MmPrepareMdlForReuse(subPacket->Mdl);
IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl,
(PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) +
(ULONG) (subPacket->Offset - masterPacket->Offset),
subPacket->Length);
TRANSFER(subPacket);
return;
}
// We were able to relocate the bad sector so now do a write and
// then read to make sure it's ok.
subPacket->Mdl = subPacket->PartialMdl;
subPacket->CompletionRoutine = StripeWpRecoverPhase7;
subPacket->ReadPacket = FALSE;
TRANSFER(subPacket);
}
VOID
StripeWpRecoverPhase5(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for a single sector read
of the main member after a successful write to check and
see if the write was successful.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket = (PSWP_RECOVER_TP) TransferPacket;
PSWP_TP masterPacket = (PSWP_TP) subPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = subPacket->IoStatus.Status;
if (FsRtlIsTotalDeviceFailure(status)) {
masterPacket->OneReadFailed = FALSE;
masterPacket->IoStatus = subPacket->IoStatus;
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
if (!NT_SUCCESS(status) ||
RtlCompareMemory(MmGetSystemAddressForMdl(subPacket->PartialMdl),
MmGetSystemAddressForMdl(subPacket->VerifyMdl),
subPacket->Length) != subPacket->Length) {
subPacket->Mdl = subPacket->PartialMdl;
subPacket->CompletionRoutine = StripeWpRecoverPhase6;
subPacket->TargetVolume->ReplaceBadSector(subPacket);
return;
}
if (subPacket->Offset + subPacket->Length ==
masterPacket->Offset + masterPacket->Length) {
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
subPacket->Mdl = subPacket->PartialMdl;
subPacket->Offset += subPacket->Length;
subPacket->CompletionRoutine = StripeWpRecoverPhase2;
MmPrepareMdlForReuse(subPacket->Mdl);
IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl,
(PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) +
(ULONG) (subPacket->Offset - masterPacket->Offset),
subPacket->Length);
TRANSFER(subPacket);
}
VOID
StripeWpRecoverPhase4(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for a single sector write
of the main member.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket = (PSWP_RECOVER_TP) TransferPacket;
PSWP_TP masterPacket = (PSWP_TP) subPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = subPacket->IoStatus.Status;
if (FsRtlIsTotalDeviceFailure(status)) {
masterPacket->OneReadFailed = FALSE;
masterPacket->IoStatus = subPacket->IoStatus;
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
if (!NT_SUCCESS(status)) {
subPacket->CompletionRoutine = StripeWpRecoverPhase6;
subPacket->TargetVolume->ReplaceBadSector(subPacket);
return;
}
// Write was successful so try a read and then compare.
subPacket->Mdl = subPacket->VerifyMdl;
subPacket->CompletionRoutine = StripeWpRecoverPhase5;
subPacket->ReadPacket = TRUE;
TRANSFER(subPacket);
}
VOID
StripeWpRecoverPhase3(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for a single sector read
of the other member.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket = (PSWP_RECOVER_TP) TransferPacket;
PSWP_TP masterPacket = (PSWP_TP) subPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = subPacket->IoStatus.Status;
KIRQL irql;
BOOLEAN b;
if (!NT_SUCCESS(status)) {
masterPacket->IoStatus.Status = STATUS_DEVICE_DATA_ERROR;
masterPacket->IoStatus.Information = 0;
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
// We have the data required in the subpacket partial mdl.
// Try writting it back to where the read failed and see
// if the sector just fixes itself.
subPacket->CompletionRoutine = StripeWpRecoverPhase4;
subPacket->ReadPacket = FALSE;
TRANSFER(subPacket);
}
VOID
StripeWpRecoverPhase2(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for a single sector transfer
that is part of a larger recover operation.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket = (PSWP_RECOVER_TP) TransferPacket;
PSWP_TP masterPacket = (PSWP_TP) subPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = subPacket->IoStatus.Status;
KIRQL irql;
if (FsRtlIsTotalDeviceFailure(status)) {
masterPacket->OneReadFailed = FALSE;
masterPacket->IoStatus = subPacket->IoStatus;
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
if (NT_SUCCESS(status)) {
if (subPacket->Offset + subPacket->Length ==
masterPacket->Offset + masterPacket->Length) {
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
subPacket->Offset += subPacket->Length;
MmPrepareMdlForReuse(subPacket->Mdl);
IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl,
(PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) +
(ULONG) (subPacket->Offset - masterPacket->Offset),
subPacket->Length);
TRANSFER(subPacket);
return;
}
// This read sector failed from a bad sector error. Try
// regenerating the data from the other members.
subPacket->CompletionRoutine = StripeWpRecoverPhase3;
t->RegeneratePacket(subPacket, FALSE);
}
VOID
StripeWpRecoverEmergencyCompletion(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This routine is the completion for use of the emergency recover packet
in a recover operation.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_TP transferPacket = (PSWP_TP) TransferPacket;
PSTRIPE_WP t = transferPacket->StripeWithParity;
PSWP_RECOVER_TP subPacket = t->_eRecoverPacket;
transferPacket->CompletionRoutine = transferPacket->SavedCompletionRoutine;
subPacket->Mdl = subPacket->PartialMdl;
IoBuildPartialMdl(transferPacket->Mdl, subPacket->Mdl,
MmGetMdlVirtualAddress(transferPacket->Mdl),
t->QuerySectorSize());
subPacket->Length = t->QuerySectorSize();
subPacket->Offset = transferPacket->Offset;
subPacket->CompletionRoutine = StripeWpRecoverPhase2;
subPacket->TargetVolume = transferPacket->TargetVolume;
subPacket->Thread = transferPacket->Thread;
subPacket->IrpFlags = transferPacket->IrpFlags;
subPacket->ReadPacket = TRUE;
subPacket->MasterPacket = transferPacket;
subPacket->StripeWithParity = t;
subPacket->WhichMember = transferPacket->WhichMember;
TRANSFER(subPacket);
}
VOID
StripeWpRecoverPhase1(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for an acquire io region
to a recover operation.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_TP transferPacket = (PSWP_TP) TransferPacket;
PSTRIPE_WP t = transferPacket->StripeWithParity;
PSWP_RECOVER_TP subPacket;
KIRQL irql;
transferPacket->CompletionRoutine = transferPacket->SavedCompletionRoutine;
transferPacket->IoStatus.Status = STATUS_SUCCESS;
transferPacket->IoStatus.Information = transferPacket->Length;
subPacket = new SWP_RECOVER_TP;
if (subPacket && !subPacket->AllocateMdls(t->QuerySectorSize())) {
delete subPacket;
subPacket = NULL;
}
if (!subPacket) {
KeAcquireSpinLock(&t->_spinLock, &irql);
if (t->_eRecoverPacketInUse) {
transferPacket->SavedCompletionRoutine =
transferPacket->CompletionRoutine;
transferPacket->CompletionRoutine = StripeWpRecoverEmergencyCompletion;
InsertTailList(&t->_eRecoverPacketQueue, &transferPacket->QueueEntry);
KeReleaseSpinLock(&t->_spinLock, irql);
return;
}
t->_eRecoverPacketInUse = TRUE;
KeReleaseSpinLock(&t->_spinLock, irql);
subPacket = t->_eRecoverPacket;
}
subPacket->Mdl = subPacket->PartialMdl;
IoBuildPartialMdl(transferPacket->Mdl, subPacket->Mdl,
MmGetMdlVirtualAddress(transferPacket->Mdl),
t->QuerySectorSize());
subPacket->Length = t->QuerySectorSize();
subPacket->Offset = transferPacket->Offset;
subPacket->CompletionRoutine = StripeWpRecoverPhase2;
subPacket->TargetVolume = transferPacket->TargetVolume;
subPacket->Thread = transferPacket->Thread;
subPacket->IrpFlags = transferPacket->IrpFlags;
subPacket->ReadPacket = TRUE;
subPacket->MasterPacket = transferPacket;
subPacket->StripeWithParity = t;
subPacket->WhichMember = transferPacket->WhichMember;
TRANSFER(subPacket);
}
VOID
STRIPE_WP::Recover(
IN OUT PSWP_TP TransferPacket
)
{
ASSERT(TransferPacket->ReadPacket);
TransferPacket->SavedCompletionRoutine = TransferPacket->CompletionRoutine;
TransferPacket->CompletionRoutine = StripeWpRecoverPhase1;
_overlappedIoManager.AcquireIoRegion(TransferPacket, TRUE);
}
VOID
StripeWpMaxTransferCompletionRoutine(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This is the completion routine for a sector transfer subordinate
to a MAX transfer operation.
Arguments:
TransferPacket - Supplies the subordinate transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket = (PSWP_RECOVER_TP) TransferPacket;
PSWP_TP masterPacket = (PSWP_TP) subPacket->MasterPacket;
PSTRIPE_WP t = masterPacket->StripeWithParity;
NTSTATUS status = subPacket->IoStatus.Status;
if (FsRtlIsTotalDeviceFailure(status)) {
masterPacket->IoStatus = subPacket->IoStatus;
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
if (subPacket->Offset + subPacket->Length ==
masterPacket->Offset + masterPacket->Length) {
t->RecycleRecoverTp(subPacket);
masterPacket->CompletionRoutine(masterPacket);
return;
}
subPacket->Offset += subPacket->Length;
MmPrepareMdlForReuse(subPacket->Mdl);
IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl,
(PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) +
(ULONG) (subPacket->Offset - masterPacket->Offset),
subPacket->Length);
if (subPacket->ReadPacket) {
t->RegeneratePacket(subPacket, FALSE);
} else {
TRANSFER(subPacket);
}
}
VOID
StripeWpMaxTransferEmergencyCompletion(
IN OUT PTRANSFER_PACKET TransferPacket
)
/*++
Routine Description:
This routine is the completion for use of the emergency recover packet
in a max transfer operation.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_TP transferPacket = (PSWP_TP) TransferPacket;
PSTRIPE_WP t = transferPacket->StripeWithParity;
PSWP_RECOVER_TP subPacket = t->_eRecoverPacket;
transferPacket->CompletionRoutine = transferPacket->SavedCompletionRoutine;
subPacket->Mdl = subPacket->PartialMdl;
IoBuildPartialMdl(transferPacket->Mdl, subPacket->Mdl,
MmGetMdlVirtualAddress(transferPacket->Mdl),
t->QuerySectorSize());
subPacket->Length = t->QuerySectorSize();
subPacket->Offset = transferPacket->Offset;
subPacket->CompletionRoutine = StripeWpMaxTransferCompletionRoutine;
subPacket->TargetVolume = transferPacket->TargetVolume;
subPacket->Thread = transferPacket->Thread;
subPacket->IrpFlags = transferPacket->IrpFlags;
subPacket->ReadPacket = transferPacket->ReadPacket;
subPacket->MasterPacket = transferPacket;
subPacket->StripeWithParity = t;
subPacket->WhichMember = transferPacket->WhichMember;
if (subPacket->ReadPacket) {
t->RegeneratePacket(subPacket, FALSE);
} else {
TRANSFER(subPacket);
}
}
VOID
STRIPE_WP::MaxTransfer(
IN OUT PSWP_TP TransferPacket
)
/*++
Routine Description:
This routine transfers the maximum possible subset of the given transfer
by doing it one sector at a time.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
PSWP_RECOVER_TP subPacket;
KIRQL irql;
TransferPacket->IoStatus.Status = STATUS_SUCCESS;
TransferPacket->IoStatus.Information = TransferPacket->Length;
subPacket = new SWP_RECOVER_TP;
if (subPacket && !subPacket->AllocateMdls(QuerySectorSize())) {
delete subPacket;
subPacket = NULL;
}
if (!subPacket) {
KeAcquireSpinLock(&_spinLock, &irql);
if (_eRecoverPacketInUse) {
TransferPacket->SavedCompletionRoutine =
TransferPacket->CompletionRoutine;
TransferPacket->CompletionRoutine = StripeWpMaxTransferEmergencyCompletion;
InsertTailList(&_eRecoverPacketQueue, &TransferPacket->QueueEntry);
KeReleaseSpinLock(&_spinLock, irql);
return;
}
_eRecoverPacketInUse = TRUE;
KeReleaseSpinLock(&_spinLock, irql);
subPacket = _eRecoverPacket;
}
subPacket->Mdl = subPacket->PartialMdl;
IoBuildPartialMdl(TransferPacket->Mdl, subPacket->Mdl,
MmGetMdlVirtualAddress(TransferPacket->Mdl),
QuerySectorSize());
subPacket->Length = QuerySectorSize();
subPacket->Offset = TransferPacket->Offset;
subPacket->CompletionRoutine = StripeWpMaxTransferCompletionRoutine;
subPacket->TargetVolume = TransferPacket->TargetVolume;
subPacket->Thread = TransferPacket->Thread;
subPacket->IrpFlags = TransferPacket->IrpFlags;
subPacket->ReadPacket = TransferPacket->ReadPacket;
subPacket->MasterPacket = TransferPacket;
subPacket->StripeWithParity = this;
subPacket->WhichMember = TransferPacket->WhichMember;
if (subPacket->ReadPacket) {
RegeneratePacket(subPacket, FALSE);
} else {
TRANSFER(subPacket);
}
}
VOID
STRIPE_WP::RecycleRecoverTp(
IN OUT PSWP_RECOVER_TP TransferPacket
)
/*++
Routine Description:
This routine recycles the given recover transfer packet and services
the emergency queue if need be.
Arguments:
TransferPacket - Supplies the transfer packet.
Return Value:
None.
--*/
{
KIRQL irql;
PLIST_ENTRY l;
PTRANSFER_PACKET p;
if (TransferPacket != _eRecoverPacket) {
delete TransferPacket;
return;
}
MmPrepareMdlForReuse(_eRecoverPacket->PartialMdl);
KeAcquireSpinLock(&_spinLock, &irql);
if (IsListEmpty(&_eRecoverPacketQueue)) {
_eRecoverPacketInUse = FALSE;
KeReleaseSpinLock(&_spinLock, irql);
return;
}
l = RemoveHeadList(&_eRecoverPacketQueue);
KeReleaseSpinLock(&_spinLock, irql);
p = CONTAINING_RECORD(l, TRANSFER_PACKET, QueueEntry);
p->CompletionRoutine(p);
return;
}