You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1162 lines
31 KiB
1162 lines
31 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
pdo.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the dispatch routines for scsiport's physical device
|
|
objects
|
|
|
|
Authors:
|
|
|
|
Peter Wieland
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "port.h"
|
|
|
|
#if DBG
|
|
static const char *__file__ = __FILE__;
|
|
#endif
|
|
|
|
VOID
|
|
SpAdapterCleanup(
|
|
IN PADAPTER_EXTENSION DeviceExtension
|
|
);
|
|
|
|
VOID
|
|
SpReapChildren(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
);
|
|
|
|
BOOLEAN
|
|
SpTerminateAdapterSynchronized (
|
|
IN PADAPTER_EXTENSION Adapter
|
|
);
|
|
|
|
BOOLEAN
|
|
SpRemoveAdapterSynchronized(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
);
|
|
|
|
VOID
|
|
SpFlushAllRequests(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN UCHAR SrbStatus
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, SpDeleteLogicalUnit)
|
|
#pragma alloc_text(PAGE, SpRemoveLogicalUnit)
|
|
#pragma alloc_text(PAGE, SpWaitForRemoveLock)
|
|
#pragma alloc_text(PAGE, SpAdapterCleanup)
|
|
#pragma alloc_text(PAGE, SpReapChildren)
|
|
|
|
#pragma alloc_text(PAGELOCK, ScsiPortRemoveAdapter)
|
|
#endif
|
|
|
|
|
|
BOOLEAN
|
|
SpRemoveLogicalUnit(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN UCHAR RemoveType
|
|
)
|
|
|
|
{
|
|
PADAPTER_EXTENSION adapterExtension = LogicalUnit->AdapterExtension;
|
|
|
|
ULONG isRemoved;
|
|
ULONG oldDebugLevel;
|
|
|
|
PAGED_CODE();
|
|
|
|
if(LogicalUnit->CommonExtension.IsRemoved != REMOVE_COMPLETE) {
|
|
|
|
if(RemoveType == IRP_MN_REMOVE_DEVICE) {
|
|
|
|
SpWaitForRemoveLock(LogicalUnit->DeviceObject, SP_BASE_REMOVE_LOCK );
|
|
|
|
//
|
|
// If the device was claimed we should release it now.
|
|
//
|
|
|
|
if(LogicalUnit->IsClaimed) {
|
|
LogicalUnit->IsClaimed = FALSE;
|
|
LogicalUnit->IsLegacyClaim = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
DebugPrint((1, "SpRemoveLogicalUnit - %sremoving device %#p\n",
|
|
(RemoveType == IRP_MN_SURPRISE_REMOVAL) ? "surprise " : "",
|
|
LogicalUnit));
|
|
|
|
//
|
|
// If the lun isn't marked as missing yet or is marked as missing but
|
|
// PNP hasn't been informed yet then we cannot delete it. Set it back
|
|
// to the NO_REMOVE state so that we'll be able to attempt a rescan.
|
|
//
|
|
// Likewise if the lun is invisible then just swallow the remove
|
|
// operation now that we've cleared any existing claims.
|
|
//
|
|
|
|
if(RemoveType == IRP_MN_REMOVE_DEVICE) {
|
|
|
|
//
|
|
// If the device is not missing or is missing but is still
|
|
// enumerated then don't finish destroying it.
|
|
//
|
|
|
|
if((LogicalUnit->IsMissing == TRUE) &&
|
|
(LogicalUnit->IsEnumerated == FALSE)) {
|
|
|
|
// do nothing here - fall through and destroy the device.
|
|
|
|
} else {
|
|
|
|
DebugPrint((1, "SpRemoveLogicalUnit - device is not missing "
|
|
"and will not be destroyed\n"));
|
|
|
|
SpAcquireRemoveLock(LogicalUnit->DeviceObject, SP_BASE_REMOVE_LOCK);
|
|
|
|
LogicalUnit->CommonExtension.IsRemoved = NO_REMOVE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
} else if((LogicalUnit->IsVisible == FALSE) &&
|
|
(LogicalUnit->IsMissing == FALSE)) {
|
|
|
|
//
|
|
// The surprise remove came because the device is no longer
|
|
// visible. We don't want to destroy it.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Mark the device as uninitialized so that we'll go back and
|
|
// recreate all the necessary stuff if it gets restarted.
|
|
//
|
|
|
|
LogicalUnit->CommonExtension.IsInitialized = FALSE;
|
|
|
|
//
|
|
// Delete the device map entry for this one (if any).
|
|
//
|
|
|
|
SpDeleteDeviceMapEntry(&(LogicalUnit->CommonExtension));
|
|
|
|
if(RemoveType == IRP_MN_REMOVE_DEVICE) {
|
|
|
|
ASSERT(LogicalUnit->RequestTimeoutCounter == -1);
|
|
ASSERT(LogicalUnit->ReadyLogicalUnit == NULL);
|
|
ASSERT(LogicalUnit->PendingRequest == NULL);
|
|
ASSERT(LogicalUnit->BusyRequest == NULL);
|
|
ASSERT(LogicalUnit->QueueCount == 0);
|
|
|
|
LogicalUnit->CommonExtension.IsRemoved = REMOVE_COMPLETE;
|
|
|
|
//
|
|
// Yank this out of the logical unit list.
|
|
//
|
|
|
|
SpRemoveLogicalUnitFromBin(LogicalUnit->AdapterExtension,
|
|
LogicalUnit);
|
|
|
|
LogicalUnit->PathId = 0xff;
|
|
LogicalUnit->TargetId = 0xff;
|
|
LogicalUnit->Lun = 0xff;
|
|
|
|
//
|
|
// If this device wasn't temporary then delete it.
|
|
//
|
|
|
|
if(LogicalUnit->IsTemporary == FALSE) {
|
|
SpDeleteLogicalUnit(LogicalUnit);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpDeleteLogicalUnit(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will release any resources held for the logical unit, mark the
|
|
device extension as deleted, and call the io system to actually delete
|
|
the object. The device object will be deleted once it's reference count
|
|
drops to zero.
|
|
|
|
Arguments:
|
|
|
|
LogicalUnit - the device object for the logical unit to be deleted.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(LogicalUnit->ReadyLogicalUnit == NULL);
|
|
ASSERT(LogicalUnit->PendingRequest == NULL);
|
|
ASSERT(LogicalUnit->BusyRequest == NULL);
|
|
ASSERT(LogicalUnit->QueueCount == 0);
|
|
|
|
ASSERT(LogicalUnit->PathId == 0xff);
|
|
ASSERT(LogicalUnit->TargetId == 0xff);
|
|
ASSERT(LogicalUnit->Lun == 0xff);
|
|
|
|
//
|
|
// Unregister with WMI.
|
|
//
|
|
|
|
if(LogicalUnit->CommonExtension.WmiInitialized == TRUE) {
|
|
|
|
//
|
|
// Destroy all our WMI resources and unregister with WMI.
|
|
//
|
|
|
|
IoWMIRegistrationControl(LogicalUnit->DeviceObject,
|
|
WMIREG_ACTION_DEREGISTER);
|
|
|
|
//
|
|
// We should be asking the WmiFreeRequestList of remove some
|
|
// free cells.
|
|
|
|
LogicalUnit->CommonExtension.WmiInitialized = FALSE;
|
|
SpWmiDestroySpRegInfo(LogicalUnit->DeviceObject);
|
|
}
|
|
|
|
#if DBG
|
|
// ASSERT(LogicalUnit->CommonExtension.RemoveTrackingList == NULL);
|
|
ExDeleteNPagedLookasideList(
|
|
&(LogicalUnit->CommonExtension.RemoveTrackingLookasideList));
|
|
#endif
|
|
|
|
//
|
|
// If the request sense irp still exists, delete it.
|
|
//
|
|
|
|
if(LogicalUnit->RequestSenseIrp != NULL) {
|
|
IoFreeIrp(LogicalUnit->RequestSenseIrp);
|
|
LogicalUnit->RequestSenseIrp = NULL;
|
|
}
|
|
|
|
if(LogicalUnit->HwLogicalUnitExtension != NULL) {
|
|
ExFreePool(LogicalUnit->HwLogicalUnitExtension);
|
|
LogicalUnit->HwLogicalUnitExtension = NULL;
|
|
}
|
|
|
|
if(LogicalUnit->SerialNumber.Buffer != NULL) {
|
|
ExFreePool(LogicalUnit->SerialNumber.Buffer);
|
|
RtlInitAnsiString(&(LogicalUnit->SerialNumber), NULL);
|
|
}
|
|
|
|
if(LogicalUnit->DeviceIdentifierPage != NULL) {
|
|
ExFreePool(LogicalUnit->DeviceIdentifierPage);
|
|
LogicalUnit->DeviceIdentifierPage = NULL;
|
|
}
|
|
|
|
//
|
|
// If this lun is temporary then clear the RescanLun field in the adapter.
|
|
//
|
|
|
|
if(LogicalUnit->IsTemporary) {
|
|
ASSERT(LogicalUnit->AdapterExtension->RescanLun = LogicalUnit);
|
|
LogicalUnit->AdapterExtension->RescanLun = NULL;
|
|
} else {
|
|
ASSERT(LogicalUnit->AdapterExtension->RescanLun != LogicalUnit);
|
|
}
|
|
|
|
IoDeleteDevice(LogicalUnit->DeviceObject);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
ScsiPortRemoveAdapter(
|
|
IN PDEVICE_OBJECT AdapterObject,
|
|
IN BOOLEAN Surprise
|
|
)
|
|
{
|
|
PADAPTER_EXTENSION adapter = AdapterObject->DeviceExtension;
|
|
PCOMMON_EXTENSION commonExtension = AdapterObject->DeviceExtension;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_FDO(AdapterObject);
|
|
ASSERT(adapter->IsPnp);
|
|
|
|
//
|
|
// Set the flag PD_ADAPTER_REMOVED to keep scsiport from calling into the
|
|
// miniport after we've started this teardown.
|
|
//
|
|
|
|
if(Surprise == FALSE) {
|
|
PVOID sectionHandle;
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Wait until all outstanding requests have been completed. If the
|
|
// adapter was surprise removed, we don't need to wait on the remove
|
|
// lock again, since we already waited for it in the surprise remove
|
|
// path.
|
|
//
|
|
|
|
if (commonExtension->CurrentPnpState != IRP_MN_SURPRISE_REMOVAL) {
|
|
SpWaitForRemoveLock(AdapterObject, AdapterObject);
|
|
}
|
|
|
|
//
|
|
// If the device is started we should uninitialize the miniport and
|
|
// release it's resources. Fortunately this is exactly what stop does.
|
|
//
|
|
|
|
if((commonExtension->CurrentPnpState != IRP_MN_SURPRISE_REMOVAL) &&
|
|
((commonExtension->CurrentPnpState == IRP_MN_START_DEVICE) ||
|
|
(commonExtension->PreviousPnpState == IRP_MN_START_DEVICE))) {
|
|
|
|
//
|
|
// Okay. If this adapter can't support remove then we're dead
|
|
//
|
|
|
|
ASSERT(SpIsAdapterControlTypeSupported(adapter, ScsiStopAdapter) == TRUE);
|
|
|
|
//
|
|
// Stop the miniport now that it's safe.
|
|
//
|
|
|
|
SpEnableDisableAdapter(adapter, FALSE);
|
|
|
|
//
|
|
// Mark the adapter as removed.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
sectionHandle = MmLockPagableCodeSection(ScsiPortRemoveAdapter);
|
|
InterlockedIncrement(&SpPAGELOCKLockCount);
|
|
#endif
|
|
KeAcquireSpinLock(&(adapter->SpinLock), &oldIrql);
|
|
adapter->SynchronizeExecution(adapter->InterruptObject,
|
|
SpRemoveAdapterSynchronized,
|
|
adapter);
|
|
|
|
KeReleaseSpinLock(&(adapter->SpinLock), oldIrql);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
InterlockedDecrement(&SpPAGELOCKLockCount);
|
|
MmUnlockPagableImageSection(sectionHandle);
|
|
#endif
|
|
|
|
}
|
|
SpReapChildren(adapter);
|
|
}
|
|
|
|
if(commonExtension->WmiInitialized == TRUE) {
|
|
|
|
//
|
|
// Destroy all our WMI resources and unregister with WMI.
|
|
//
|
|
|
|
IoWMIRegistrationControl(AdapterObject, WMIREG_ACTION_DEREGISTER);
|
|
SpWmiRemoveFreeMiniPortRequestItems(adapter);
|
|
commonExtension->WmiInitialized = FALSE;
|
|
commonExtension->WmiMiniPortInitialized = FALSE;
|
|
}
|
|
|
|
//
|
|
// If we are surprise removed, the following gets executed twice, but
|
|
// it's safe to do so.
|
|
//
|
|
|
|
SpDeleteDeviceMapEntry(commonExtension);
|
|
SpDestroyAdapter(adapter, Surprise);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpWaitForRemoveLock(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID LockTag
|
|
)
|
|
{
|
|
PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Mark the thing as removing
|
|
//
|
|
|
|
commonExtension->IsRemoved = REMOVE_PENDING;
|
|
|
|
//
|
|
// Release our outstanding lock.
|
|
//
|
|
|
|
SpReleaseRemoveLock(DeviceObject, LockTag);
|
|
|
|
DebugPrint((4, "SpWaitForRemoveLock - Reference count is now %d\n",
|
|
commonExtension->RemoveLock));
|
|
|
|
KeWaitForSingleObject(&(commonExtension->RemoveEvent),
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
DebugPrint((4, "SpWaitForRemoveLock - removing device %#p\n",
|
|
DeviceObject));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpDestroyAdapter(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN BOOLEAN Surprise
|
|
)
|
|
{
|
|
SpReleaseAdapterResources(Adapter, FALSE, Surprise);
|
|
SpAdapterCleanup(Adapter);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpAdapterCleanup(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up the names associated with the specified adapter
|
|
and the i/o system counts.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Supplies a pointer to the device extension to be deleted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCOMMON_EXTENSION commonExtension = &(Adapter->CommonExtension);
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If we assigned a port number to this adapter then attempt to delete the
|
|
// symbolic links we created to it.
|
|
//
|
|
|
|
if(Adapter->PortNumber != -1) {
|
|
|
|
PWCHAR wideNameStrings[] = {L"\\Device\\ScsiPort%d",
|
|
L"\\DosDevices\\Scsi%d:"};
|
|
ULONG i;
|
|
|
|
for(i = 0; i < (sizeof(wideNameStrings) / sizeof(PWCHAR)); i++) {
|
|
WCHAR wideLinkName[64];
|
|
UNICODE_STRING unicodeLinkName;
|
|
|
|
swprintf(wideLinkName, wideNameStrings[i], Adapter->PortNumber);
|
|
RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
|
|
IoDeleteSymbolicLink(&unicodeLinkName);
|
|
}
|
|
|
|
Adapter->PortNumber = -1;
|
|
|
|
//
|
|
// Decrement the scsiport count.
|
|
//
|
|
|
|
IoGetConfigurationInformation()->ScsiPortCount--;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpReleaseAdapterResources(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN BOOLEAN Stop,
|
|
IN BOOLEAN Surprise
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function deletes all of the storage associated with a device
|
|
extension, disconnects from the timers and interrupts and then deletes the
|
|
object. This function can be called at any time during the initialization.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Supplies a pointer to the device extesnion to be deleted.
|
|
Surprise - This is redundant, but is used by stop, remove and surprise-remove
|
|
SurpriseRemoved - Indicates that the adapter has been surprise-removed
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PCOMMON_EXTENSION commonExtension = &(Adapter->CommonExtension);
|
|
ULONG j;
|
|
PVOID tempPointer;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
|
|
if(!Surprise && !Stop) {
|
|
|
|
//
|
|
// Free the Remove tracking lookaside list.
|
|
//
|
|
|
|
ExDeleteNPagedLookasideList(&(commonExtension->RemoveTrackingLookasideList));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Stop the time and disconnect the interrupt if they have been
|
|
// initialized. The interrupt object is connected after
|
|
// timer has been initialized, and the interrupt object is connected, but
|
|
// before the timer is started.
|
|
//
|
|
|
|
if(Adapter->DeviceObject->Timer != NULL) {
|
|
IoStopTimer(Adapter->DeviceObject);
|
|
KeCancelTimer(&(Adapter->MiniPortTimer));
|
|
}
|
|
|
|
if(Adapter->SynchronizeExecution != SpSynchronizeExecution) {
|
|
|
|
if (Adapter->InterruptObject) {
|
|
IoDisconnectInterrupt(Adapter->InterruptObject);
|
|
}
|
|
|
|
if (Adapter->InterruptObject2) {
|
|
IoDisconnectInterrupt(Adapter->InterruptObject2);
|
|
Adapter->InterruptObject2 = NULL;
|
|
}
|
|
|
|
//
|
|
// SpSynchronizeExecution expects to get a pointer to the
|
|
// adapter extension as the "interrupt" parameter.
|
|
//
|
|
|
|
Adapter->InterruptObject = (PVOID) Adapter;
|
|
Adapter->SynchronizeExecution = SpSynchronizeExecution;
|
|
}
|
|
|
|
//
|
|
// Delete the miniport's device extension
|
|
//
|
|
|
|
if (Adapter->HwDeviceExtension != NULL) {
|
|
|
|
PHW_DEVICE_EXTENSION devExt =
|
|
CONTAINING_RECORD(Adapter->HwDeviceExtension,
|
|
HW_DEVICE_EXTENSION,
|
|
HwDeviceExtension);
|
|
|
|
ExFreePool(devExt);
|
|
Adapter->HwDeviceExtension = NULL;
|
|
}
|
|
|
|
//
|
|
// Free the configuration information structure.
|
|
//
|
|
|
|
if (Adapter->PortConfig) {
|
|
ExFreePool(Adapter->PortConfig);
|
|
Adapter->PortConfig = NULL;
|
|
}
|
|
|
|
//
|
|
// Deallocate SCSIPORT WMI REGINFO information, if any.
|
|
//
|
|
|
|
SpWmiDestroySpRegInfo(Adapter->DeviceObject);
|
|
|
|
//
|
|
// Free the common buffer.
|
|
//
|
|
|
|
if (SpVerifyingCommonBuffer(Adapter)) {
|
|
|
|
SpFreeCommonBufferVrfy(Adapter);
|
|
|
|
} else {
|
|
|
|
if (Adapter->SrbExtensionBuffer != NULL &&
|
|
Adapter->CommonBufferSize != 0) {
|
|
|
|
if (Adapter->DmaAdapterObject == NULL) {
|
|
|
|
//
|
|
// Since there is no adapter just free the non-paged pool.
|
|
//
|
|
|
|
ExFreePool(Adapter->SrbExtensionBuffer);
|
|
|
|
} else {
|
|
|
|
if(Adapter->UncachedExtensionIsCommonBuffer == FALSE) {
|
|
MmFreeContiguousMemorySpecifyCache(Adapter->SrbExtensionBuffer,
|
|
Adapter->CommonBufferSize,
|
|
MmCached);
|
|
} else {
|
|
|
|
FreeCommonBuffer(
|
|
Adapter->DmaAdapterObject,
|
|
Adapter->CommonBufferSize,
|
|
Adapter->PhysicalCommonBuffer,
|
|
Adapter->SrbExtensionBuffer,
|
|
FALSE);
|
|
}
|
|
|
|
}
|
|
Adapter->SrbExtensionBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get rid of our dma adapter.
|
|
//
|
|
|
|
if(Adapter->DmaAdapterObject != NULL) {
|
|
PutDmaAdapter(Adapter->DmaAdapterObject);
|
|
Adapter->DmaAdapterObject = NULL;
|
|
}
|
|
|
|
//
|
|
// Free the SRB data array, if this is not a surprise remove. We should
|
|
// not free the lookaside list in case of a surprise remove, as some
|
|
// requests might have allocated SRB_DATA blocks w/o holding the adapter
|
|
// remove lock. All other resources are required only if we ere able to
|
|
// acquire the adapter remove lock, which we guarantee we won't at this point.
|
|
// It is safe to free the array during remove, because we delete all the LUN's
|
|
// before realeasing adapters resources
|
|
|
|
if( !Surprise ){
|
|
|
|
if (Adapter->SrbDataListInitialized) {
|
|
|
|
if(Adapter->EmergencySrbData != NULL) {
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&Adapter->SrbDataLookasideList,
|
|
Adapter->EmergencySrbData);
|
|
Adapter->EmergencySrbData = NULL;
|
|
|
|
}
|
|
|
|
ExDeleteNPagedLookasideList(&Adapter->SrbDataLookasideList);
|
|
Adapter->SrbDataListInitialized = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
if (Adapter->InquiryBuffer != NULL) {
|
|
ExFreePool(Adapter->InquiryBuffer);
|
|
Adapter->InquiryBuffer = NULL;
|
|
}
|
|
|
|
if (Adapter->InquirySenseBuffer != NULL) {
|
|
ExFreePool(Adapter->InquirySenseBuffer);
|
|
Adapter->InquirySenseBuffer = NULL;
|
|
}
|
|
|
|
if (Adapter->InquiryIrp != NULL) {
|
|
IoFreeIrp(Adapter->InquiryIrp);
|
|
Adapter->InquiryIrp = NULL;
|
|
}
|
|
|
|
if (Adapter->InquiryMdl != NULL) {
|
|
IoFreeMdl(Adapter->InquiryMdl);
|
|
Adapter->InquiryMdl = NULL;
|
|
}
|
|
|
|
//
|
|
// Unmap any mapped areas.
|
|
//
|
|
|
|
SpReleaseMappedAddresses(Adapter);
|
|
|
|
//
|
|
// If we've got any resource lists allocated still we should free them
|
|
// now.
|
|
//
|
|
|
|
if(Adapter->AllocatedResources != NULL) {
|
|
ExFreePool(Adapter->AllocatedResources);
|
|
Adapter->AllocatedResources = NULL;
|
|
}
|
|
|
|
if(Adapter->TranslatedResources != NULL) {
|
|
ExFreePool(Adapter->TranslatedResources);
|
|
Adapter->TranslatedResources = NULL;
|
|
}
|
|
|
|
//
|
|
// Cleanup verifier resources.
|
|
//
|
|
|
|
if (SpVerifierActive(Adapter)) {
|
|
SpDoVerifierCleanup(Adapter);
|
|
}
|
|
|
|
#if defined(FORWARD_PROGRESS)
|
|
//
|
|
// Cleanup the adapter's reserved pages.
|
|
//
|
|
|
|
if (Adapter->ReservedPages != NULL) {
|
|
MmFreeMappingAddress(Adapter->ReservedPages,
|
|
SCSIPORT_TAG_MAPPING_LIST);
|
|
Adapter->ReservedPages = NULL;
|
|
}
|
|
|
|
if (Adapter->ReservedMdl != NULL) {
|
|
IoFreeMdl(Adapter->ReservedMdl);
|
|
Adapter->ReservedMdl = NULL;
|
|
}
|
|
#endif
|
|
|
|
Adapter->CommonExtension.IsInitialized = FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpReapChildren(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
{
|
|
ULONG j;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Run through the logical unit bins and remove any child devices which
|
|
// remain.
|
|
//
|
|
|
|
for(j = 0; j < NUMBER_LOGICAL_UNIT_BINS; j++) {
|
|
|
|
while(Adapter->LogicalUnitList[j].List != NULL) {
|
|
|
|
PLOGICAL_UNIT_EXTENSION lun =
|
|
Adapter->LogicalUnitList[j].List;
|
|
|
|
lun->IsMissing = TRUE;
|
|
lun->IsEnumerated = FALSE;
|
|
|
|
SpRemoveLogicalUnit(lun, IRP_MN_REMOVE_DEVICE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the initiator LUs.
|
|
//
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
PLOGICAL_UNIT_EXTENSION lu = Adapter->InitiatorLU[j];
|
|
if (lu != NULL) {
|
|
Adapter->InitiatorLU[j] = NULL;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpTerminateAdapter(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will terminate the miniport's control of the adapter. It
|
|
does not cleanly shutdown the miniport and should only be called when
|
|
scsiport is notified that the adapter has been surprise removed.
|
|
|
|
This works by synchronizing with the miniport and setting flags to
|
|
disable any new calls into the miniport. Once this has been done it can
|
|
run through and complete any i/o requests which may still be inside
|
|
the miniport.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter to terminate.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&(Adapter->SpinLock));
|
|
|
|
if (Adapter->CommonExtension.CurrentPnpState == IRP_MN_START_DEVICE) {
|
|
|
|
//
|
|
// TA synchronized will stop all calls into the miniport and complete
|
|
// all active requests.
|
|
//
|
|
|
|
Adapter->SynchronizeExecution(Adapter->InterruptObject,
|
|
SpTerminateAdapterSynchronized,
|
|
Adapter);
|
|
|
|
Adapter->CommonExtension.PreviousPnpState = 0xff;
|
|
|
|
|
|
SpFlushAllRequests(Adapter->HwDeviceExtension,
|
|
0xff,
|
|
0xff,
|
|
0xff,
|
|
SRB_STATUS_NO_HBA);
|
|
|
|
|
|
//
|
|
// Stop the miniport timer
|
|
//
|
|
|
|
KeCancelTimer(&(Adapter->MiniPortTimer));
|
|
|
|
//
|
|
// We keep the device object timer running so that any held, busy or
|
|
// otherwise deferred requests will have a chance to get flushed out.
|
|
// We can give the whole process a boost by setting the adapter timeout
|
|
// counter to 1 (it will go to zero in the tick handler) and running
|
|
// the tick handler by hand here.
|
|
//
|
|
|
|
Adapter->PortTimeoutCounter = 1;
|
|
ScsiPortTickHandler(Adapter->DeviceObject, NULL);
|
|
|
|
} else {
|
|
KeReleaseSpinLockFromDpcLevel(&(Adapter->SpinLock));
|
|
}
|
|
|
|
KeLowerIrql(oldIrql);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SpTerminateAdapterSynchronized(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
{
|
|
//
|
|
// Disable the interrupt from coming in.
|
|
//
|
|
|
|
SET_FLAG(Adapter->InterruptData.InterruptFlags, PD_ADAPTER_REMOVED);
|
|
|
|
|
|
ScsiPortCompleteRequest(Adapter->HwDeviceExtension,
|
|
0xff,
|
|
0xff,
|
|
0xff,
|
|
SRB_STATUS_NO_HBA);
|
|
|
|
//
|
|
//This needs to be done to start the next request sitting in the adapter
|
|
//queue, otherwise surprise remove will wait forever for the remove
|
|
//lockcount to be zero.
|
|
//
|
|
|
|
ScsiPortNotification(NextRequest,
|
|
Adapter->HwDeviceExtension);
|
|
|
|
//
|
|
// Run the completion DPC.
|
|
//
|
|
|
|
if(TEST_FLAG(Adapter->InterruptData.InterruptFlags,
|
|
PD_NOTIFICATION_REQUIRED)) {
|
|
SpRequestCompletionDpc(Adapter->DeviceObject);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpRemoveAdapterSynchronized(
|
|
PADAPTER_EXTENSION Adapter
|
|
)
|
|
{
|
|
//
|
|
// Disable the interrupt from coming in.
|
|
//
|
|
|
|
SET_FLAG(Adapter->InterruptData.InterruptFlags, PD_ADAPTER_REMOVED);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
SpFlushAllRequests(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN UCHAR SrbStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flushes all requests in all the Lun device queues, busy and pending
|
|
requests on all Luns. The request stuck in the adapter's CurrentIrp
|
|
field of the adapter due to PD_RESET_HELD will be taken care of by
|
|
the tick handler. In short all the requests stuck anywhere in ScsiPort
|
|
will be flushed. Unfortunately this is not enough to handle Surprise
|
|
Remove. We also need a small check in Startio so that we flush all
|
|
requests on the lun for which we got a request.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtenson - Supplies the HBA miniport driver's adapter data storage.
|
|
|
|
TargetId, Lun and PathId - specify device address on a SCSI bus.
|
|
|
|
SrbStatus - Status to be returned in each completed SRB.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Note: This routine must be called with the Adapter SpinLock held, and
|
|
the lock would be released by this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
ULONG binNumber;
|
|
PIRP listIrp = NULL;
|
|
PIRP nextIrp;
|
|
PKDEVICE_QUEUE_ENTRY packet;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
|
|
for (binNumber = 0; binNumber < NUMBER_LOGICAL_UNIT_BINS; binNumber++) {
|
|
|
|
PLOGICAL_UNIT_BIN bin = &deviceExtension->LogicalUnitList[binNumber];
|
|
PLOGICAL_UNIT_EXTENSION LogicalUnit;
|
|
ULONG limit = 0;
|
|
|
|
LogicalUnit = bin->List;
|
|
|
|
DebugPrint((2, "ScsiPortCompleteRequest: Completing requests in "
|
|
"bin %d [%#p]\n",
|
|
binNumber, bin));
|
|
|
|
for(LogicalUnit = bin->List;
|
|
LogicalUnit != NULL;
|
|
LogicalUnit = LogicalUnit->NextLogicalUnit) {
|
|
|
|
PLIST_ENTRY entry;
|
|
|
|
ASSERT(limit++ < 1000);
|
|
|
|
//
|
|
// See if this logical unit matches the pattern. Check for -1
|
|
// first since this seems to be the most popular way to complete
|
|
// requests.
|
|
//
|
|
|
|
if (((PathId == SP_UNTAGGED) || (PathId == LogicalUnit->PathId)) &&
|
|
((TargetId == SP_UNTAGGED) ||
|
|
(TargetId == LogicalUnit->TargetId)) &&
|
|
((Lun == SP_UNTAGGED) || (Lun == LogicalUnit->Lun))) {
|
|
|
|
//
|
|
// The queue may not be busy so we have to use the IfBusy variant.
|
|
// Use a zero key to pull items from the head of it (if any are there)
|
|
//
|
|
|
|
while ((packet =
|
|
KeRemoveByKeyDeviceQueueIfBusy(
|
|
&(LogicalUnit->DeviceObject->DeviceQueue),
|
|
0))
|
|
!= NULL) {
|
|
|
|
nextIrp = CONTAINING_RECORD(packet,
|
|
IRP,
|
|
Tail.Overlay.DeviceQueueEntry);
|
|
|
|
//
|
|
// Get the srb.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(nextIrp);
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
//
|
|
// Set the status code.
|
|
//
|
|
|
|
srb->SrbStatus = SrbStatus;
|
|
nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// Link the requests. They will be completed after the
|
|
// spinlock is released.
|
|
//
|
|
|
|
nextIrp->Tail.Overlay.ListEntry.Flink =
|
|
(PLIST_ENTRY)listIrp;
|
|
|
|
listIrp = nextIrp;
|
|
}
|
|
|
|
//
|
|
// If there is a pending request on the LU, add it to the list so it
|
|
// gets flushed along with the queued requests.
|
|
//
|
|
|
|
if (LogicalUnit->PendingRequest != NULL) {
|
|
|
|
PIRP irp = LogicalUnit->PendingRequest->CurrentIrp;
|
|
srb = LogicalUnit->PendingRequest->CurrentSrb;
|
|
|
|
DebugPrint((1, "SpFlushReleaseQueue: flushing pending request irp:%p srb:%p\n", irp, srb));
|
|
|
|
srb->SrbStatus = SrbStatus;
|
|
irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
irp->Tail.Overlay.ListEntry.Flink = (PLIST_ENTRY) listIrp;
|
|
listIrp = irp;
|
|
|
|
LogicalUnit->PendingRequest = NULL;
|
|
ASSERT(LogicalUnit->LuFlags | LU_PENDING_LU_REQUEST);
|
|
CLEAR_FLAG(LogicalUnit->LuFlags, LU_PENDING_LU_REQUEST);
|
|
|
|
}
|
|
|
|
//
|
|
// If there is a busy request on the LU, add it to the list so it
|
|
// gets flushed along with the queued requests.
|
|
//
|
|
|
|
if ( LogicalUnit->BusyRequest ) {
|
|
|
|
PIRP irp = LogicalUnit->BusyRequest->CurrentIrp;
|
|
srb = LogicalUnit->BusyRequest->CurrentSrb;
|
|
|
|
DebugPrint((1, "SpFlushReleaseQueue: flushing busy request irp:%\
|
|
p srb:%p\n", irp, srb));
|
|
|
|
srb->SrbStatus = SrbStatus;
|
|
irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
irp->Tail.Overlay.ListEntry.Flink = (PLIST_ENTRY) listIrp;
|
|
listIrp = irp;
|
|
|
|
LogicalUnit->BusyRequest = NULL;
|
|
ASSERT((LU_LOGICAL_UNIT_IS_BUSY | LU_QUEUE_IS_FULL));
|
|
|
|
CLEAR_FLAG(LogicalUnit->LuFlags,
|
|
(LU_LOGICAL_UNIT_IS_BUSY | LU_QUEUE_IS_FULL));
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&(deviceExtension->SpinLock));
|
|
|
|
//
|
|
// Complete the flushed requests.
|
|
//
|
|
|
|
while (listIrp != NULL) {
|
|
|
|
PSRB_DATA srbData;
|
|
|
|
nextIrp = listIrp;
|
|
listIrp = (PIRP) nextIrp->Tail.Overlay.ListEntry.Flink;
|
|
|
|
//
|
|
// Get the srb.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(nextIrp);
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
srbData = srb->OriginalRequest;
|
|
|
|
srb->OriginalRequest = nextIrp;
|
|
|
|
SpReleaseRemoveLock(deviceExtension->DeviceObject, nextIrp);
|
|
SpCompleteRequest(deviceExtension->DeviceObject,
|
|
nextIrp,
|
|
srbData,
|
|
IO_NO_INCREMENT);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|