534 lines
17 KiB
C
534 lines
17 KiB
C
/****************************************************************************/
|
|
// 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;
|
|
}
|