|
|
/****************************************************************************/ // buffer.c
//
// TermDD default OutBuf management.
//
// Copyright (C) 1998-2000 Microsoft Corporation
/****************************************************************************/
#include <precomp.h>
#pragma hdrstop
/*
* Define default stack size for IRP allocation. * (This size will be checked by the TdRawWrite routine.) */ #define OUTBUF_STACK_SIZE 4
#define OUTBUF_TIMEOUT 60000 // 1 minute
#if DBG
extern PICA_DISPATCH IcaStackDispatchTable[]; #endif
/****************************************************************************/ // IcaBufferGetUsableSpace
//
// Used by the protocol stack drivers to determine the number of usable bytes
// in a TermDD-created OutBuf, given the total size of the OutBuf. This allows
// a stack driver to target a particular OutBuf size and pack data up to the
// edges of the internal OutBuf headers. The returned size can be used as an
// allocation size request that will return an OutBuf of the right size.
/****************************************************************************/ unsigned IcaBufferGetUsableSpace(unsigned OutBufTotalSize) { unsigned IrpSize; unsigned MdlSize; unsigned MaxOutBufOverhead;
// Use the same overhead calculations used in IcaBufferAllocInternal()
// below, plus a 4-byte offset to cover the extra 1-byte difference
// required in the requesting size to reach a target buffer size.
IrpSize = IoSizeOfIrp(OUTBUF_STACK_SIZE) + 8;
if (OutBufTotalSize <= MaxOutBufAlloc) MdlSize = MaxOutBufMdlOverhead; else MdlSize = (unsigned)MmSizeOfMdl((PVOID)(PAGE_SIZE - 1), OutBufTotalSize);
MaxOutBufOverhead = ((sizeof(OUTBUF) + 7) & ~7) + IrpSize + MdlSize; return OutBufTotalSize - MaxOutBufOverhead - 4; }
/*******************************************************************************
* IcaBufferAlloc * * pContext (input) * pointer to SDCONTEXT of caller * fWait (input) * wait for buffer * fControl (input) * control buffer flag * ByteCount (input) * size of buffer to allocate (zero - use default size) * pOutBufOrig (input) * pointer to original OUTBUF (or null) * pOutBuf (output) * address to return pointer to OUTBUF structure ******************************************************************************/ NTSTATUS IcaBufferAlloc( IN PSDCONTEXT pContext, IN BOOLEAN fWait, IN BOOLEAN fControl, IN ULONG ByteCount, IN POUTBUF pOutBufOrig, OUT POUTBUF *ppOutBuf) { PSDLINK pSdLink; PICA_STACK pStack; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK pointer. */ pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext); pStack = pSdLink->pStack; ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack); ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable); ASSERT(ExIsResourceAcquiredExclusiveLite( &pStack->Resource));
/*
* Walk up the SDLINK list looking for a driver which has specified * a BufferAlloc callup routine. If we find one, then call the * driver BufferAlloc routine to let it handle the call. */ while ((pSdLink = IcaGetPreviousSdLink(pSdLink)) != NULL) { ASSERT( pSdLink->pStack == pStack ); if (pSdLink->SdContext.pCallup->pSdBufferAlloc) { IcaReferenceSdLink(pSdLink); Status = (pSdLink->SdContext.pCallup->pSdBufferAlloc)( pSdLink->SdContext.pContext, fWait, fControl, ByteCount, pOutBufOrig, ppOutBuf); IcaDereferenceSdLink(pSdLink); return Status; } }
/*
* We didn't find a callup routine to handle the request, * so we'll process it here. */ Status = IcaBufferAllocInternal(pContext, fWait, fControl, ByteCount, pOutBufOrig, ppOutBuf);
TRACESTACK((pStack, TC_ICADD, TT_API3, "TermDD: IcaBufferAlloc: 0x%08x, Status=0x%x\n", *ppOutBuf, Status));
return Status; }
NTSTATUS IcaBufferAllocInternal( IN PSDCONTEXT pContext, IN BOOLEAN fWait, IN BOOLEAN fControl, IN ULONG ByteCount, IN POUTBUF pOutBufOrig, OUT POUTBUF *ppOutBuf) { PSDLINK pSdLink; PICA_STACK pStack; int PoolIndex; ULONG irpSize; ULONG mdlSize; ULONG AllocationSize; KIRQL oldIrql; PLIST_ENTRY Head; POUTBUF pOutBuf; NTSTATUS Status; unsigned MaxOutBufOverhead;
/*
* Use SD passed context to get the SDLINK pointer. */ pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext ); pStack = pSdLink->pStack; ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack ); ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
/*
* If original buffer is specified use it's flags */ if (pOutBufOrig) { fWait = (BOOLEAN) pOutBufOrig->fWait; fControl = (BOOLEAN) pOutBufOrig->fControl; }
/*
* Check if we already have our maximum number of buffers allocated */ while (!fControl && (pStack->OutBufAllocCount >= pStack->OutBufCount)) { /*
* increment performance counter */ pStack->ProtocolStatus.Output.WaitForOutBuf++;
/*
* Return if it's not ok to wait */ if (!fWait) return(STATUS_IO_TIMEOUT);
/*
* We hit the high watermark */ pStack->fWaitForOutBuf = TRUE;
/*
* Only wait for non-control requests */ KeClearEvent(&pStack->OutBufEvent); Status = IcaWaitForSingleObject(pContext, &pStack->OutBufEvent, OUTBUF_TIMEOUT); if (NT_SUCCESS(Status)) { if (Status != STATUS_WAIT_0) return STATUS_IO_TIMEOUT; } else { return Status; } }
/*
* If the caller did not specify a byte count * then use the standard outbuf size for this stack. */ if (ByteCount == 0) ByteCount = pStack->OutBufLength;
// Note MaxOutBufOverhead is the max for the default max allocation.
// It will be recalculated if the requested alloc size is greater
// than can be handled by the default.
irpSize = IoSizeOfIrp(OUTBUF_STACK_SIZE) + 8; mdlSize = MaxOutBufMdlOverhead; MaxOutBufOverhead = ((sizeof(OUTBUF) + 7) & ~7) + irpSize + mdlSize;
/*
* Determine which buffer pool to use, if any, * and the OutBuf length to allocate, if necessary. * Note that the max requested ByteCount that will hit the buffer pool * is (MaxOutBufAlloc - 1 - MaxOutBufOverhead). */ if ((ByteCount + MaxOutBufOverhead) < MaxOutBufAlloc) {
ASSERT(((ByteCount + MaxOutBufOverhead) / MinOutBufAlloc) < (1 << NumAllocSigBits)); PoolIndex = OutBufPoolMapping[(ByteCount + MaxOutBufOverhead) / MinOutBufAlloc];
IcaAcquireSpinLock(&IcaSpinLock, &oldIrql); if (!IsListEmpty(&IcaFreeOutBufHead[PoolIndex])) { Head = RemoveHeadList(&IcaFreeOutBufHead[PoolIndex]); IcaReleaseSpinLock(&IcaSpinLock, oldIrql); pOutBuf = CONTAINING_RECORD(Head, OUTBUF, Links); ASSERT(pOutBuf->PoolIndex == PoolIndex); } else { IcaReleaseSpinLock(&IcaSpinLock, oldIrql); AllocationSize = OutBufPoolAllocSizes[PoolIndex]; pOutBuf = ICA_ALLOCATE_POOL(NonPagedPool, AllocationSize); if (pOutBuf == NULL) return STATUS_NO_MEMORY;
// Prevent leaking control OutBufs on OutBuf free.
if (fControl) PoolIndex = FreeThisOutBuf; } } else { PoolIndex = FreeThisOutBuf;
/*
* Determine the sizes of the various components of an OUTBUF. * Note that these are all worst-case calculations -- * actual size of the MDL may be smaller. */ mdlSize = (ULONG)MmSizeOfMdl((PVOID)(PAGE_SIZE - 1), ByteCount);
/*
* Add up the component sizes of an OUTBUF to determine * the total size that is needed to allocate. */ AllocationSize = ((sizeof(OUTBUF) + 7) & ~7) + irpSize + mdlSize + ((ByteCount + 3) & ~3);
pOutBuf = ICA_ALLOCATE_POOL(NonPagedPool, AllocationSize); if (pOutBuf == NULL) return STATUS_NO_MEMORY; }
/*
* Initialize the IRP pointer and the IRP itself. */ pOutBuf->pIrp = (PIRP)((BYTE *)pOutBuf + ((sizeof(OUTBUF) + 7) & ~7)); IoInitializeIrp(pOutBuf->pIrp, (USHORT)irpSize, OUTBUF_STACK_SIZE);
/*
* Set up the MDL pointer but don't build it yet. * It will be built by the TD write code if needed. */ pOutBuf->pMdl = (PMDL)((PCHAR)pOutBuf->pIrp + irpSize);
/*
* Set up the address buffer pointer. */ pOutBuf->pBuffer = (PUCHAR)pOutBuf->pMdl + mdlSize + pStack->SdOutBufHeader;
/*
* Initialize the rest of output buffer */ InitializeListHead(&pOutBuf->Links); pOutBuf->OutBufLength = ByteCount; pOutBuf->PoolIndex = PoolIndex; pOutBuf->MaxByteCount = ByteCount - (pStack->SdOutBufHeader + pStack->SdOutBufTrailer); pOutBuf->ByteCount = 0; pOutBuf->fIrpCompleted = FALSE;
/*
* Copy inherited fields */ if (pOutBufOrig == NULL) { pOutBuf->fWait = fWait; // wait for outbuf allocation
pOutBuf->fControl = fControl; // control buffer (ack/nak)
pOutBuf->fRetransmit = FALSE; // not a retransmit
pOutBuf->fCompress = TRUE; // compress data
pOutBuf->StartTime = 0; // time stamp
pOutBuf->Sequence = 0; // zero sequence number
pOutBuf->Fragment = 0; // zero fragment number
} else { pOutBuf->fWait = pOutBufOrig->fWait; pOutBuf->fControl = pOutBufOrig->fControl; pOutBuf->fRetransmit = pOutBufOrig->fRetransmit; pOutBuf->fCompress = pOutBufOrig->fCompress; pOutBuf->StartTime = pOutBufOrig->StartTime; pOutBuf->Sequence = pOutBufOrig->Sequence; pOutBuf->Fragment = pOutBufOrig->Fragment++; }
/*
* Increment allocated buffer count */ pStack->OutBufAllocCount++;
/*
* Return buffer to caller */ *ppOutBuf = pOutBuf;
/*
* Return buffer to caller */ return STATUS_SUCCESS; }
/*******************************************************************************
* IcaBufferFree * * pContext (input) * pointer to SDCONTEXT of caller * pOutBuf (input) * pointer to OUTBUF structure ******************************************************************************/ void IcaBufferFree(IN PSDCONTEXT pContext, IN POUTBUF pOutBuf) { PSDLINK pSdLink; PICA_STACK pStack; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK pointer. */ pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext); pStack = pSdLink->pStack; ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack); ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable); ASSERT(ExIsResourceAcquiredExclusiveLite( &pStack->Resource));
/*
* Walk up the SDLINK list looking for a driver which has specified * a BufferFree callup routine. If we find one, then call the * driver BufferFree routine to let it handle the call. */ while ((pSdLink = IcaGetPreviousSdLink(pSdLink)) != NULL) { ASSERT(pSdLink->pStack == pStack); if (pSdLink->SdContext.pCallup->pSdBufferFree) { IcaReferenceSdLink(pSdLink); (pSdLink->SdContext.pCallup->pSdBufferFree)( pSdLink->SdContext.pContext, pOutBuf); IcaDereferenceSdLink(pSdLink); return; } }
IcaBufferFreeInternal(pContext, pOutBuf);
TRACESTACK((pStack, TC_ICADD, TT_API3, "TermDD: IcaBufferFree: 0x%08x\n", pOutBuf)); }
/*******************************************************************************
* IcaBufferError * * pContext (input) * pointer to SDCONTEXT of caller * pOutBuf (input) * pointer to OUTBUF structure ******************************************************************************/ void IcaBufferError(IN PSDCONTEXT pContext, IN POUTBUF pOutBuf) { PSDLINK pSdLink; PICA_STACK pStack; NTSTATUS Status;
/*
* Use SD passed context to get the SDLINK pointer. */ pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext); pStack = pSdLink->pStack; ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack); ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable); ASSERT(ExIsResourceAcquiredExclusiveLite( &pStack->Resource));
/*
* Walk up the SDLINK list looking for a driver which has specified * a BufferError callup routine. If we find one, then call the * driver BufferError routine to let it handle the call. */ while ((pSdLink = IcaGetPreviousSdLink(pSdLink)) != NULL) { ASSERT(pSdLink->pStack == pStack); if (pSdLink->SdContext.pCallup->pSdBufferError) { IcaReferenceSdLink(pSdLink); (pSdLink->SdContext.pCallup->pSdBufferError)( pSdLink->SdContext.pContext, pOutBuf); IcaDereferenceSdLink(pSdLink); return; } }
IcaBufferFreeInternal(pContext, pOutBuf);
TRACESTACK((pStack, TC_ICADD, TT_API3, "TermDD: IcaBufferError: 0x%08x\n", pOutBuf)); }
void IcaBufferFreeInternal(IN PSDCONTEXT pContext, IN POUTBUF pOutBuf) { PSDLINK pSdLink; PICA_STACK pStack; KIRQL oldIrql;
/*
* Use SD passed context to get the SDLINK pointer. */ pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext); pStack = pSdLink->pStack; ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack); ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable);
/*
* If the buffer came from a free pool, return it to the pool, * otherwise free it. Note that pOutBuf->OutBufLength is actually the * pool index to use. */ if (pOutBuf->PoolIndex != FreeThisOutBuf) { ASSERT(pOutBuf->PoolIndex >= 0 && pOutBuf->PoolIndex < NumOutBufPools); IcaAcquireSpinLock(&IcaSpinLock, &oldIrql); InsertHeadList(&IcaFreeOutBufHead[pOutBuf->PoolIndex], &pOutBuf->Links); IcaReleaseSpinLock(&IcaSpinLock, oldIrql); } else { ICA_FREE_POOL(pOutBuf); }
/*
* Decrement allocated buffer count */ pStack->OutBufAllocCount--; ASSERT((LONG)pStack->OutBufAllocCount >= 0);
/*
* If we hit the high watermark then we should wait until the low * watermark is hit before signaling the allocator to continue. * This should prevent excessive task switching. */ if (pStack->fWaitForOutBuf) { if (pStack->OutBufAllocCount <= pStack->OutBufLowWaterMark) { pStack->fWaitForOutBuf = FALSE;
/*
* Signal outbuf event (buffer is now available) */ (void) KeSetEvent(&pStack->OutBufEvent, EVENT_INCREMENT, FALSE); } } }
/*******************************************************************************
* IcaGetLowWaterMark * * Description : Gets the low water mark that the stack specified * * pContext (input) * pointer to SDCONTEXT of caller ******************************************************************************/ ULONG IcaGetLowWaterMark(IN PSDCONTEXT pContext) { ULONG ulRet = 0; PICA_STACK pStack; PSDLINK pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext);
ASSERT(pSdLink); ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack); ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable);
ASSERT(ExIsResourceAcquiredExclusive( &pSdLink->pStack->Resource)); if (NULL != pSdLink) { pStack = pSdLink->pStack; ulRet = pStack->OutBufLowWaterMark; } return ulRet; }
/*******************************************************************************
* IcaGetSizeForNoLowWaterMark * * Description : Finds if the stack specified a no low water mark * If so, returns the size needed to bypass ring * returns zero if the stack does not specify a PD_NO_LOWWATERMARK * pContext (input) * pointer to SDCONTEXT of caller ******************************************************************************/ ULONG IcaGetSizeForNoLowWaterMark(IN PSDCONTEXT pContext) { ULONG retVal = 0; ULONG ulLowWm = IcaGetLowWaterMark(pContext);
if ( MAX_LOW_WATERMARK == ulLowWm ) { retVal = MaxOutBufAlloc; } return retVal; }
|