385 lines
9.2 KiB
C
385 lines
9.2 KiB
C
/*++
|
|
|
|
Copyright (c) 1998-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sendbuffer.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the buffered send package.
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 14-Aug-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
|
|
//
|
|
// Private types.
|
|
//
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, UlBufferedSendFile )
|
|
#endif // ALLOC_PRAGMA
|
|
#if 0
|
|
NOT PAGEABLE -- UlInitializeSendBuffer
|
|
NOT PAGEABLE -- UlBufferedSendData
|
|
NOT PAGEABLE -- UlFlushSendBuffer
|
|
NOT PAGEABLE -- UlCleanupSendBuffer
|
|
#endif
|
|
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a send buffer.
|
|
|
|
Arguments:
|
|
|
|
pSendBuffer - Supplies the MDL_SEND_BUFFER to initialize.
|
|
|
|
pConnection - Supplies the UL_CONNECTION to associate with the
|
|
send buffer.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlInitializeSendBuffer(
|
|
OUT PMDL_SEND_BUFFER pSendBuffer,
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
pSendBuffer->pConnection = pConnection;
|
|
pSendBuffer->pMdlChain = NULL;
|
|
pSendBuffer->pMdlLink = &pSendBuffer->pMdlChain;
|
|
pSendBuffer->BytesBuffered = 0;
|
|
|
|
} // UlInitializeSendBuffer
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initiates a send on the specified send buffer. If possible, new MDLs
|
|
are created and linked onto the buffer's MDL chain and the send is
|
|
deferred.
|
|
|
|
Arguments:
|
|
|
|
pSendBuffer - Supplies the MDL_SEND_BUFFER to send.
|
|
|
|
pMdlChain - Supplies a pointer to a MDL chain describing the
|
|
data buffers to send.
|
|
|
|
ForceFlush - Supplies TRUE if the buffer should be always be
|
|
flushed. In other words, a send will always be initiated
|
|
regardless of the amount of data buffered. Supplies FALSE
|
|
if the incoming MDL should be linked onto the MDL chain
|
|
for a deferred send, if possible.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the send is complete.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status. Will be STATUS_SUCCESS if the buffer
|
|
was simply linked onto the MDL chain for a deferred send. In this
|
|
case, the completion routine will NOT be called.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlBufferedSendData(
|
|
IN PMDL_SEND_BUFFER pSendBuffer,
|
|
IN PMDL pMdl,
|
|
IN BOOLEAN ForceFlush,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG mdlLength;
|
|
PMDL pMdlClone;
|
|
PVOID pMdlAddress;
|
|
|
|
//
|
|
// Determine the size of the incoming MDL and update the number
|
|
// of bytes we've buffered so far.
|
|
//
|
|
|
|
mdlLength = UlGetMdlChainByteCount( pMdl );
|
|
pSendBuffer->BytesBuffered += mdlLength;
|
|
|
|
//
|
|
// If we've exceeded the max buffer threshold, or if the caller
|
|
// is forcing a flush, then chain the incoming MDL onto the MDL
|
|
// chain and initiate a send.
|
|
//
|
|
// CODEWORK: To make life easier, I'll also flush if the incoming
|
|
// MDL is actually the head of a MDL chain. As an optimization,
|
|
// it may be worth the effort to clone & attach the chain.
|
|
//
|
|
|
|
if (pSendBuffer->BytesBuffered >= MAX_SEND_BUFFER ||
|
|
ForceFlush ||
|
|
pMdl->Next != NULL)
|
|
{
|
|
(*pSendBuffer->pMdlLink) = pMdl;
|
|
|
|
status = UlFlushSendBuffer(
|
|
pSendBuffer,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clone the incoming MDL, then link it onto the chain.
|
|
//
|
|
|
|
ASSERT( pMdl->Next == NULL );
|
|
pMdlClone = UlCloneMdl( pMdl );
|
|
|
|
if (pMdlClone != NULL)
|
|
{
|
|
IF_DEBUG( SEND_BUFFER )
|
|
{
|
|
KdPrint((
|
|
"UlBufferedSendData: buffering MDL %p [%lu] on %p\n",
|
|
pMdlClone,
|
|
mdlLength,
|
|
pSendBuffer
|
|
));
|
|
}
|
|
|
|
(*pSendBuffer->pMdlLink) = pMdlClone;
|
|
pSendBuffer->pMdlLink = &pMdlClone->Next;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Couldn't allocate a new MDL.
|
|
//
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlBufferedSendData
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initiates a send on the specified file cache entry. If possible, new
|
|
MDLs are created and linked onto the buffer's MDL chain and the send is
|
|
deferred.
|
|
|
|
Arguments:
|
|
|
|
pSendBuffer - Supplies the MDL_SEND_BUFFER to send.
|
|
|
|
pFileCacheEntry - Supplies the UL_FILE_CACHE_ENTRY describing the
|
|
file to send.
|
|
|
|
pByteRange - Supplies the byte range within the file to send.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the send is complete.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status. Will be STATUS_SUCCESS if the buffer
|
|
was simply linked onto the MDL chain for a deferred send. In this
|
|
case, the completion routine will NOT be called.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlBufferedSendFile(
|
|
IN PMDL_SEND_BUFFER pSendBuffer,
|
|
IN PUL_FILE_CACHE_ENTRY pFileCacheEntry,
|
|
IN PUL_BYTE_RANGE pByteRange,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_FILE_CACHE_ENTRY( pFileCacheEntry ) );
|
|
|
|
return STATUS_NOT_SUPPORTED; // NYI
|
|
|
|
} // UlBufferedSendFile
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Flushes any pending MDLs attached to the send buffer.
|
|
|
|
Arguments:
|
|
|
|
pSendBuffer - Supplies the MDL_SEND_BUFFER to flush.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the buffer is flushed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlFlushSendBuffer(
|
|
IN PMDL_SEND_BUFFER pSendBuffer,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG bytesBuffered;
|
|
|
|
bytesBuffered = pSendBuffer->BytesBuffered;
|
|
pSendBuffer->BytesBuffered = 0;
|
|
|
|
IF_DEBUG( SEND_BUFFER )
|
|
{
|
|
KdPrint((
|
|
"UlFlushSendBuffer: flushing %p [%lu]\n",
|
|
pSendBuffer,
|
|
bytesBuffered
|
|
));
|
|
}
|
|
|
|
if (bytesBuffered > 0)
|
|
{
|
|
status = UlSendData(
|
|
pSendBuffer->pConnection,
|
|
pSendBuffer->pMdlChain,
|
|
bytesBuffered,
|
|
pCompletionRoutine,
|
|
pCompletionContext,
|
|
FALSE
|
|
);
|
|
}
|
|
else
|
|
{
|
|
status = UlInvokeCompletionRoutine(
|
|
STATUS_SUCCESS,
|
|
0,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlFlushSendBuffer
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up any MDLs allocated & attached to the send buffer.
|
|
|
|
Arguments:
|
|
|
|
pSendBuffer - Supplies the MDL_SEND_BUFFER to cleanup.
|
|
|
|
Return Value:
|
|
|
|
PMDL - Returns a pointer to the last MDL on the chain, NULL
|
|
if the chain is empty.
|
|
|
|
--***************************************************************************/
|
|
PMDL
|
|
UlCleanupSendBuffer(
|
|
IN OUT PMDL_SEND_BUFFER pSendBuffer
|
|
)
|
|
{
|
|
PMDL pMdl;
|
|
PMDL pNextMdl;
|
|
|
|
IF_DEBUG( SEND_BUFFER )
|
|
{
|
|
KdPrint((
|
|
"UlCleanupSendBuffer: cleaning %p\n",
|
|
pSendBuffer
|
|
));
|
|
}
|
|
|
|
//
|
|
// Walk the MDL chain and free all *except* the last one.
|
|
// (The last MDL attached to the chain was not one of the clones
|
|
// we allocated, it's a 'client' MDL. It's the client code's
|
|
// responsibility to free it appropriately.)
|
|
//
|
|
|
|
pMdl = pSendBuffer->pMdlChain;
|
|
|
|
while (TRUE)
|
|
{
|
|
pNextMdl = pMdl->Next;
|
|
|
|
if (pNextMdl == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
IoFreeMdl( pMdl );
|
|
pMdl = pNextMdl;
|
|
}
|
|
|
|
return pMdl;
|
|
|
|
} // UlCleanupSendBuffer
|
|
|