windows-nt/Source/XPSP1/NT/termsrv/drivers/termdd/buffer.c
2020-09-26 16:20:57 +08:00

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;
}