/*++ Copyright (C) 1991-5 Microsoft Corporation Module Name: redist.cxx Abstract: This module contains the code specific to redistributions for the fault tolerance driver. Author: Norbert Kusters 6-Feb-1997 Environment: kernel mode only Notes: Revision History: --*/ extern "C" { #include } #include class PROPOGATE_CHANGES_WORK_ITEM : public WORK_QUEUE_ITEM { public: PREDISTRIBUTION Redistribution; FT_COMPLETION_ROUTINE CompletionRoutine; PVOID Context; }; typedef PROPOGATE_CHANGES_WORK_ITEM *PPROPOGATE_CHANGES_WORK_ITEM; #ifdef ALLOC_PRAGMA #pragma code_seg("PAGE") #endif NTSTATUS REDISTRIBUTION::Initialize( IN OUT PROOT_EXTENSION RootExtension, IN FT_LOGICAL_DISK_ID LogicalDiskId, IN OUT PFT_VOLUME* VolumeArray, IN USHORT ArraySize, IN PVOID ConfigInfo, IN PVOID StateInfo ) /*++ Routine Description: Initialize routine for FT_VOLUME of type REDISTRIBUTION. Arguments: RootExtension - Supplies the root device extension. LogicalDiskId - Supplies the logical disk id for this volume. VolumeArray - Supplies the array of volumes for this volume set. ArraySize - Supplies the number of volumes in the volume array. ConfigInfo - Supplies the configuration information. StateInfo - Supplies the state information. Return Value: NTSTATUS --*/ { BOOLEAN oneGood; USHORT i; NTSTATUS status; PFT_REDISTRIBUTION_CONFIGURATION_INFORMATION configInfo; LONGLONG firstRowSize; LONGLONG secondRowSize; LONGLONG numRows; LONGLONG tmpNumRows; if (ArraySize != 2) { return STATUS_INVALID_PARAMETER; } oneGood = FALSE; for (i = 0; i < ArraySize; i++) { if (VolumeArray[i]) { oneGood = TRUE; } } if (!oneGood) { return STATUS_INVALID_PARAMETER; } status = COMPOSITE_FT_VOLUME::Initialize(RootExtension, LogicalDiskId, VolumeArray, ArraySize, ConfigInfo, StateInfo); if (!NT_SUCCESS(status)) { return status; } configInfo = (PFT_REDISTRIBUTION_CONFIGURATION_INFORMATION) ConfigInfo; _stripeSize = configInfo->StripeSize; if (_stripeSize < QuerySectorSize()) { return STATUS_INVALID_PARAMETER; } for (i = 0; _stripeSize%2 == 0; i++) { _stripeSize /= 2; } if (_stripeSize != 1) { return STATUS_INVALID_PARAMETER; } _stripeSize = configInfo->StripeSize; if( !configInfo->FirstMemberWidth || !configInfo->SecondMemberWidth) { return STATUS_INVALID_PARAMETER; } _firstWidth = configInfo->FirstMemberWidth; _totalWidth = _firstWidth + configInfo->SecondMemberWidth; if (VolumeArray[0]) { _firstSize = VolumeArray[0]->QueryVolumeSize(); } else { _firstSize = 0; } if (_firstSize && VolumeArray[1]) { firstRowSize = _firstWidth*_stripeSize; numRows = _firstSize/firstRowSize; secondRowSize = configInfo->SecondMemberWidth*_stripeSize; tmpNumRows = VolumeArray[1]->QueryVolumeSize()/secondRowSize; if (tmpNumRows < numRows) { numRows = tmpNumRows; } _totalSize = numRows*_totalWidth*_stripeSize; if (_totalSize < _firstSize) { return STATUS_INVALID_PARAMETER; } } else { _totalSize = 0; } _syncOk = TRUE; _stopSyncs = FALSE; RtlCopyMemory(&_state, StateInfo, sizeof(_state)); if (_state.BytesRedistributed < _firstSize) { _redistributionComplete = FALSE; } else { _redistributionComplete = TRUE; } status = _overlappedIoManager.Initialize(0); return status; } FT_LOGICAL_DISK_TYPE REDISTRIBUTION::QueryLogicalDiskType( ) /*++ Routine Description: This routine returns the type of the logical disk. Arguments: None. Return Value: The type of the logical disk. --*/ { return FtRedistribution; } NTSTATUS REDISTRIBUTION::CheckIo( OUT PBOOLEAN IsIoOk ) /*++ Routine Description: This routine returns whether or not IO is possible on the given logical disk. Arguments: IsIoOk - Returns the state of IO. Return Value: NTSTATUS --*/ { USHORT n, i; PFT_VOLUME vol; NTSTATUS status; n = QueryNumMembers(); for (i = 0; i < n; i++) { vol = GetMemberUnprotected(i); if (!vol) { *IsIoOk = FALSE; return STATUS_SUCCESS; } status = vol->CheckIo(IsIoOk); if (!NT_SUCCESS(status)) { return status; } if (!(*IsIoOk)) { return STATUS_SUCCESS; } } return STATUS_SUCCESS; } #ifdef ALLOC_PRAGMA #pragma code_seg("PAGELK") #endif REDISTRIBUTION::~REDISTRIBUTION( ) { } VOID RedistributionTwoPartCompletionRoutine( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This routine is the completion of a two part operation. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP transferPacket = (PREDISTRIBUTION_TP) TransferPacket; PREDISTRIBUTION_LOCK_TP lockPacket = (PREDISTRIBUTION_LOCK_TP) transferPacket->MasterPacket; PTRANSFER_PACKET masterPacket = lockPacket->MasterPacket; NTSTATUS status = transferPacket->IoStatus.Status; KIRQL irql; LONG count; KeAcquireSpinLock(&lockPacket->SpinLock, &irql); if (NT_SUCCESS(status)) { if (NT_SUCCESS(lockPacket->IoStatus.Status)) { lockPacket->IoStatus.Information += transferPacket->IoStatus.Information; } } else { if (FtpIsWorseStatus(status, lockPacket->IoStatus.Status)) { lockPacket->IoStatus.Status = status; } } count = --lockPacket->RefCount; KeReleaseSpinLock(&lockPacket->SpinLock, irql); delete transferPacket; if (!count) { masterPacket->IoStatus = lockPacket->IoStatus; delete lockPacket; masterPacket->CompletionRoutine(masterPacket); } } VOID RedistributionRegionLockCompletion( IN OUT PTRANSFER_PACKET LockPacket ) /*++ Routine Description: This routine is the completion of a region lock operation. Arguments: LockPacket - Supplies the lock packet. Return Value: None. --*/ { PREDISTRIBUTION_LOCK_TP lockPacket = (PREDISTRIBUTION_LOCK_TP) LockPacket; PREDISTRIBUTION t = lockPacket->Redistribution; PTRANSFER_PACKET masterPacket = lockPacket->MasterPacket; KIRQL irql; LONGLONG bytesRedistributed; LONGLONG redistOffset, regularOffset; ULONG redistLength, regularLength; PREDISTRIBUTION_TP redistPacket, regularPacket; PCHAR vp; KeAcquireSpinLock(&t->_spinLock, &irql); bytesRedistributed = t->_state.BytesRedistributed; KeReleaseSpinLock(&t->_spinLock, irql); if (lockPacket->Offset < bytesRedistributed) { redistOffset = lockPacket->Offset; redistLength = lockPacket->Length; if (redistOffset + redistLength > bytesRedistributed) { redistLength = (ULONG) (bytesRedistributed - redistOffset); } } else { redistLength = 0; } if (redistLength < lockPacket->Length) { regularLength = lockPacket->Length - redistLength; regularOffset = lockPacket->Offset + redistLength; } else { regularLength = 0; } KeInitializeSpinLock(&lockPacket->SpinLock); lockPacket->IoStatus.Status = STATUS_SUCCESS; lockPacket->IoStatus.Information = 0; lockPacket->RefCount = 0; if (lockPacket->Mdl && redistLength && regularLength) { vp = (PCHAR) MmGetMdlVirtualAddress(lockPacket->Mdl); } if (redistLength) { lockPacket->RefCount++; redistPacket = new REDISTRIBUTION_TP; if (!redistPacket) { delete lockPacket; masterPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; masterPacket->IoStatus.Information = 0; masterPacket->CompletionRoutine(masterPacket); return; } if (regularLength) { if (lockPacket->Mdl) { if (redistPacket->AllocateMdl(vp, redistLength)) { IoBuildPartialMdl(lockPacket->Mdl, redistPacket->Mdl, vp, redistLength); } else { delete redistPacket; delete lockPacket; masterPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; masterPacket->IoStatus.Information = 0; masterPacket->CompletionRoutine(masterPacket); return; } } else { redistPacket->Mdl = lockPacket->Mdl; } } else { redistPacket->Mdl = lockPacket->Mdl; } redistPacket->Length = redistLength; redistPacket->Offset = redistOffset; redistPacket->CompletionRoutine = RedistributionTwoPartCompletionRoutine; redistPacket->TargetVolume = t; redistPacket->Thread = lockPacket->Thread; redistPacket->IrpFlags = lockPacket->IrpFlags; redistPacket->ReadPacket = lockPacket->ReadPacket; redistPacket->MasterPacket = lockPacket; redistPacket->Redistribution = t; redistPacket->WhichMember = 0; } if (regularLength) { lockPacket->RefCount++; regularPacket = new REDISTRIBUTION_TP; if (!regularPacket) { if (redistLength) { delete redistPacket; } delete lockPacket; masterPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; masterPacket->IoStatus.Information = 0; masterPacket->CompletionRoutine(masterPacket); return; } if (redistLength) { if (lockPacket->Mdl) { if (regularPacket->AllocateMdl(vp, regularLength)) { IoBuildPartialMdl(lockPacket->Mdl, regularPacket->Mdl, vp, regularLength); } else { if (redistLength) { delete redistPacket; } delete regularPacket; delete lockPacket; masterPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; masterPacket->IoStatus.Information = 0; masterPacket->CompletionRoutine(masterPacket); return; } } else { regularPacket->Mdl = lockPacket->Mdl; } } else { regularPacket->Mdl = lockPacket->Mdl; } regularPacket->Length = regularLength; regularPacket->Offset = regularOffset; regularPacket->CompletionRoutine = RedistributionTwoPartCompletionRoutine; regularPacket->TargetVolume = t->GetMemberUnprotected(0); regularPacket->Thread = lockPacket->Thread; regularPacket->IrpFlags = lockPacket->IrpFlags; regularPacket->ReadPacket = lockPacket->ReadPacket; regularPacket->MasterPacket = lockPacket; regularPacket->Redistribution = t; regularPacket->WhichMember = 0; } if (redistLength) { t->RedistributeTransfer(redistPacket); } if (regularLength) { TRANSFER(regularPacket); } } VOID REDISTRIBUTION::Transfer( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: Transfer routine for REDISTRIBUTION type FT_VOLUME. Figure out which volumes this request needs to be dispatched to. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_LOCK_TP lockPacket; if (!_redistributionComplete) { if (TransferPacket->Offset + TransferPacket->Length > _firstSize) { TransferPacket->IoStatus.Status = STATUS_INVALID_PARAMETER; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } lockPacket = new REDISTRIBUTION_LOCK_TP; if (!lockPacket) { TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } lockPacket->Mdl = TransferPacket->Mdl; lockPacket->Length = TransferPacket->Length; lockPacket->Offset = TransferPacket->Offset; lockPacket->CompletionRoutine = RedistributionRegionLockCompletion; lockPacket->TargetVolume = TransferPacket->TargetVolume; lockPacket->Thread = TransferPacket->Thread; lockPacket->IrpFlags = TransferPacket->IrpFlags; lockPacket->ReadPacket = TransferPacket->ReadPacket; lockPacket->MasterPacket = TransferPacket; lockPacket->Redistribution = this; _overlappedIoManager.AcquireIoRegion(lockPacket, TRUE); return; } if (TransferPacket->Offset + TransferPacket->Length > _totalSize) { TransferPacket->IoStatus.Status = STATUS_INVALID_PARAMETER; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } RedistributeTransfer(TransferPacket); } VOID RedistributionReplaceCompletion( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for a replace request. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_LOCK_TP transferPacket = (PREDISTRIBUTION_LOCK_TP) TransferPacket; PTRANSFER_PACKET masterPacket = transferPacket->MasterPacket; masterPacket->IoStatus = transferPacket->IoStatus; delete transferPacket; masterPacket->CompletionRoutine(masterPacket); } VOID RedistributionLockReplaceCompletion( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for a lock request. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_LOCK_TP transferPacket = (PREDISTRIBUTION_LOCK_TP) TransferPacket; PREDISTRIBUTION t = transferPacket->Redistribution; KIRQL irql; LONGLONG bytesRedistributed; KeAcquireSpinLock(&t->_spinLock, &irql); bytesRedistributed = t->_state.BytesRedistributed; KeReleaseSpinLock(&t->_spinLock, irql); transferPacket->CompletionRoutine = RedistributionReplaceCompletion; if (transferPacket->Offset < bytesRedistributed) { t->RedistributeReplaceBadSector(transferPacket); } else { transferPacket->TargetVolume = t->GetMemberUnprotected(0); transferPacket->TargetVolume->ReplaceBadSector(transferPacket); } } VOID REDISTRIBUTION::ReplaceBadSector( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This routine attempts to fix the given bad sector by routing the request to the appropriate sub-volume. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_LOCK_TP lockPacket; if (!_redistributionComplete) { if (TransferPacket->Offset + TransferPacket->Length > _firstSize) { TransferPacket->IoStatus.Status = STATUS_INVALID_PARAMETER; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } lockPacket = new REDISTRIBUTION_LOCK_TP; if (!lockPacket) { TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } lockPacket->Mdl = TransferPacket->Mdl; lockPacket->Length = TransferPacket->Length; lockPacket->Offset = TransferPacket->Offset; lockPacket->CompletionRoutine = RedistributionLockReplaceCompletion; lockPacket->TargetVolume = TransferPacket->TargetVolume; lockPacket->Thread = TransferPacket->Thread; lockPacket->IrpFlags = TransferPacket->IrpFlags; lockPacket->ReadPacket = TransferPacket->ReadPacket; lockPacket->MasterPacket = TransferPacket; lockPacket->Redistribution = this; _overlappedIoManager.AcquireIoRegion(lockPacket, TRUE); return; } if (TransferPacket->Offset + TransferPacket->Length > _totalSize) { TransferPacket->IoStatus.Status = STATUS_INVALID_PARAMETER; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } RedistributeReplaceBadSector(TransferPacket); } VOID RedistributionCompositeVolumeCompletionRoutine( 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 RedistributionSyncPhase6( IN OUT PVOID SyncPacket, IN NTSTATUS Status ) /*++ Routine Description: This is the completion routine for the update state part of a sync. Arguments: SyncPacket - Supplies the transfer packet. Status - Supplies the status. Return Value: None. --*/ { PREDISTRIBUTION_SYNC_TP syncPacket = (PREDISTRIBUTION_SYNC_TP) SyncPacket; PREDISTRIBUTION_TP ioPacket = &syncPacket->IoPacket; PREDISTRIBUTION t = syncPacket->Redistribution; KIRQL irql; BOOLEAN allDone; if (!NT_SUCCESS(Status)) { KeAcquireSpinLock(&t->_spinLock, &irql); t->_state.BytesRedistributed -= ioPacket->Length; t->_syncOk = TRUE; KeReleaseSpinLock(&t->_spinLock, irql); t->_overlappedIoManager.ReleaseIoRegion(syncPacket); RedistributionCompositeVolumeCompletionRoutine(syncPacket->Context, STATUS_SUCCESS); delete syncPacket; return; } if (t->_state.BytesRedistributed == t->_firstSize) { t->_redistributionComplete = TRUE; allDone = TRUE; } else { allDone = FALSE; } t->_overlappedIoManager.ReleaseIoRegion(syncPacket); if (allDone) { RedistributionCompositeVolumeCompletionRoutine(syncPacket->Context, STATUS_SUCCESS); delete syncPacket; return; } syncPacket->Offset = t->_state.BytesRedistributed; t->_overlappedIoManager.AcquireIoRegion(syncPacket, TRUE); } VOID RedistributionSyncPhase5( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for the write verify part of a sync. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP ioPacket = (PREDISTRIBUTION_TP) TransferPacket; PREDISTRIBUTION_SYNC_TP syncPacket = (PREDISTRIBUTION_SYNC_TP) ioPacket->MasterPacket; PREDISTRIBUTION t = syncPacket->Redistribution; NTSTATUS status = ioPacket->IoStatus.Status; KIRQL irql; if (FsRtlIsTotalDeviceFailure(status)) { KeAcquireSpinLock(&t->_spinLock, &irql); t->_syncOk = TRUE; KeReleaseSpinLock(&t->_spinLock, irql); RedistributionCompositeVolumeCompletionRoutine(syncPacket->Context, status); delete syncPacket; return; } if (!NT_SUCCESS(status)) { KeAcquireSpinLock(&t->_spinLock, &irql); t->_syncOk = TRUE; KeReleaseSpinLock(&t->_spinLock, irql); RedistributionCompositeVolumeCompletionRoutine(syncPacket->Context, status); delete syncPacket; FtpLogError(t->_rootExtension, t->QueryLogicalDiskId(), FT_REDISTRIBUTION_ERROR, status, 0); return; } KeAcquireSpinLock(&t->_spinLock, &irql); t->_state.BytesRedistributed += ioPacket->Length; KeReleaseSpinLock(&t->_spinLock, irql); t->PropogateStateChanges(RedistributionSyncPhase6, syncPacket); } VOID RedistributionSyncPhase4( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for the verify write part of a sync. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP ioPacket = (PREDISTRIBUTION_TP) TransferPacket; PREDISTRIBUTION_SYNC_TP syncPacket = (PREDISTRIBUTION_SYNC_TP) ioPacket->MasterPacket; PREDISTRIBUTION t = syncPacket->Redistribution; NTSTATUS status = ioPacket->IoStatus.Status; KIRQL irql; if (FsRtlIsTotalDeviceFailure(status)) { KeAcquireSpinLock(&t->_spinLock, &irql); t->_syncOk = TRUE; KeReleaseSpinLock(&t->_spinLock, irql); RedistributionCompositeVolumeCompletionRoutine(syncPacket->Context, status); delete syncPacket; return; } if (!NT_SUCCESS(status)) { ioPacket->CompletionRoutine = RedistributionSyncPhase5; t->CarefulWrite(ioPacket); return; } KeAcquireSpinLock(&t->_spinLock, &irql); t->_state.BytesRedistributed += ioPacket->Length; KeReleaseSpinLock(&t->_spinLock, irql); t->PropogateStateChanges(RedistributionSyncPhase6, syncPacket); } VOID RedistributionSyncPhase3( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for the initial write part of a sync. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP ioPacket = (PREDISTRIBUTION_TP) TransferPacket; PREDISTRIBUTION_SYNC_TP syncPacket = (PREDISTRIBUTION_SYNC_TP) ioPacket->MasterPacket; PREDISTRIBUTION t = syncPacket->Redistribution; NTSTATUS status = ioPacket->IoStatus.Status; KIRQL irql; if (FsRtlIsTotalDeviceFailure(status)) { KeAcquireSpinLock(&t->_spinLock, &irql); t->_syncOk = TRUE; KeReleaseSpinLock(&t->_spinLock, irql); RedistributionCompositeVolumeCompletionRoutine(syncPacket->Context, status); delete syncPacket; return; } if (!NT_SUCCESS(status)) { ioPacket->CompletionRoutine = RedistributionSyncPhase5; t->CarefulWrite(ioPacket); return; } ioPacket->CompletionRoutine = RedistributionSyncPhase4; t->VerifyWrite(ioPacket); } VOID RedistributionSyncPhase2( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for the read part of a sync. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP ioPacket = (PREDISTRIBUTION_TP) TransferPacket; PREDISTRIBUTION_SYNC_TP syncPacket = (PREDISTRIBUTION_SYNC_TP) ioPacket->MasterPacket; PREDISTRIBUTION t = syncPacket->Redistribution; NTSTATUS status = ioPacket->IoStatus.Status; LONGLONG rowSize, rowNum, rowOffset, firstSize; KIRQL irql; KeAcquireSpinLock(&t->_spinLock, &irql); if (t->_stopSyncs) { t->_syncOk = TRUE; KeReleaseSpinLock(&t->_spinLock, irql); RedistributionCompositeVolumeCompletionRoutine(syncPacket->Context, STATUS_SUCCESS); delete syncPacket; return; } KeReleaseSpinLock(&t->_spinLock, irql); if (FsRtlIsTotalDeviceFailure(status)) { KeAcquireSpinLock(&t->_spinLock, &irql); t->_syncOk = TRUE; KeReleaseSpinLock(&t->_spinLock, irql); RedistributionCompositeVolumeCompletionRoutine(syncPacket->Context, status); delete syncPacket; return; } if (!NT_SUCCESS(status)) { t->MaxTransfer(ioPacket); return; } rowSize = t->_totalWidth*t->_stripeSize; rowNum = ioPacket->Offset/rowSize; rowOffset = ioPacket->Offset%rowSize; firstSize = t->_firstWidth*t->_stripeSize; ioPacket->CompletionRoutine = RedistributionSyncPhase3; ioPacket->ReadPacket = FALSE; if (rowOffset < firstSize) { ioPacket->Offset = rowNum*firstSize + rowOffset; } else { ioPacket->Offset = rowNum*(rowSize - firstSize) + rowOffset - firstSize; ioPacket->TargetVolume = t->GetMemberUnprotected(1); ioPacket->WhichMember = 1; } TRANSFER(ioPacket); } VOID RedistributionSyncPhase1( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for the lock part of a sync. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_SYNC_TP syncPacket = (PREDISTRIBUTION_SYNC_TP) TransferPacket; PREDISTRIBUTION t = syncPacket->Redistribution; PREDISTRIBUTION_TP packet = &syncPacket->IoPacket; packet->Offset = t->_state.BytesRedistributed; if (packet->Offset + packet->Length > t->_firstSize) { packet->Length = (ULONG) (t->_firstSize - packet->Offset); } packet->CompletionRoutine = RedistributionSyncPhase2; packet->TargetVolume = t->GetMemberUnprotected(0); packet->Thread = PsGetCurrentThread(); packet->ReadPacket = TRUE; packet->WhichMember = 0; TRANSFER(packet); } VOID REDISTRIBUTION::StartSyncOperations( IN BOOLEAN RegenerateOrphans, IN FT_COMPLETION_ROUTINE CompletionRoutine, IN PVOID Context ) /*++ Routine Description: This routine starts off the redistribution of the data. Arguments: RegenerateOrphans - Supplies whether or not to try and regenerate orphaned members. CompletionRoutine - Supplies the completion routine. Context - Supplies the context for the completion routine. Return Value: None. --*/ { PFT_COMPLETION_ROUTINE_CONTEXT context; PREDISTRIBUTION_SYNC_TP syncPacket; KIRQL irql; PREDISTRIBUTION_TP packet; if (_redistributionComplete) { COMPOSITE_FT_VOLUME::StartSyncOperations(RegenerateOrphans, CompletionRoutine, Context); return; } context = (PFT_COMPLETION_ROUTINE_CONTEXT) ExAllocatePool(NonPagedPool, sizeof(FT_COMPLETION_ROUTINE_CONTEXT)); if (!context) { CompletionRoutine(Context, STATUS_INSUFFICIENT_RESOURCES); return; } syncPacket = new REDISTRIBUTION_SYNC_TP; if (!syncPacket) { ExFreePool(context); CompletionRoutine(Context, STATUS_INSUFFICIENT_RESOURCES); return; } if (!syncPacket->IoPacket.AllocateMdl(_stripeSize)) { delete syncPacket; ExFreePool(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( RegenerateOrphans, RedistributionCompositeVolumeCompletionRoutine, context); KeAcquireSpinLock(&_spinLock, &irql); if (_syncOk) { _syncOk = FALSE; _stopSyncs = FALSE; } else { KeReleaseSpinLock(&_spinLock, irql); RedistributionCompositeVolumeCompletionRoutine(context, STATUS_SUCCESS); return; } KeReleaseSpinLock(&_spinLock, irql); syncPacket->Mdl = NULL; syncPacket->Offset = _state.BytesRedistributed; syncPacket->Length = _stripeSize; syncPacket->CompletionRoutine = RedistributionSyncPhase1; syncPacket->TargetVolume = this; syncPacket->Thread = PsGetCurrentThread(); syncPacket->IrpFlags = SL_OVERRIDE_VERIFY_VOLUME; syncPacket->ReadPacket = TRUE; syncPacket->Context = context; syncPacket->Redistribution = this; packet = &syncPacket->IoPacket; packet->Length = _stripeSize; packet->IrpFlags = SL_OVERRIDE_VERIFY_VOLUME; packet->MasterPacket = syncPacket; packet->Redistribution = this; _overlappedIoManager.AcquireIoRegion(syncPacket, TRUE); } VOID REDISTRIBUTION::StopSyncOperations( ) /*++ Routine Description: This routine stops all sync operations. Arguments: None. Return Value: None. --*/ { KIRQL irql; COMPOSITE_FT_VOLUME::StopSyncOperations(); KeAcquireSpinLock(&_spinLock, &irql); _stopSyncs = TRUE; KeReleaseSpinLock(&_spinLock, irql); } LONGLONG REDISTRIBUTION::QueryVolumeSize( ) /*++ Routine Description: Returns the number of bytes on the entire volume. Arguments: None. Return Value: The volume size in bytes. --*/ { return _redistributionComplete ? _totalSize : _firstSize; } VOID REDISTRIBUTION::CompleteNotification( IN BOOLEAN IoPending ) /*++ Routine Description: This routine is called to notify the volume that it is complete and to therefore prepare for incoming requests. Arguments: IoPending - Supplies whether or not there is IO pending. Return Value: None. --*/ { LONGLONG firstRowSize, numRows, secondRowSize, tmpNumRows; COMPOSITE_FT_VOLUME::CompleteNotification(IoPending); _firstSize = GetMember(0)->QueryVolumeSize(); firstRowSize = _firstWidth*_stripeSize; numRows = _firstSize/firstRowSize; secondRowSize = (_totalWidth - _firstWidth)*_stripeSize; tmpNumRows = GetMember(1)->QueryVolumeSize()/secondRowSize; if (tmpNumRows < numRows) { numRows = tmpNumRows; } _totalSize = numRows*_totalWidth*_stripeSize; if (_state.BytesRedistributed >= _firstSize) { _redistributionComplete = TRUE; } } VOID REDISTRIBUTION::NewStateArrival( IN PVOID NewStateInstance ) /*++ Routine Description: This routine takes the new state instance arrival combined with its current state to come up with the new current state for the volume. If the two states cannot be reconciled then this routine returns FALSE indicating that the volume is invalid and should be broken into its constituant parts. Arguments: NewStateInstance - Supplies the new state instance. Return Value: None. --*/ { BOOLEAN changed = FALSE; PFT_REDISTRIBUTION_STATE_INFORMATION state; state = (PFT_REDISTRIBUTION_STATE_INFORMATION) NewStateInstance; if (state->BytesRedistributed > _state.BytesRedistributed) { _state.BytesRedistributed = state->BytesRedistributed; changed = TRUE; } if (changed) { PropogateStateChanges(NULL, NULL); } } VOID RedistributionTransferCompletionRoutine( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: Completion routine for REDISTRIBUTION::RedistributionDispatch. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP transferPacket = (PREDISTRIBUTION_TP) TransferPacket; PTRANSFER_PACKET masterPacket = transferPacket->MasterPacket; NTSTATUS status = transferPacket->IoStatus.Status; KIRQL irql; if (!NT_SUCCESS(status)) { KeAcquireSpinLock(&masterPacket->SpinLock, &irql); if (FtpIsWorseStatus(status, masterPacket->IoStatus.Status)) { masterPacket->IoStatus.Status = status; } KeReleaseSpinLock(&masterPacket->SpinLock, irql); } delete transferPacket; if (!InterlockedDecrement(&masterPacket->RefCount)) { masterPacket->CompletionRoutine(masterPacket); } } VOID REDISTRIBUTION::RedistributeTransfer( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This routine launches the given transfer packet using the new redistributed data allocation scheme. Arguments: TransferPacket - Supplies the transfer packet to launch. Return Value: None. --*/ { LONGLONG begin, end; LONGLONG rowSize, firstSize, rowBegin, rowOffsetBegin, rowEnd, rowOffsetEnd; ULONG numRequests, i, numRows; PCHAR vp; LIST_ENTRY q; LONGLONG off, off2; ULONG len, len2; USHORT whichMember; BOOLEAN two; PREDISTRIBUTION_TP p; PLIST_ENTRY l; begin = TransferPacket->Offset; end = TransferPacket->Offset + TransferPacket->Length; rowSize = _totalWidth*_stripeSize; rowBegin = begin/rowSize; rowOffsetBegin = begin%rowSize; rowEnd = end/rowSize; rowOffsetEnd = end%rowSize; firstSize = _firstWidth*_stripeSize; if (TransferPacket->Mdl) { vp = (PCHAR) MmGetMdlVirtualAddress(TransferPacket->Mdl); } InitializeListHead(&q); numRows = (ULONG) (rowEnd - rowBegin + 1); numRequests = 0; for (i = 0; i < numRows; i++) { if (i == 0) { if (numRows == 1) { if (rowOffsetBegin < firstSize) { if (rowOffsetEnd > firstSize) { two = TRUE; whichMember = 0; off = rowBegin*firstSize + rowOffsetBegin; len = (ULONG) (firstSize - rowOffsetBegin); off2 = rowBegin*(rowSize - firstSize); len2 = (ULONG) (rowOffsetEnd - firstSize); } else { two = FALSE; whichMember = 0; off = rowBegin*firstSize + rowOffsetBegin; len = (ULONG) (rowOffsetEnd - rowOffsetBegin); } } else { two = FALSE; whichMember = 1; off = rowBegin*(rowSize - firstSize) + rowOffsetBegin - firstSize; len = (ULONG) (rowOffsetEnd - rowOffsetBegin); } } else { if (rowOffsetBegin < firstSize) { two = TRUE; whichMember = 0; off = rowBegin*firstSize + rowOffsetBegin; len = (ULONG) (firstSize - rowOffsetBegin); off2 = rowBegin*(rowSize - firstSize); len2 = (ULONG) (rowSize - firstSize); } else { two = FALSE; whichMember = 1; off = rowBegin*(rowSize - firstSize) + rowOffsetBegin - firstSize; len = (ULONG) (rowSize - rowOffsetBegin); } } } else if (i == numRows - 1) { if (!rowOffsetEnd) { continue; } if (rowOffsetEnd > firstSize) { two = TRUE; whichMember = 0; off = rowEnd*firstSize; len = (ULONG) firstSize; off2 = rowEnd*(rowSize - firstSize); len2 = (ULONG) (rowOffsetEnd - firstSize); } else { two = FALSE; whichMember = 0; off = rowEnd*firstSize; len = (ULONG) rowOffsetEnd; } } else { two = TRUE; whichMember = 0; len = (ULONG) firstSize; len2 = (ULONG) (rowSize - firstSize); off = (rowBegin + i)*len; off2 = (rowBegin + i)*len2; } p = new REDISTRIBUTION_TP; if (!p) { break; } if (!two && numRows == 1) { p->Mdl = TransferPacket->Mdl; } else { if (TransferPacket->Mdl) { if (p->AllocateMdl(vp, len)) { IoBuildPartialMdl(TransferPacket->Mdl, p->Mdl, vp, len); } else { delete p; break; } vp += len; } else { p->Mdl = TransferPacket->Mdl; } } p->Length = len; p->Offset = off; p->CompletionRoutine = RedistributionTransferCompletionRoutine; p->TargetVolume = GetMemberUnprotected(whichMember); p->Thread = TransferPacket->Thread; p->IrpFlags = TransferPacket->IrpFlags; p->ReadPacket = TransferPacket->ReadPacket; p->SpecialRead = TransferPacket->SpecialRead; p->MasterPacket = TransferPacket; p->Redistribution = this; p->WhichMember = whichMember; InsertTailList(&q, &p->QueueEntry); numRequests++; if (!two) { continue; } p = new REDISTRIBUTION_TP; if (!p) { break; } if (TransferPacket->Mdl) { if (p->AllocateMdl(vp, len2)) { IoBuildPartialMdl(TransferPacket->Mdl, p->Mdl, vp, len2); } else { delete p; break; } vp += len2; } else { p->Mdl = TransferPacket->Mdl; } p->Length = len2; p->Offset = off2; p->CompletionRoutine = RedistributionTransferCompletionRoutine; p->TargetVolume = GetMemberUnprotected(1); p->Thread = TransferPacket->Thread; p->IrpFlags = TransferPacket->IrpFlags; p->ReadPacket = TransferPacket->ReadPacket; p->SpecialRead = TransferPacket->SpecialRead; p->MasterPacket = TransferPacket; p->Redistribution = this; p->WhichMember = 1; InsertTailList(&q, &p->QueueEntry); numRequests++; } if (i < numRows) { while (!IsListEmpty(&q)) { l = RemoveHeadList(&q); p = CONTAINING_RECORD(l, REDISTRIBUTION_TP, QueueEntry); delete p; } TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } KeInitializeSpinLock(&TransferPacket->SpinLock); TransferPacket->IoStatus.Status = STATUS_SUCCESS; TransferPacket->IoStatus.Information = TransferPacket->Length; TransferPacket->RefCount = numRequests; while (!IsListEmpty(&q)) { l = RemoveHeadList(&q); p = CONTAINING_RECORD(l, REDISTRIBUTION_TP, QueueEntry); TRANSFER(p); } } VOID RedistributeReplaceBadSectorCompletion( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: Completion routine for REDISTRIBUTION::RedistributionReplaceBadSector. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP transferPacket = (PREDISTRIBUTION_TP) TransferPacket; PTRANSFER_PACKET masterPacket = transferPacket->MasterPacket; masterPacket->IoStatus = transferPacket->IoStatus; delete transferPacket; masterPacket->CompletionRoutine(masterPacket); } VOID REDISTRIBUTION::RedistributeReplaceBadSector( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This routine launches the given transfer packet using the new redistributed data allocation scheme. Arguments: TransferPacket - Supplies the transfer packet to launch. Return Value: None. --*/ { LONGLONG begin, rowSize, rowBegin, rowOffsetBegin, firstSize, offset; USHORT whichMember; PREDISTRIBUTION_TP p; begin = TransferPacket->Offset; rowSize = _totalWidth*_stripeSize; rowBegin = begin/rowSize; rowOffsetBegin = begin%rowSize; firstSize = _firstWidth*_stripeSize; if (rowOffsetBegin < firstSize) { offset = rowBegin*firstSize + rowOffsetBegin; whichMember = 0; } else { offset = rowBegin*(rowSize - firstSize) + rowOffsetBegin - firstSize; whichMember = 1; } p = new REDISTRIBUTION_TP; if (!p) { TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } p->Mdl = TransferPacket->Mdl; p->Length = TransferPacket->Length; p->Offset = offset; p->CompletionRoutine = RedistributeReplaceBadSectorCompletion; p->TargetVolume = GetMemberUnprotected(whichMember); p->Thread = TransferPacket->Thread; p->IrpFlags = TransferPacket->IrpFlags; p->ReadPacket = TransferPacket->ReadPacket; p->SpecialRead = TransferPacket->SpecialRead; p->MasterPacket = TransferPacket; p->Redistribution = this; p->WhichMember = whichMember; p->TargetVolume->ReplaceBadSector(p); } VOID RedistributionPropogateStateChangesWorker( IN PVOID WorkItem ) /*++ Routine Description: This routine is a worker thread routine for propogating state changes. Arguments: Mirror - Supplies a pointer to the mirror object. Return Value: None. --*/ { PPROPOGATE_CHANGES_WORK_ITEM workItem = (PPROPOGATE_CHANGES_WORK_ITEM) WorkItem; PREDISTRIBUTION t = workItem->Redistribution; NTSTATUS status; KIRQL irql; FT_REDISTRIBUTION_STATE_INFORMATION state; status = FtpAcquireWithTimeout(t->_rootExtension); if (!NT_SUCCESS(status)) { if (workItem->CompletionRoutine) { workItem->CompletionRoutine(workItem->Context, status); } return; } KeAcquireSpinLock(&t->_spinLock, &irql); RtlCopyMemory(&state, &t->_state, sizeof(state)); KeReleaseSpinLock(&t->_spinLock, irql); status = t->_diskInfoSet->WriteStateInformation(t->QueryLogicalDiskId(), &state, sizeof(state)); FtpRelease(t->_rootExtension); if (workItem->CompletionRoutine) { workItem->CompletionRoutine(workItem->Context, status); } } VOID REDISTRIBUTION::PropogateStateChanges( IN FT_COMPLETION_ROUTINE CompletionRoutine, IN PVOID Context ) /*++ Routine Description: This routine propogates the changes in the local memory state to the on disk state. Arguments: CompletionRoutine - Supplies the completion routine. Context - Supplies the context. Return Value: None. --*/ { PPROPOGATE_CHANGES_WORK_ITEM workItem; workItem = (PPROPOGATE_CHANGES_WORK_ITEM) ExAllocatePool(NonPagedPool, sizeof(PROPOGATE_CHANGES_WORK_ITEM)); if (!workItem) { return; } workItem->Redistribution = this; workItem->CompletionRoutine = CompletionRoutine; workItem->Context = Context; ExInitializeWorkItem(workItem, RedistributionPropogateStateChangesWorker, workItem); FtpQueueWorkItem(_rootExtension, workItem); } VOID RedistributionMaxTransferCompletionRoutine( 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. --*/ { PREDISTRIBUTION_TP subPacket = (PREDISTRIBUTION_TP) TransferPacket; PREDISTRIBUTION_TP masterPacket = (PREDISTRIBUTION_TP) subPacket->MasterPacket; NTSTATUS status = subPacket->IoStatus.Status; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->IoStatus = subPacket->IoStatus; delete subPacket; masterPacket->CompletionRoutine(masterPacket); return; } if (subPacket->Offset + subPacket->Length == masterPacket->Offset + masterPacket->Length) { delete 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); } VOID REDISTRIBUTION::MaxTransfer( IN OUT PREDISTRIBUTION_TP TransferPacket ) /*++ Routine Description: This routine propogates sector by sector the given transfer packet, ignoring errors. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP subPacket; TransferPacket->IoStatus.Status = STATUS_SUCCESS; TransferPacket->IoStatus.Information = TransferPacket->Length; subPacket = new REDISTRIBUTION_TP; if (!subPacket) { TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } subPacket->Length = QuerySectorSize(); subPacket->Offset = TransferPacket->Offset; subPacket->CompletionRoutine = RedistributionMaxTransferCompletionRoutine; subPacket->TargetVolume = TransferPacket->TargetVolume; subPacket->Thread = TransferPacket->Thread; subPacket->IrpFlags = TransferPacket->IrpFlags; subPacket->ReadPacket = TransferPacket->ReadPacket; subPacket->MasterPacket = TransferPacket; subPacket->Redistribution = this; subPacket->WhichMember = TransferPacket->WhichMember; if (!subPacket->AllocateMdl((PVOID) (PAGE_SIZE - 1), subPacket->Length)) { delete subPacket; TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } IoBuildPartialMdl(TransferPacket->Mdl, subPacket->Mdl, MmGetMdlVirtualAddress(TransferPacket->Mdl), subPacket->Length); TRANSFER(subPacket); } VOID RedistributionVerifyWriteCompletion( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for a verify write operation. Arguments: TransferPacket - Supplies the subordinate transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP verifyPacket = (PREDISTRIBUTION_TP) TransferPacket; PREDISTRIBUTION_TP masterPacket = (PREDISTRIBUTION_TP) verifyPacket->MasterPacket; NTSTATUS status = verifyPacket->IoStatus.Status; PVOID p, q; ULONG l; if (!NT_SUCCESS(status)) { masterPacket->IoStatus = verifyPacket->IoStatus; delete verifyPacket; masterPacket->CompletionRoutine(masterPacket); return; } p = MmGetSystemAddressForMdl(verifyPacket->Mdl); q = MmGetSystemAddressForMdl(masterPacket->Mdl); l = (ULONG)RtlCompareMemory(p, q, verifyPacket->Length); if (l != verifyPacket->Length) { masterPacket->IoStatus.Status = STATUS_DEVICE_DATA_ERROR; masterPacket->IoStatus.Information = 0; } delete verifyPacket; masterPacket->CompletionRoutine(masterPacket); } VOID REDISTRIBUTION::VerifyWrite( IN OUT PREDISTRIBUTION_TP TransferPacket ) /*++ Routine Description: This routine verifies that the given write was success by reading and comparing. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_TP verifyPacket; verifyPacket = new REDISTRIBUTION_TP; if (!verifyPacket) { TransferPacket->CompletionRoutine(TransferPacket); return; } if (!verifyPacket->AllocateMdl(TransferPacket->Length)) { delete verifyPacket; TransferPacket->CompletionRoutine(TransferPacket); return; } verifyPacket->Length = TransferPacket->Length; verifyPacket->Offset = TransferPacket->Offset; verifyPacket->CompletionRoutine = RedistributionVerifyWriteCompletion; verifyPacket->TargetVolume = TransferPacket->TargetVolume; verifyPacket->Thread = TransferPacket->Thread; verifyPacket->IrpFlags = TransferPacket->IrpFlags; verifyPacket->ReadPacket = TRUE; verifyPacket->MasterPacket = TransferPacket; verifyPacket->Redistribution = this; verifyPacket->WhichMember = TransferPacket->WhichMember; TRANSFER(verifyPacket); } VOID RedistributionCarefulWritePhase1( IN OUT PTRANSFER_PACKET TransferPacket ); VOID RedistributionCarefulWritePhase5( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for a sector read subordinate to a carefule write operation after a replace bad sector. Arguments: TransferPacket - Supplies the subordinate transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_CW_TP subPacket = (PREDISTRIBUTION_CW_TP) TransferPacket; PREDISTRIBUTION_TP masterPacket = (PREDISTRIBUTION_TP) subPacket->MasterPacket; NTSTATUS status = subPacket->IoStatus.Status; PVOID p, q; ULONG l; if (NT_SUCCESS(status)) { p = MmGetSystemAddressForMdl(subPacket->PartialMdl); q = MmGetSystemAddressForMdl(subPacket->VerifyMdl); l = (ULONG)RtlCompareMemory(p, q, subPacket->Length); } else { l = 0; } if (l != subPacket->Length) { masterPacket->IoStatus = subPacket->IoStatus; delete subPacket; masterPacket->CompletionRoutine(masterPacket); return; } if (subPacket->Offset + subPacket->Length == masterPacket->Offset + masterPacket->Length) { delete subPacket; masterPacket->IoStatus.Status = STATUS_SUCCESS; masterPacket->IoStatus.Information = masterPacket->Length; masterPacket->CompletionRoutine(masterPacket); return; } subPacket->Offset += subPacket->Length; subPacket->CompletionRoutine = RedistributionCarefulWritePhase1; subPacket->ReadPacket = FALSE; subPacket->Mdl = subPacket->PartialMdl; MmPrepareMdlForReuse(subPacket->Mdl); IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl, (PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) + (ULONG) (subPacket->Offset - masterPacket->Offset), subPacket->Length); TRANSFER(subPacket); } VOID RedistributionCarefulWritePhase4( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for a sector write subordinate to a carefule write operation after a sector replace operation. Arguments: TransferPacket - Supplies the subordinate transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_CW_TP subPacket = (PREDISTRIBUTION_CW_TP) TransferPacket; PREDISTRIBUTION_TP masterPacket = (PREDISTRIBUTION_TP) subPacket->MasterPacket; NTSTATUS status = subPacket->IoStatus.Status; if (!NT_SUCCESS(status)) { masterPacket->IoStatus = subPacket->IoStatus; delete subPacket; masterPacket->CompletionRoutine(masterPacket); return; } subPacket->Mdl = subPacket->VerifyMdl; subPacket->CompletionRoutine = RedistributionCarefulWritePhase5; subPacket->ReadPacket = TRUE; TRANSFER(subPacket); } VOID RedistributionCarefulWritePhase3( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for a sector replace subordinate to a carefule write operation. Arguments: TransferPacket - Supplies the subordinate transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_CW_TP subPacket = (PREDISTRIBUTION_CW_TP) TransferPacket; PREDISTRIBUTION_TP masterPacket = (PREDISTRIBUTION_TP) subPacket->MasterPacket; subPacket->CompletionRoutine = RedistributionCarefulWritePhase4; subPacket->ReadPacket = FALSE; subPacket->Mdl = subPacket->PartialMdl; MmPrepareMdlForReuse(subPacket->Mdl); IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl, (PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) + (ULONG) (subPacket->Offset - masterPacket->Offset), subPacket->Length); TRANSFER(subPacket); } VOID RedistributionCarefulWritePhase2( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for a sector read subordinate to a carefule write operation. Arguments: TransferPacket - Supplies the subordinate transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_CW_TP subPacket = (PREDISTRIBUTION_CW_TP) TransferPacket; PREDISTRIBUTION_TP masterPacket = (PREDISTRIBUTION_TP) subPacket->MasterPacket; NTSTATUS status = subPacket->IoStatus.Status; PVOID p, q; ULONG l; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->IoStatus = subPacket->IoStatus; delete subPacket; masterPacket->CompletionRoutine(masterPacket); return; } if (NT_SUCCESS(status)) { p = MmGetSystemAddressForMdl(subPacket->PartialMdl); q = MmGetSystemAddressForMdl(subPacket->VerifyMdl); l = (ULONG)RtlCompareMemory(p, q, subPacket->Length); } else { l = 0; } if (l != subPacket->Length) { subPacket->CompletionRoutine = RedistributionCarefulWritePhase3; subPacket->TargetVolume->ReplaceBadSector(subPacket); return; } if (subPacket->Offset + subPacket->Length == masterPacket->Offset + masterPacket->Length) { delete subPacket; masterPacket->IoStatus.Status = STATUS_SUCCESS; masterPacket->IoStatus.Information = masterPacket->Length; masterPacket->CompletionRoutine(masterPacket); return; } subPacket->Offset += subPacket->Length; subPacket->CompletionRoutine = RedistributionCarefulWritePhase1; subPacket->ReadPacket = FALSE; subPacket->Mdl = subPacket->PartialMdl; MmPrepareMdlForReuse(subPacket->Mdl); IoBuildPartialMdl(masterPacket->Mdl, subPacket->Mdl, (PCHAR) MmGetMdlVirtualAddress(masterPacket->Mdl) + (ULONG) (subPacket->Offset - masterPacket->Offset), subPacket->Length); TRANSFER(subPacket); } VOID RedistributionCarefulWritePhase1( IN OUT PTRANSFER_PACKET TransferPacket ) /*++ Routine Description: This is the completion routine for a sector write subordinate to a carefule write operation. Arguments: TransferPacket - Supplies the subordinate transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_CW_TP subPacket = (PREDISTRIBUTION_CW_TP) TransferPacket; PREDISTRIBUTION_TP masterPacket = (PREDISTRIBUTION_TP) subPacket->MasterPacket; NTSTATUS status = subPacket->IoStatus.Status; if (FsRtlIsTotalDeviceFailure(status)) { masterPacket->IoStatus = subPacket->IoStatus; delete subPacket; masterPacket->CompletionRoutine(masterPacket); return; } if (!NT_SUCCESS(status)) { subPacket->CompletionRoutine = RedistributionCarefulWritePhase3; subPacket->TargetVolume->ReplaceBadSector(subPacket); return; } subPacket->Mdl = subPacket->VerifyMdl; subPacket->CompletionRoutine = RedistributionCarefulWritePhase2; subPacket->ReadPacket = TRUE; TRANSFER(subPacket); } VOID REDISTRIBUTION::CarefulWrite( IN OUT PREDISTRIBUTION_TP TransferPacket ) /*++ Routine Description: This routine writes the given packet, sector by sector, replacing bad sectors if need be. Arguments: TransferPacket - Supplies the transfer packet. Return Value: None. --*/ { PREDISTRIBUTION_CW_TP subPacket; ASSERT(!TransferPacket->ReadPacket); subPacket = new REDISTRIBUTION_CW_TP; if (!subPacket) { TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } subPacket->Length = QuerySectorSize(); subPacket->Offset = TransferPacket->Offset; subPacket->CompletionRoutine = RedistributionCarefulWritePhase1; subPacket->TargetVolume = TransferPacket->TargetVolume; subPacket->Thread = TransferPacket->Thread; subPacket->IrpFlags = TransferPacket->IrpFlags; subPacket->ReadPacket = FALSE; subPacket->MasterPacket = TransferPacket; subPacket->Redistribution = TransferPacket->Redistribution; subPacket->WhichMember = TransferPacket->WhichMember; if (!subPacket->AllocateMdls(subPacket->Length)) { delete subPacket; TransferPacket->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; TransferPacket->IoStatus.Information = 0; TransferPacket->CompletionRoutine(TransferPacket); return; } subPacket->Mdl = subPacket->PartialMdl; IoBuildPartialMdl(TransferPacket->Mdl, subPacket->Mdl, MmGetMdlVirtualAddress(TransferPacket->Mdl), subPacket->Length); TRANSFER(subPacket); } NTSTATUS REDISTRIBUTION::QueryPhysicalOffsets( IN LONGLONG LogicalOffset, OUT PVOLUME_PHYSICAL_OFFSET* PhysicalOffsets, OUT PULONG NumberOfPhysicalOffsets ) /*++ Routine Description: This routine returns physical disk and offset for a given volume logical offset. Arguments: LogicalOffset - Supplies the logical offset PhysicalOffsets - Returns the physical offsets NumberOfPhysicalOffsets - Returns the number of physical offsets Return Value: NTSTATUS --*/ { USHORT n, whichMember, whichStripeInSet; LONGLONG whichStripe, whichSet, whichRow; LONGLONG bytesRedistributed, logicalOffsetInMember; PFT_VOLUME vol; KIRQL irql; if (LogicalOffset < 0) { return STATUS_INVALID_PARAMETER; } if (_redistributionComplete) { if (_totalSize <= LogicalOffset) { return STATUS_INVALID_PARAMETER; } } else { KeAcquireSpinLock(&_spinLock, &irql); bytesRedistributed = _state.BytesRedistributed; KeReleaseSpinLock(&_spinLock, irql); if (bytesRedistributed <= LogicalOffset) { return STATUS_INVALID_PARAMETER; } } ASSERT(_stripeSize); ASSERT(_totalWidth); whichStripe = LogicalOffset/_stripeSize; whichSet = whichStripe/_totalWidth; whichStripeInSet = (USHORT) (whichStripe%_totalWidth); if (whichStripeInSet < _firstWidth) { whichMember = 0; whichRow = whichSet*_firstWidth + whichStripeInSet; } else { whichMember = 1; whichRow = whichSet*(_totalWidth-_firstWidth) + whichStripeInSet - _firstWidth; } vol = GetMember(whichMember); if (!vol) { return STATUS_INVALID_PARAMETER; } logicalOffsetInMember = whichRow*_stripeSize + LogicalOffset%_stripeSize; return vol->QueryPhysicalOffsets(logicalOffsetInMember, PhysicalOffsets, NumberOfPhysicalOffsets); } NTSTATUS REDISTRIBUTION::QueryLogicalOffset( IN PVOLUME_PHYSICAL_OFFSET PhysicalOffset, OUT PLONGLONG LogicalOffset ) /*++ Routine Description: This routine returns the volume logical offset for a given disk number and physical offset. Arguments: PhysicalOffset - Supplies the physical offset LogicalOffset - Returns the logical offset Return Value: NTSTATUS --*/ { USHORT n, i, whichStripeInSet; LONGLONG whichStripe, whichSet, whichRow, bytesRedistributed; LONGLONG logicalOffset, logicalOffsetInMember; NTSTATUS status; PFT_VOLUME vol; KIRQL irql; n = QueryNumMembers(); ASSERT(n == 2); ASSERT(_stripeSize); ASSERT(_firstWidth); ASSERT(_totalWidth > _firstWidth); for (i = 0; i < n; i++) { vol = GetMember(i); if (!vol) { continue; } status = vol->QueryLogicalOffset(PhysicalOffset, &logicalOffsetInMember); if (NT_SUCCESS(status)) { whichRow = logicalOffsetInMember/_stripeSize; if (i == 0) { whichSet = whichRow/_firstWidth; whichStripeInSet = (USHORT) (whichRow%_firstWidth); } else { ASSERT(i == 1); whichSet = whichRow/(_totalWidth-_firstWidth); whichStripeInSet = (USHORT) (whichRow%(_totalWidth-_firstWidth)) + _firstWidth; } whichStripe = whichSet*_totalWidth + whichStripeInSet; logicalOffset = whichStripe*_stripeSize + logicalOffsetInMember%_stripeSize; if (_redistributionComplete) { if (_totalSize <= logicalOffset) { return STATUS_INVALID_PARAMETER; } } else { KeAcquireSpinLock(&_spinLock, &irql); bytesRedistributed = _state.BytesRedistributed; KeReleaseSpinLock(&_spinLock, irql); if (bytesRedistributed <= logicalOffset) { return STATUS_INVALID_PARAMETER; } } *LogicalOffset = logicalOffset; return status; } } return STATUS_INVALID_PARAMETER; }