/*++ Copyright (c) 1991-5 Microsoft Corporation Module Name: mirror.cxx Abstract: This module contains the code specific to mirrors 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 { PMIRROR Mirror; PFT_VOLUME SpareVolume; FT_COMPLETION_ROUTINE CompletionRoutine; PVOID Context; PMIRROR_TP Packet; } START_REGENERATION_CONTEXT, *PSTART_REGENERATION_CONTEXT; MIRROR::~MIRROR( ) { if (_ePacket) { delete _ePacket; _ePacket = NULL; } if (_ePacket2) { delete _ePacket2; _ePacket2 = NULL; } if (_eRecoverPacket) { delete _eRecoverPacket; _eRecoverPacket = NULL; } } NTSTATUS MIRROR::Initialize( IN OUT PFT_VOLUME* VolumeArray, IN ULONG ArraySize ) /*++ Routine Description: Initialize routine for FT_VOLUME of type MIRROR. Arguments: VolumeArray - Supplies the array of volumes for this mirror. ArraySize - Supplies the number of volumes in this mirror. Return Value: None. --*/ { NTSTATUS status; ULONG i; LONGLONG volsize; if (ArraySize != 2) { return STATUS_INVALID_PARAMETER; } status = COMPOSITE_FT_VOLUME::Initialize(VolumeArray, ArraySize); if (!NT_SUCCESS(status)) { return status; } _volumeSize = VolumeArray[0]->QueryVolumeSize(); for (i = 1; i < ArraySize; i++) { volsize = VolumeArray[i]->QueryVolumeSize(); if (volsize < _volumeSize) { _volumeSize = volsize; } } for (i = 0; i < ArraySize; i++) { _requestCount[i] = 0; } _waitingForOrphanIdle = NULL; _syncExpected = TRUE; _ePacket = new MIRROR_TP; if (!_ePacket) { return STATUS_INSUFFICIENT_RESOURCES; } _ePacket2 = new MIRROR_TP; if (!_ePacket2) { return STATUS_INSUFFICIENT_RESOURCES; } _ePacketInUse = FALSE; InitializeListHead(&_ePacketQueue); _eRecoverPacket = new MIRROR_RECOVER_TP; if (!_eRecoverPacket) { return STATUS_INSUFFICIENT_RESOURCES; } if (!_eRecoverPacket->AllocateMdls(QuerySectorSize())) { return STATUS_INSUFFICIENT_RESOURCES; } _eRecoverPacketInUse = FALSE; InitializeListHead(&_eRecoverPacketQueue); status = _overlappedIoManager.Initialize(0); return status; } VOID MIRROR::Transfer( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: Transfer routine for MIRROR type FT_VOLUME. Balance READs as much as possible and propogate WRITEs to both the primary and secondary volumes. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { KIRQL irql; PMIRROR_TP packet1, packet2; if (TransferPacket->Offset + TransferPacket->Length > _volumeSize) { TransferPacket->IoStatus.Status = STATUS_INVALID_PARAMETER; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } if (!TransferPacket->Mdl) { TransferPacket->ReadPacket = FALSE; } KeAcquireSpinLock(&_spinLock, &irql); if (_ePacketInUse && TransferPacket->Mdl) { InsertTailList(&_ePacketQueue, &TransferPacket->QueueEntry); KeReleaseSpinLock(&_spinLock, irql); return; } KeReleaseSpinLock(&_spinLock, irql); packet1 = new MIRROR_TP; if (packet1 && !TransferPacket->ReadPacket) { packet2 = new MIRROR_TP; if (!packet2) { delete packet1; packet1 = NULL; } } else { packet2 = NULL; } if (!packet1) { if (!TransferPacket->Mdl) { TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } KeAcquireSpinLock(&_spinLock, &irql); if (_ePacketInUse) { InsertTailList(&_ePacketQueue, &TransferPacket->QueueEntry); KeReleaseSpinLock(&_spinLock, irql); return; } _ePacketInUse = TRUE; KeReleaseSpinLock(&_spinLock, irql); packet1 = _ePacket; packet2 = _ePacket2; } if (TransferPacket->ReadPacket) { if (!LaunchRead(TransferPacket, packet1)) { if (packet1 != _ePacket) { delete packet1; } } } else { if (!LaunchWrite(TransferPacket, packet1, packet2)) { if (packet1 != _ePacket) { delete packet1; delete packet2; } } } } VOID MIRROR::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 MirrorCompositeVolumeCompletionRoutine( 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 FinishRegenerate( IN PMIRROR Mirror, IN PFT_COMPLETION_ROUTINE_CONTEXT RegenContext, IN PMIRROR_TP TransferPacket ) { PMIRROR t = Mirror; KIRQL oldIrql; PLIST_ENTRY l; PMIRROR_TP packet; BOOLEAN b; delete TransferPacket; MirrorCompositeVolumeCompletionRoutine(RegenContext, STATUS_SUCCESS); KeAcquireSpinLock(&t->_spinLock, &oldIrql); b = t->DecrementRequestCount(0) || t->DecrementRequestCount(1); KeReleaseSpinLock(&t->_spinLock, oldIrql); if (b) { t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext, STATUS_SUCCESS); } } VOID MirrorRegenerateCompletionRoutine( IN PTRANSFER_PACKET TransferPacket ); VOID MirrorRegeneratePhase1( IN PTRANSFER_PACKET TransferPacket ) { TransferPacket->CompletionRoutine = MirrorRegenerateCompletionRoutine; TRANSFER(TransferPacket); } VOID MirrorRegenerateCompletionRoutine( IN PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: Completion routine for MIRROR::RestartRegenerations routine. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PMIRROR_TP transferPacket = (PMIRROR_TP) TransferPacket; PFT_COMPLETION_ROUTINE_CONTEXT context = (PFT_COMPLETION_ROUTINE_CONTEXT) transferPacket->MasterPacket; PMIRROR t = transferPacket->Mirror; KIRQL oldIrql; PLIST_ENTRY l; PMIRROR_TP packet; if (!NT_SUCCESS(transferPacket->IoStatus.Status)) { // We can't get a VERIFY_REQUIRED because we put IrpFlags equal // to SL_OVERRIDE_VERIFY_VOLUME. ASSERT(transferPacket->IoStatus.Status != STATUS_VERIFY_REQUIRED); if (FsRtlIsTotalDeviceFailure(transferPacket->IoStatus.Status)) { KeAcquireSpinLock(&t->_spinLock, &oldIrql); transferPacket->TargetVolume->SetMemberState(Orphaned); KeReleaseSpinLock(&t->_spinLock, oldIrql); FinishRegenerate(t, context, 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; } // Set up for the next packet. transferPacket->Thread = PsGetCurrentThread(); transferPacket->ReadPacket = !transferPacket->ReadPacket; transferPacket->WhichMember = (transferPacket->WhichMember + 1)%2; transferPacket->TargetVolume = t->GetMemberUnprotected( transferPacket->WhichMember); if (transferPacket->TargetVolume->QueryMemberState() == Orphaned) { FinishRegenerate(t, context, transferPacket); return; } if (transferPacket->ReadPacket) { t->_overlappedIoManager.ReleaseIoRegion(transferPacket); if (transferPacket->Offset + STRIPE_SIZE >= t->_volumeSize) { KeAcquireSpinLock(&t->_spinLock, &oldIrql); t->GetMemberUnprotected( (transferPacket->WhichMember+1)%2)-> SetMemberState(Healthy); KeReleaseSpinLock(&t->_spinLock, oldIrql); FinishRegenerate(t, context, transferPacket); return; } transferPacket->Offset += STRIPE_SIZE; if (t->_volumeSize - transferPacket->Offset < STRIPE_SIZE) { transferPacket->Length = (ULONG) (t->_volumeSize - transferPacket->Offset); } transferPacket->CompletionRoutine = MirrorRegeneratePhase1; t->_overlappedIoManager.AcquireIoRegion(transferPacket, TRUE); } else { TRANSFER(transferPacket); } } VOID MIRROR::StartSyncOperations( IN FT_COMPLETION_ROUTINE CompletionRoutine, IN PVOID Context ) /*++ Routine Description: This routine 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. --*/ { KIRQL oldIrql; PFT_VOLUME pri, sec; ULONG srcIndex; PMIRROR_TP packet; PFT_COMPLETION_ROUTINE_CONTEXT context; BOOLEAN b; ULONG i; 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; COMPOSITE_FT_VOLUME::StartSyncOperations( MirrorCompositeVolumeCompletionRoutine, context); srcIndex = 0; KeAcquireSpinLock(&_spinLock, &oldIrql); if (_syncExpected) { _syncExpected = FALSE; } else { KeReleaseSpinLock(&_spinLock, oldIrql); MirrorCompositeVolumeCompletionRoutine(context, STATUS_SUCCESS); return; } pri = GetMemberUnprotected(0); sec = GetMemberUnprotected(1); if (pri->QueryMemberStateUnprotected() != Regenerating && sec->QueryMemberStateUnprotected() != Regenerating) { KeReleaseSpinLock(&_spinLock, oldIrql); MirrorCompositeVolumeCompletionRoutine(context, STATUS_SUCCESS); return; } if (pri->QueryMemberStateUnprotected() == Regenerating) { srcIndex = 1; } if (GetMemberUnprotected(srcIndex)->QueryMemberStateUnprotected() != Healthy) { KeReleaseSpinLock(&_spinLock, oldIrql); MirrorCompositeVolumeCompletionRoutine(context, STATUS_SUCCESS); return; } IncrementRequestCount(0); IncrementRequestCount(1); KeReleaseSpinLock(&_spinLock, oldIrql); packet = new MIRROR_TP; if (packet && !packet->AllocateMdl(STRIPE_SIZE)) { delete packet; packet = NULL; } if (!packet) { MirrorCompositeVolumeCompletionRoutine(context, STATUS_INSUFFICIENT_RESOURCES); KeAcquireSpinLock(&_spinLock, &oldIrql); b = DecrementRequestCount(0) || DecrementRequestCount(1); KeReleaseSpinLock(&_spinLock, oldIrql); if (b) { _waitingForOrphanIdle(_waitingForOrphanIdleContext, STATUS_SUCCESS); } return; } packet->Length = STRIPE_SIZE; packet->Offset = 0; packet->CompletionRoutine = MirrorRegeneratePhase1; packet->Thread = PsGetCurrentThread(); packet->IrpFlags = SL_OVERRIDE_VERIFY_VOLUME; packet->ReadPacket = TRUE; packet->MasterPacket = (PMIRROR_TP) context; packet->Mirror = this; packet->WhichMember = srcIndex; packet->TargetVolume = GetMemberUnprotected(packet->WhichMember); _overlappedIoManager.AcquireIoRegion(packet, TRUE); } VOID StartRegeneration( 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; PMIRROR t = context->Mirror; ULONG orphanNumber = t->_waitingOrphanNumber; KIRQL oldIrql; PFT_VOLUME vol; BOOLEAN b; KeAcquireSpinLock(&t->_spinLock, &oldIrql); vol = t->GetMemberUnprotected(orphanNumber); t->SetMemberUnprotected(orphanNumber, context->SpareVolume); context->SpareVolume->SetMemberState(Regenerating); t->IncrementRequestCount(orphanNumber); t->_waitingForOrphanIdle = NULL; KeReleaseSpinLock(&t->_spinLock, oldIrql); if (vol != context->SpareVolume) { FtpDisolveVolume(t->_extension, vol); } t->_overlappedIoManager.AcquireIoRegion(context->Packet, TRUE); ExFreePool(context); } BOOLEAN MIRROR::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, numUnhealthy, orphanNumber; PFT_VOLUME vol; PSTART_REGENERATION_CONTEXT startContext; PFT_COMPLETION_ROUTINE_CONTEXT context; BOOLEAN b; PMIRROR_TP packet; if (SpareVolume->QueryVolumeSize() < _volumeSize) { 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 MIRROR_TP; if (packet && !packet->AllocateMdl(STRIPE_SIZE)) { 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->Mirror = 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 = STRIPE_SIZE; packet->Offset = 0; packet->CompletionRoutine = MirrorRegeneratePhase1; packet->Thread = PsGetCurrentThread(); packet->IrpFlags = SL_OVERRIDE_VERIFY_VOLUME; packet->ReadPacket = TRUE; packet->MasterPacket = (PTRANSFER_PACKET) context; packet->Mirror = this; KeAcquireSpinLock(&_spinLock, &oldIrql); numUnhealthy = 0; orphanNumber = 0; for (i = 0; i < 2; i++) { vol = GetMemberUnprotected(i); if (vol->QueryMemberStateUnprotected() != Healthy) { numUnhealthy++; orphanNumber = i; } } vol = GetMemberUnprotected(orphanNumber); if (numUnhealthy != 1 || vol->QueryMemberStateUnprotected() != Orphaned || _waitingForOrphanIdle || _syncExpected) { KeReleaseSpinLock(&_spinLock, oldIrql); ExFreePool(context); ExFreePool(startContext); delete packet; return COMPOSITE_FT_VOLUME::Regenerate(SpareVolume, CompletionRoutine, Context); } packet->WhichMember = (orphanNumber + 1)%2; packet->TargetVolume = GetMemberUnprotected(packet->WhichMember); IncrementRequestCount(packet->WhichMember); if (_requestCount[orphanNumber] != 0) { _waitingForOrphanIdle = StartRegeneration; _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); } _overlappedIoManager.AcquireIoRegion(packet, TRUE); return TRUE; } BOOLEAN MIRROR::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. --*/ { // Return FALSE since creating check data will show up as a // regeneration. return FALSE; } VOID MIRROR::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; KeAcquireSpinLock(&_spinLock, &irql); if (_syncExpected) { GetMemberUnprotected(1)->SetMemberState(Regenerating); } else { ASSERT(0); } KeReleaseSpinLock(&_spinLock, irql); } LONGLONG MIRROR::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 MIRROR::QueryVolumeType( ) /*++ Routine Description: Returns the volume type. Arguments: None. Return Value: Mirror - A mirror. --*/ { return Mirror; } FT_STATE MIRROR::QueryVolumeState( ) /*++ Routine Description: Returns the state of the volume. Arguments: None. Return Value: The state of this volume. --*/ { KIRQL oldIrql; FT_PARTITION_STATE priState, secState; KeAcquireSpinLock(&_spinLock, &oldIrql); priState = GetMemberUnprotected(0)->QueryMemberStateUnprotected(); secState = GetMemberUnprotected(1)->QueryMemberStateUnprotected(); KeReleaseSpinLock(&_spinLock, oldIrql); if (priState == Healthy) { if (secState == Healthy) { return FtStateOk; } if (secState == Orphaned) { return FtHasOrphan; } return FtRegenerating; } if (priState == Orphaned) { if (secState == Healthy) { return FtHasOrphan; } return FtDisabled; } if (secState == Healthy) { return FtRegenerating; } return FtDisabled; } BOOLEAN MIRROR::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; } VOID MIRROR::MemberStateChangeNotification( IN PFT_VOLUME ChangedMember ) /*++ Routine Description: This routine is called on the parent volume when a member volume changes it's member state. This routine records the changed information for posterity. Arguments: ChangedMember - Supplies the member that has changed. Return Value: None. --*/ { PFT_VOLUME otherMember; COMPOSITE_FT_VOLUME::MemberStateChangeNotification(ChangedMember); if (ChangedMember->QueryMemberState() == Orphaned) { if (ChangedMember == GetMemberUnprotected(0)) { otherMember = GetMemberUnprotected(1); } else if (ChangedMember == GetMemberUnprotected(1)) { otherMember = GetMemberUnprotected(0); } if (otherMember->QueryMemberState() == Healthy) { otherMember->SetFtBitInPartitionType(TRUE, TRUE); } } } BOOLEAN MIRROR::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 MirrorTransferCompletionRoutine( IN PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: Completion routine for MIRROR::Transfer function. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PMIRROR_TP transferPacket = (PMIRROR_TP) TransferPacket; PTRANSFER_PACKET masterPacket = transferPacket->MasterPacket; NTSTATUS status = transferPacket->IoStatus.Status; PMIRROR t = transferPacket->Mirror; KIRQL irql; LONG count; ULONG oldMember; BOOLEAN b; // Check for the read completion case. if (transferPacket->ReadPacket) { if (!NT_SUCCESS(status) && status != STATUS_VERIFY_REQUIRED) { if (FsRtlIsTotalDeviceFailure(status)) { // Device failure case. KeAcquireSpinLock(&t->_spinLock, &irql); transferPacket->TargetVolume->SetMemberState(Orphaned); KeReleaseSpinLock(&t->_spinLock, irql); if (!transferPacket->OneReadFailed) { transferPacket->OneReadFailed = TRUE; oldMember = transferPacket->WhichMember; transferPacket->WhichMember = (transferPacket->WhichMember + 1) % 2; KeAcquireSpinLock(&t->_spinLock, &irql); transferPacket->TargetVolume = t->GetMemberUnprotected( transferPacket->WhichMember); if (transferPacket->TargetVolume-> QueryMemberStateUnprotected() == Healthy) { t->IncrementRequestCount(transferPacket->WhichMember); b = t->DecrementRequestCount(oldMember); KeReleaseSpinLock(&t->_spinLock, irql); if (b) { t->_waitingForOrphanIdle( t->_waitingForOrphanIdleContext, STATUS_SUCCESS); } TRANSFER(transferPacket); return; } KeReleaseSpinLock(&t->_spinLock, irql); } } else { // Bad sector case. if (!transferPacket->OneReadFailed) { transferPacket->OneReadFailed = TRUE; t->Recover(transferPacket); return; } } } masterPacket->IoStatus = transferPacket->IoStatus; masterPacket->CompletionRoutine(masterPacket); KeAcquireSpinLock(&t->_spinLock, &irql); b = t->DecrementRequestCount(transferPacket->WhichMember); KeReleaseSpinLock(&t->_spinLock, irql); if (b) { t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext, STATUS_SUCCESS); } t->Recycle(transferPacket, TRUE); return; } // This a write or a verify in which two requests may have been sent. KeAcquireSpinLock(&masterPacket->SpinLock, &irql); if (NT_SUCCESS(status)) { if (NT_SUCCESS(masterPacket->IoStatus.Status)) { masterPacket->IoStatus.Information = transferPacket->IoStatus.Information; } } else { if (FsRtlIsTotalDeviceFailure(status) && status != STATUS_VERIFY_REQUIRED) { KeAcquireSpinLock(&t->_spinLock, &irql); transferPacket->TargetVolume->SetMemberState(Orphaned); KeReleaseSpinLock(&t->_spinLock, irql); } masterPacket->IoStatus.Information = 0; if (FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) { masterPacket->IoStatus.Status = status; } } count = --masterPacket->RefCount; KeReleaseSpinLock(&masterPacket->SpinLock, irql); if (!count) { masterPacket->CompletionRoutine(masterPacket); } KeAcquireSpinLock(&t->_spinLock, &irql); b = t->DecrementRequestCount(transferPacket->WhichMember); KeReleaseSpinLock(&t->_spinLock, irql); if (b) { t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext, STATUS_SUCCESS); } t->Recycle(transferPacket, count ? FALSE : TRUE); } BOOLEAN MIRROR::LaunchRead( IN OUT PTRANSFER_PACKET TransferPacket, IN OUT PMIRROR_TP Packet1 ) /*++ Routine Description: This routine lauches the given read transfer packet in parallel accross all members using the given mirror transfer packet. Arguments: TransferPacket - Supplies the transfer packet to launch. Packet1 - Supplies a worker transfer packet. Return Value: FALSE - The read request was not launched. TRUE - The read request was launched. --*/ { PMIRROR_TP packet; KIRQL irql; PFT_VOLUME pri, sec; packet = Packet1; packet->Mdl = TransferPacket->Mdl; packet->Length = TransferPacket->Length; packet->Offset = TransferPacket->Offset; packet->CompletionRoutine = MirrorTransferCompletionRoutine; packet->Thread = TransferPacket->Thread; packet->IrpFlags = TransferPacket->IrpFlags; packet->ReadPacket = TransferPacket->ReadPacket; packet->MasterPacket = TransferPacket; packet->Mirror = this; // Determine which member to dispatch this read request to. // Balance the load if both members are healthy. KeAcquireSpinLock(&_spinLock, &irql); pri = GetMemberUnprotected(0); sec = GetMemberUnprotected(1); if (TransferPacket->SpecialRead) { if (TransferPacket->SpecialRead == TP_SPECIAL_READ_PRIMARY) { packet->WhichMember = 0; packet->TargetVolume = pri; } else { packet->WhichMember = 1; packet->TargetVolume = sec; } if (packet->TargetVolume->QueryMemberStateUnprotected() != Healthy) { packet->WhichMember = 2; } } else if (pri->QueryMemberStateUnprotected() == Healthy) { if (sec->QueryMemberStateUnprotected() == Healthy) { if (_requestCount[0] > _requestCount[1]) { packet->WhichMember = 1; } else { packet->WhichMember = 0; } } else { packet->WhichMember = 0; } } else { if (sec->QueryMemberStateUnprotected() == Healthy) { packet->WhichMember = 1; } else { packet->WhichMember = 2; } } if (packet->WhichMember < 2) { packet->TargetVolume = GetMemberUnprotected(packet->WhichMember); IncrementRequestCount(packet->WhichMember); } KeReleaseSpinLock(&_spinLock, irql); if (packet->WhichMember >= 2) { TransferPacket->IoStatus.Status = STATUS_NO_SUCH_DEVICE; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return FALSE; } TRANSFER(packet); return TRUE; } VOID MirrorWritePhase1( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This routine sends down the given transfer packets for a write to the volumes. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PTRANSFER_PACKET p; p = ((PMIRROR_TP) TransferPacket)->SecondWritePacket; if (p) { p->CompletionRoutine = MirrorTransferCompletionRoutine; TRANSFER(p); } TransferPacket->CompletionRoutine = MirrorTransferCompletionRoutine; TRANSFER(TransferPacket); } BOOLEAN MIRROR::LaunchWrite( IN OUT PTRANSFER_PACKET TransferPacket, IN OUT PMIRROR_TP Packet1, IN OUT PMIRROR_TP Packet2 ) /*++ Routine Description: This routine lauches the given write transfer packet in parallel accross all members using the given mirror transfer packets. Arguments: TransferPacket - Supplies the transfer packet to launch. Packet1 - Supplies a worker transfer packet. Packet2 - Supplies a worker transfer packet. Return Value: FALSE - The read request was not launched. TRUE - The read request was launched. --*/ { PMIRROR_TP packet; KIRQL irql; PFT_VOLUME pri, sec; FT_PARTITION_STATE priState, secState; LONGLONG rowStart; ULONG numRows, length, remainder, source; LONG count; BOOLEAN b; KeInitializeSpinLock(&TransferPacket->SpinLock); TransferPacket->IoStatus.Status = STATUS_SUCCESS; TransferPacket->IoStatus.Information = 0; TransferPacket->RefCount = 2; // Send down the first request to the primary or to the source // if we're doing a regenerate. KeAcquireSpinLock(&_spinLock, &irql); pri = GetMemberUnprotected(0); priState = pri->QueryMemberStateUnprotected(); sec = GetMemberUnprotected(1); secState = sec->QueryMemberStateUnprotected(); if (priState != Healthy && secState != Healthy) { TransferPacket->RefCount = 0; } else { if (priState == Orphaned) { TransferPacket->RefCount = 1; } else { IncrementRequestCount(0); } if (secState == Orphaned) { TransferPacket->RefCount = 1; } else { IncrementRequestCount(1); } if (priState == Healthy) { source = 0; } else { source = 1; } } KeReleaseSpinLock(&_spinLock, irql); if (!TransferPacket->RefCount) { TransferPacket->IoStatus.Status = STATUS_NO_SUCH_DEVICE; TransferPacket->CompletionRoutine(TransferPacket); return FALSE; } packet = Packet1; packet->Mdl = TransferPacket->Mdl; packet->Length = TransferPacket->Length; packet->Offset = TransferPacket->Offset; packet->CompletionRoutine = MirrorWritePhase1; packet->Thread = TransferPacket->Thread; packet->IrpFlags = TransferPacket->IrpFlags; packet->ReadPacket = TransferPacket->ReadPacket; packet->MasterPacket = TransferPacket; packet->Mirror = this; packet->WhichMember = source; packet->SecondWritePacket = NULL; packet->TargetVolume = GetMemberUnprotected(packet->WhichMember); if (TransferPacket->RefCount == 1) { _overlappedIoManager.AcquireIoRegion(packet, TRUE); if (Packet2 != _ePacket && Packet2 != _ePacket2) { delete Packet2; } return TRUE; } packet = Packet2; packet->Mdl = TransferPacket->Mdl; packet->Length = TransferPacket->Length; packet->Offset = TransferPacket->Offset; packet->CompletionRoutine = MirrorWritePhase1; packet->Thread = TransferPacket->Thread; packet->IrpFlags = TransferPacket->IrpFlags; packet->ReadPacket = TransferPacket->ReadPacket; packet->MasterPacket = TransferPacket; packet->Mirror = this; packet->WhichMember = (source + 1)%2; packet->SecondWritePacket = Packet1; packet->TargetVolume = GetMemberUnprotected(packet->WhichMember); _overlappedIoManager.AcquireIoRegion(packet, TRUE); return TRUE; } VOID MIRROR::Recycle( IN OUT PMIRROR_TP TransferPacket, IN BOOLEAN ServiceEmergencyQueue ) /*++ Routine Description: This routine recycles the given transfer packet and services the emergency queue if need be. Arguments: TransferPacket - Supplies the transfer packet. ServiceEmergencyQueue - Supplies whether or not to service the emergency queue. Return Value: None. --*/ { KIRQL irql; PLIST_ENTRY l; PTRANSFER_PACKET p; PMIRROR_TP packet1, packet2; if (TransferPacket != _ePacket && TransferPacket != _ePacket2 && TransferPacket != _eRecoverPacket) { delete TransferPacket; return; } TransferPacket->SpecialRead = 0; TransferPacket->OneReadFailed = FALSE; _overlappedIoManager.ReleaseIoRegion(TransferPacket); if (TransferPacket == _eRecoverPacket) { 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; } if (!ServiceEmergencyQueue) { return; } for (;;) { KeAcquireSpinLock(&_spinLock, &irql); if (IsListEmpty(&_ePacketQueue)) { _ePacketInUse = FALSE; KeReleaseSpinLock(&_spinLock, irql); break; } l = RemoveHeadList(&_ePacketQueue); KeReleaseSpinLock(&_spinLock, irql); p = CONTAINING_RECORD(l, TRANSFER_PACKET, QueueEntry); packet1 = new MIRROR_TP; if (packet1 && !TransferPacket->ReadPacket) { packet2 = new MIRROR_TP; if (!packet2) { delete packet1; packet1 = NULL; } } else { packet2 = NULL; } if (!packet1) { packet1 = _ePacket; packet2 = _ePacket2; } if (TransferPacket->ReadPacket) { if (!LaunchRead(TransferPacket, packet1)) { if (packet1 != _ePacket) { delete packet1; packet1 = NULL; } } } else { if (!LaunchWrite(TransferPacket, packet1, packet2)) { if (packet1 != _ePacket) { delete packet1; delete packet2; packet1 = NULL; } } } if (packet1 == _ePacket) { break; } } } VOID MirrorRecoverPhase8( 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. --*/ { PMIRROR_RECOVER_TP subPacket = (PMIRROR_RECOVER_TP) TransferPacket; PMIRROR_TP masterPacket = (PMIRROR_TP) subPacket->MasterPacket; PMIRROR t = masterPacket->Mirror; NTSTATUS status = subPacket->IoStatus.Status; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->OneReadFailed = FALSE; masterPacket->IoStatus = subPacket->IoStatus; t->Recycle(subPacket, TRUE); 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->Recycle(subPacket, TRUE); masterPacket->CompletionRoutine(masterPacket); return; } subPacket->Mdl = subPacket->PartialMdl; subPacket->Offset += subPacket->Length; subPacket->CompletionRoutine = MirrorRecoverPhase2; 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 MirrorRecoverPhase7( 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. --*/ { PMIRROR_RECOVER_TP subPacket = (PMIRROR_RECOVER_TP) TransferPacket; PMIRROR_TP masterPacket = (PMIRROR_TP) subPacket->MasterPacket; PMIRROR t = masterPacket->Mirror; NTSTATUS status = subPacket->IoStatus.Status; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->OneReadFailed = FALSE; masterPacket->IoStatus = subPacket->IoStatus; t->Recycle(subPacket, TRUE); 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->Recycle(subPacket, TRUE); masterPacket->CompletionRoutine(masterPacket); return; } subPacket->Mdl = subPacket->PartialMdl; subPacket->Offset += subPacket->Length; subPacket->CompletionRoutine = MirrorRecoverPhase2; 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 = MirrorRecoverPhase8; subPacket->ReadPacket = TRUE; TRANSFER(subPacket); } VOID MirrorRecoverPhase6( 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. --*/ { PMIRROR_RECOVER_TP subPacket = (PMIRROR_RECOVER_TP) TransferPacket; PMIRROR_TP masterPacket = (PMIRROR_TP) subPacket->MasterPacket; PMIRROR t = masterPacket->Mirror; 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->Recycle(subPacket, TRUE); masterPacket->CompletionRoutine(masterPacket); return; } subPacket->Mdl = subPacket->PartialMdl; subPacket->Offset += subPacket->Length; subPacket->CompletionRoutine = MirrorRecoverPhase2; 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 = MirrorRecoverPhase7; subPacket->ReadPacket = FALSE; TRANSFER(subPacket); } VOID MirrorRecoverPhase5( 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. --*/ { PMIRROR_RECOVER_TP subPacket = (PMIRROR_RECOVER_TP) TransferPacket; PMIRROR_TP masterPacket = (PMIRROR_TP) subPacket->MasterPacket; PMIRROR t = masterPacket->Mirror; NTSTATUS status = subPacket->IoStatus.Status; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->OneReadFailed = FALSE; masterPacket->IoStatus = subPacket->IoStatus; t->Recycle(subPacket, TRUE); 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 = MirrorRecoverPhase6; subPacket->TargetVolume->ReplaceBadSector(subPacket); return; } if (subPacket->Offset + subPacket->Length == masterPacket->Offset + masterPacket->Length) { t->Recycle(subPacket, TRUE); masterPacket->CompletionRoutine(masterPacket); return; } subPacket->Mdl = subPacket->PartialMdl; subPacket->Offset += subPacket->Length; subPacket->CompletionRoutine = MirrorRecoverPhase2; MmPrepareMdlForReuse(subPacket->Mdl); IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl, (PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) + (ULONG) (subPacket->Offset - masterPacket->Offset), subPacket->Length); TRANSFER(subPacket); } VOID MirrorRecoverPhase4( 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. --*/ { PMIRROR_RECOVER_TP subPacket = (PMIRROR_RECOVER_TP) TransferPacket; PMIRROR_TP masterPacket = (PMIRROR_TP) subPacket->MasterPacket; PMIRROR t = masterPacket->Mirror; NTSTATUS status = subPacket->IoStatus.Status; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->OneReadFailed = FALSE; masterPacket->IoStatus = subPacket->IoStatus; t->Recycle(subPacket, TRUE); masterPacket->CompletionRoutine(masterPacket); return; } if (!NT_SUCCESS(status)) { subPacket->CompletionRoutine = MirrorRecoverPhase6; subPacket->TargetVolume->ReplaceBadSector(subPacket); return; } // Write was successful so try a read and then compare. subPacket->Mdl = subPacket->VerifyMdl; subPacket->CompletionRoutine = MirrorRecoverPhase5; subPacket->ReadPacket = TRUE; TRANSFER(subPacket); } VOID MirrorRecoverPhase3( 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. --*/ { PMIRROR_RECOVER_TP subPacket = (PMIRROR_RECOVER_TP) TransferPacket; PMIRROR_TP masterPacket = (PMIRROR_TP) subPacket->MasterPacket; PMIRROR t = masterPacket->Mirror; NTSTATUS status = subPacket->IoStatus.Status; KIRQL irql; BOOLEAN b; KeAcquireSpinLock(&t->_spinLock, &irql); b = t->DecrementRequestCount(subPacket->WhichMember); KeReleaseSpinLock(&t->_spinLock, irql); if (b) { t->_waitingForOrphanIdle(t->_waitingForOrphanIdleContext, STATUS_SUCCESS); } if (!NT_SUCCESS(status)) { if (FsRtlIsTotalDeviceFailure(status) && status != STATUS_VERIFY_REQUIRED) { masterPacket->IoStatus.Status = STATUS_DEVICE_DATA_ERROR; masterPacket->IoStatus.Information = 0; KeAcquireSpinLock(&t->_spinLock, &irql); subPacket->TargetVolume->SetMemberState(Orphaned); KeReleaseSpinLock(&t->_spinLock, irql); } else { masterPacket->IoStatus = subPacket->IoStatus; } t->Recycle(subPacket, TRUE); 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->WhichMember = (subPacket->WhichMember + 1)%2; subPacket->CompletionRoutine = MirrorRecoverPhase4; subPacket->TargetVolume = t->GetMemberUnprotected(subPacket->WhichMember); subPacket->ReadPacket = FALSE; TRANSFER(subPacket); } VOID MirrorRecoverPhase2( 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. --*/ { PMIRROR_RECOVER_TP subPacket = (PMIRROR_RECOVER_TP) TransferPacket; PMIRROR_TP masterPacket = (PMIRROR_TP) subPacket->MasterPacket; PMIRROR t = masterPacket->Mirror; NTSTATUS status = subPacket->IoStatus.Status; KIRQL irql; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->OneReadFailed = FALSE; masterPacket->IoStatus = subPacket->IoStatus; t->Recycle(subPacket, TRUE); masterPacket->CompletionRoutine(masterPacket); return; } if (NT_SUCCESS(status)) { if (subPacket->Offset + subPacket->Length == masterPacket->Offset + masterPacket->Length) { t->Recycle(subPacket, TRUE); 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 // reading the data from the other member. subPacket->WhichMember = (subPacket->WhichMember + 1)%2; KeAcquireSpinLock(&t->_spinLock, &irql); subPacket->TargetVolume = t->GetMemberUnprotected(subPacket->WhichMember); if (subPacket->TargetVolume->QueryMemberStateUnprotected() != Healthy) { KeReleaseSpinLock(&t->_spinLock, irql); masterPacket->IoStatus = subPacket->IoStatus; t->Recycle(subPacket, TRUE); masterPacket->CompletionRoutine(masterPacket); return; } t->IncrementRequestCount(subPacket->WhichMember); KeReleaseSpinLock(&t->_spinLock, irql); subPacket->CompletionRoutine = MirrorRecoverPhase3; TRANSFER(subPacket); } VOID MirrorRecoverEmergencyCompletion( 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. --*/ { PMIRROR_TP transferPacket = (PMIRROR_TP) TransferPacket; PMIRROR t = transferPacket->Mirror; PMIRROR_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 = MirrorRecoverPhase2; subPacket->TargetVolume = transferPacket->TargetVolume; subPacket->Thread = transferPacket->Thread; subPacket->IrpFlags = transferPacket->IrpFlags; subPacket->ReadPacket = TRUE; subPacket->MasterPacket = transferPacket; subPacket->Mirror = t; subPacket->WhichMember = transferPacket->WhichMember; TRANSFER(subPacket); } VOID MirrorRecoverPhase1( 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. --*/ { PMIRROR_TP transferPacket = (PMIRROR_TP) TransferPacket; PMIRROR t = transferPacket->Mirror; PMIRROR_RECOVER_TP subPacket; KIRQL irql; transferPacket->CompletionRoutine = transferPacket->SavedCompletionRoutine; transferPacket->IoStatus.Status = STATUS_SUCCESS; transferPacket->IoStatus.Information = transferPacket->Length; subPacket = new MIRROR_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 = MirrorRecoverEmergencyCompletion; 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 = MirrorRecoverPhase2; subPacket->TargetVolume = transferPacket->TargetVolume; subPacket->Thread = transferPacket->Thread; subPacket->IrpFlags = transferPacket->IrpFlags; subPacket->ReadPacket = TRUE; subPacket->MasterPacket = transferPacket; subPacket->Mirror = t; subPacket->WhichMember = transferPacket->WhichMember; TRANSFER(subPacket); } VOID MIRROR::Recover( IN OUT PMIRROR_TP TransferPacket ) /*++ Routine Description: This routine attempts the given read packet sector by sector. Every sector that fails to read because of a bad sector error is retried on the other member and then the good data is written back to the failed sector if possible. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { ASSERT(TransferPacket->ReadPacket); TransferPacket->SavedCompletionRoutine = TransferPacket->CompletionRoutine; TransferPacket->CompletionRoutine = MirrorRecoverPhase1; _overlappedIoManager.AcquireIoRegion(TransferPacket, TRUE); } VOID MirrorMaxTransferCompletionRoutine( 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. --*/ { PMIRROR_RECOVER_TP subPacket = (PMIRROR_RECOVER_TP) TransferPacket; PMIRROR_TP masterPacket = (PMIRROR_TP) subPacket->MasterPacket; PMIRROR t = masterPacket->Mirror; NTSTATUS status = subPacket->IoStatus.Status; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->IoStatus = subPacket->IoStatus; t->Recycle(subPacket, TRUE); masterPacket->CompletionRoutine(masterPacket); return; } if (subPacket->Offset + subPacket->Length == masterPacket->Offset + masterPacket->Length) { t->Recycle(subPacket, TRUE); 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); } VOID MirrorMaxTransferEmergencyCompletion( 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. --*/ { PMIRROR_TP transferPacket = (PMIRROR_TP) TransferPacket; PMIRROR t = transferPacket->Mirror; PMIRROR_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 = MirrorMaxTransferCompletionRoutine; subPacket->TargetVolume = transferPacket->TargetVolume; subPacket->Thread = transferPacket->Thread; subPacket->IrpFlags = transferPacket->IrpFlags; subPacket->ReadPacket = transferPacket->ReadPacket; subPacket->MasterPacket = transferPacket; subPacket->Mirror = t; subPacket->WhichMember = transferPacket->WhichMember; TRANSFER(subPacket); } VOID MIRROR::MaxTransfer( IN OUT PMIRROR_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. --*/ { PMIRROR_RECOVER_TP subPacket; KIRQL irql; TransferPacket->IoStatus.Status = STATUS_SUCCESS; TransferPacket->IoStatus.Information = TransferPacket->Length; subPacket = new MIRROR_RECOVER_TP; if (subPacket && !subPacket->AllocateMdls(QuerySectorSize())) { delete subPacket; subPacket = NULL; } if (!subPacket) { KeAcquireSpinLock(&_spinLock, &irql); if (_eRecoverPacketInUse) { TransferPacket->SavedCompletionRoutine = TransferPacket->CompletionRoutine; TransferPacket->CompletionRoutine = MirrorMaxTransferEmergencyCompletion; 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 = MirrorMaxTransferCompletionRoutine; subPacket->TargetVolume = TransferPacket->TargetVolume; subPacket->Thread = TransferPacket->Thread; subPacket->IrpFlags = TransferPacket->IrpFlags; subPacket->ReadPacket = TransferPacket->ReadPacket; subPacket->MasterPacket = TransferPacket; subPacket->Mirror = this; subPacket->WhichMember = TransferPacket->WhichMember; TRANSFER(subPacket); }