|
|
/*
Copyright (c) 1998 Microsoft Corporation
Module Name:
tcputil.c
Abstract:
This module contains utility routines that used to implement the AFP/TCP interface
Author:
Shirish Koti
Revision History: 22 Jan 1998 Initial Version
--*/
#define FILENUM FILE_TCPUTIL
#include <afp.h>
#include <scavengr.h>
/*** DsiInit
* * This routine initialization of DSI related globals * * Returns: nothing * */ VOID DsiInit( IN VOID ) { DsiTcpAdapter = NULL;
INITIALIZE_SPIN_LOCK(&DsiAddressLock);
INITIALIZE_SPIN_LOCK(&DsiResourceLock);
InitializeListHead(&DsiFreeRequestList); InitializeListHead(&DsiIpAddrList);
KeInitializeEvent(&DsiShutdownEvent, NotificationEvent, False);
//
// initialize the function table of entry points into DSI
//
AfpDsiEntries.asp_AtalkAddr.Address = 0; AfpDsiEntries.asp_AspCtxt = NULL; AfpDsiEntries.asp_SetStatus = DsiAfpSetStatus; AfpDsiEntries.asp_CloseConn = DsiAfpCloseConn; AfpDsiEntries.asp_FreeConn = DsiAfpFreeConn; AfpDsiEntries.asp_ListenControl = DsiAfpListenControl; AfpDsiEntries.asp_WriteContinue = DsiAfpWriteContinue; AfpDsiEntries.asp_Reply = DsiAfpReply; AfpDsiEntries.asp_SendAttention = DsiAfpSendAttention;
}
/*** DsiCreateAdapter
* * This routine creates an adapter object. It's called whenever TDI tells us that * a tcpip interface became available * * Returns: status of operation * */ NTSTATUS FASTCALL DsiCreateAdapter( IN VOID ) { NTSTATUS status; PTCPADPTR pTcpAdptr; PTCPADPTR pCurrTcpAdptr; KIRQL OldIrql; HANDLE FileHandle; PFILE_OBJECT pFileObject; DWORD i;
KeClearEvent(&DsiShutdownEvent);
pTcpAdptr = (PTCPADPTR)AfpAllocZeroedNonPagedMemory(sizeof(TCPADPTR)); if (pTcpAdptr == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateInterface: alloc for PTCPADPTR failed\n"));
status = STATUS_INSUFFICIENT_RESOURCES; goto DsiCreateAdapter_ErrExit; }
pTcpAdptr->adp_Signature = DSI_ADAPTER_SIGNATURE;
pTcpAdptr->adp_RefCount = 1; // creation refcount
pTcpAdptr->adp_State = TCPADPTR_STATE_INIT;
InitializeListHead(&pTcpAdptr->adp_ActiveConnHead); InitializeListHead(&pTcpAdptr->adp_FreeConnHead);
INITIALIZE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock);
pTcpAdptr->adp_FileHandle = INVALID_HANDLE_VALUE; pTcpAdptr->adp_pFileObject = NULL;
//
// ok, save this adapter as our global adapter (there can only be one
// "adapter" active at any time.
//
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql);
ASSERT(DsiTcpAdapter == NULL);
if (DsiTcpAdapter == NULL) { DsiTcpAdapter = pTcpAdptr; status = STATUS_SUCCESS; RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
} else { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateAdapter: DsiTcpAdapter is not NULL!\n")); ASSERT(0);
status = STATUS_ADDRESS_ALREADY_EXISTS; RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); goto DsiCreateAdapter_ErrExit; }
//
// create TDI address for the AFP port
//
status = DsiOpenTdiAddress(pTcpAdptr, &FileHandle, &pFileObject); if (!NT_SUCCESS(status)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateAdapter: ...TdiAddr.. failed %lx on %lx!\n", status,pTcpAdptr));
goto DsiCreateAdapter_ErrExit; }
ACQUIRE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, &OldIrql);
pTcpAdptr->adp_FileHandle = FileHandle; pTcpAdptr->adp_pFileObject = pFileObject;
// mark that we now have opened the tdi address object
pTcpAdptr->adp_State |= TCPADPTR_STATE_BOUND;
// we are going to create DSI_INIT_FREECONNLIST_SIZE connections to put
// on the free list. Idea is at any time, so many (currently 2) connections
// should be on the free list.
pTcpAdptr->adp_RefCount += DSI_INIT_FREECONNLIST_SIZE;
RELEASE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, OldIrql);
//
// now, schedule an event to create those connections for the free list
//
for (i=0; i<DSI_INIT_FREECONNLIST_SIZE; i++) { DsiScheduleWorkerEvent(DsiCreateTcpConn, pTcpAdptr); }
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql); AfpServerBoundToTcp = TRUE; RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
// start off tickle timer (monitor all connections to see who needs a tickle)
AfpScavengerScheduleEvent(DsiSendTickles, NULL, DSI_TICKLE_TIMER, False);
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR,("AFP/TCP bound and ready\n"));
//
// if we came this far, all went well: return success
//
return(STATUS_SUCCESS);
//
// Error path
//
DsiCreateAdapter_ErrExit:
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateAdapter: couldn't create global adapter (%lx)\n",status));
ASSERT(0);
if (status != STATUS_ADDRESS_ALREADY_EXISTS) { ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql); DsiTcpAdapter = NULL; RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); }
if (pTcpAdptr) { AfpFreeMemory(pTcpAdptr); }
return(status); }
/*** DsiCreateTcpConn
* * This routine creates a connection object, creates a tdi connection for it * and associates it with the tdi address object for the AFP port, and finally * puts it on the free connections list of the adapter in question. * * Parm IN: pTcpAdptr - adapter context * * Returns: status of operation * */ NTSTATUS FASTCALL DsiCreateTcpConn( IN PTCPADPTR pTcpAdptr ) { PTCPCONN pTcpConn; NTSTATUS status; KIRQL OldIrql;
ASSERT(pTcpAdptr->adp_Signature == DSI_ADAPTER_SIGNATURE);
pTcpConn = (PTCPCONN)AfpAllocZeroedNonPagedMemory(sizeof(TCPCONN)); if (pTcpConn == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: alloc for TCPCONN failed\n"));
// remove the CONN refcount (we put before calling this routine)
DsiDereferenceAdapter(pTcpAdptr); return(STATUS_INSUFFICIENT_RESOURCES); }
pTcpConn->con_pTcpAdptr = pTcpAdptr; pTcpConn->con_Signature = DSI_CONN_SIGNATURE; pTcpConn->con_State = TCPCONN_STATE_INIT; pTcpConn->con_RcvState = DSI_NEW_REQUEST; pTcpConn->con_DestIpAddr = 0; pTcpConn->con_RefCount = 1; pTcpConn->con_pDsiReq = NULL; pTcpConn->con_FileHandle = INVALID_HANDLE_VALUE; pTcpConn->con_pFileObject = NULL;
DBGREFCOUNT(("DsiCreateTcpConn: CREATION inc %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState));
InitializeListHead(&pTcpConn->con_PendingReqs);
//
// initialize the TDI stuff for this connection and open handles
//
status = DsiOpenTdiConnection(pTcpConn); if (!NT_SUCCESS(status)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: ..TdiConn.. failed %lx\n",status));
// remove the CONN refcount (we put before calling this routine)
DsiDereferenceAdapter(pTcpAdptr); AfpFreeMemory(pTcpConn); return(status); }
//
// associate this connection with the addr object
//
status = DsiAssociateTdiConnection(pTcpConn); if (!NT_SUCCESS(status)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: ..AssociateTdiConn.. failed %lx\n",status));
DsiCloseTdiConnection(pTcpConn); AfpFreeMemory(pTcpConn); // remove the CONN refcount (we put before calling this routine)
DsiDereferenceAdapter(pTcpAdptr); return(status); }
//
// the connection is ready to be queued to the Free list. Make sure the
// addr object isn't closing before putting this puppy on the list
//
ACQUIRE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, &OldIrql);
if (!(pTcpAdptr->adp_State & TCPADPTR_STATE_CLOSING)) { InsertTailList(&pTcpAdptr->adp_FreeConnHead,&pTcpConn->con_Linkage); pTcpAdptr->adp_NumFreeConnections++; status = STATUS_SUCCESS; } else { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: Failed #2, pTcpAdptr %lx is closing\n",pTcpAdptr));
status = STATUS_INVALID_ADDRESS; }
RELEASE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, OldIrql);
//
// if something went wrong, undo everything
//
if (!NT_SUCCESS(status)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: something went wrong status=%lx, conn not created\n",status));
// close the TDI handles
DsiCloseTdiConnection(pTcpConn);
AfpFreeMemory(pTcpConn);
// remove the CONN refcount (we put before calling this routine)
DsiDereferenceAdapter(pTcpAdptr); } else { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_INFO, ("DsiCreateTcpConn: put new connection %lx on free list\n",pTcpConn)); }
return(status); }
/*** DsiAddIpaddressToList
* * This routine saves an "active" ipaddress in our list of ipaddresses * * Parm IN: IpAddress - the ipaddress to save * * Returns: result of the operation * */ NTSTATUS DsiAddIpaddressToList( IN IPADDRESS IpAddress ) { KIRQL OldIrql; PLIST_ENTRY pList; PIPADDRENTITY pIpaddrEntity; PIPADDRENTITY pTmpIpaddrEntity; BOOLEAN fAlreadyPresent=FALSE;
pIpaddrEntity = (PIPADDRENTITY)AfpAllocZeroedNonPagedMemory(sizeof(IPADDRENTITY)); if (pIpaddrEntity == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiAddIpaddressToList: malloc failed! (%lx)\n",IpAddress)); return(STATUS_INSUFFICIENT_RESOURCES); }
pIpaddrEntity->IpAddress = IpAddress;
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql);
pList = DsiIpAddrList.Flink;
while (pList != &DsiIpAddrList) { pTmpIpaddrEntity = CONTAINING_RECORD(pList, IPADDRENTITY, Linkage); if (pTmpIpaddrEntity->IpAddress == IpAddress) { fAlreadyPresent = TRUE; break; } pList = pList->Flink; }
if (fAlreadyPresent) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiAddIpaddressToList: %d.%d.%d.%d already present!\n", (IpAddress>>24)&0xFF,(IpAddress>>16)&0xFF,(IpAddress>>8)&0xFF,IpAddress&0xFF));
ASSERT(0);
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); return(STATUS_ADDRESS_ALREADY_EXISTS); }
InsertTailList(&DsiIpAddrList, &pIpaddrEntity->Linkage);
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
return(STATUS_SUCCESS);
}
/*** DsiRemoveIpaddressFromList
* * This routine remove ipaddress from our list of ipaddresses * * Parm IN: IpAddress - the ipaddress to remove * * Returns: TRUE if we removed the ipaddress, FALSE if we didn't * */ BOOLEAN DsiRemoveIpaddressFromList( IN IPADDRESS IpAddress ) { KIRQL OldIrql; PLIST_ENTRY pList; PIPADDRENTITY pIpaddrEntity; BOOLEAN fFoundInList=FALSE;
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql);
pList = DsiIpAddrList.Flink;
while (pList != &DsiIpAddrList) { pIpaddrEntity = CONTAINING_RECORD(pList, IPADDRENTITY, Linkage); if (pIpaddrEntity->IpAddress == IpAddress) { fFoundInList = TRUE; break; } pList = pList->Flink; }
if (!fFoundInList) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiRemoveIpaddressFromList: %d.%d.%d.%d not in the list!\n", (IpAddress>>24)&0xFF,(IpAddress>>16)&0xFF,(IpAddress>>8)&0xFF,IpAddress&0xFF));
ASSERT(0);
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); return(FALSE); }
RemoveEntryList(&pIpaddrEntity->Linkage);
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
AfpFreeMemory(pIpaddrEntity);
return(TRUE); }
/*** DsiGetRequest
* * This routine allocates a DSI Request structure and returns. For performance * reasons, we don't alloc new memory each time, but save a list of these * * Parm IN: nothin' * * Returns: pointer to a DSIREQ strucutre (NULL on failure) * */ PDSIREQ DsiGetRequest( IN VOID ) { PDSIREQ pDsiReq=NULL; PLIST_ENTRY pList; KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&DsiResourceLock, &OldIrql);
if (!IsListEmpty(&DsiFreeRequestList)) { pList = RemoveHeadList(&DsiFreeRequestList); pDsiReq = CONTAINING_RECORD(pList, DSIREQ, dsi_Linkage);
ASSERT(DsiFreeRequestListSize > 0); DsiFreeRequestListSize--;
RtlZeroMemory(pDsiReq, sizeof(DSIREQ)); }
RELEASE_SPIN_LOCK(&DsiResourceLock, OldIrql);
if (pDsiReq == NULL) { pDsiReq = (PDSIREQ)AfpAllocZeroedNonPagedMemory(sizeof(DSIREQ)); }
if (pDsiReq != NULL) { pDsiReq->dsi_Signature = DSI_REQUEST_SIGNATURE;
InitializeListHead(&pDsiReq->dsi_Linkage); } else { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetRequest: malloc failed!\n")); }
return(pDsiReq); }
/*** DsiGetReqBuffer
* * This routine allocates a buffer to hold either a header or a command * The likelihood of this function getting called is pretty slim (basically * if a packet is fragmented by TCP). So we simply make a call to alloc * * Parm IN: BufLen - length of the buffer requested * * Returns: pointer to a buffer (NULL on failure) * */ PBYTE DsiGetReqBuffer( IN DWORD BufLen ) { PBYTE pBuffer=NULL;
pBuffer = AfpAllocNonPagedMemory(BufLen);
#if DBG
if (pBuffer == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetReqBuffer: malloc failed!\n")); } #endif
return(pBuffer); }
/*** DsiFreeRequest
* * This routine frees up a previously allocated DSI Request structure. Again, * for performance reasons, we don't free up the memory but put this in a list * * Parm IN: pDsiReq - the request to be freed * * Returns: nothing * */ VOID DsiFreeRequest( PDSIREQ pDsiReq ) {
KIRQL OldIrql; BOOLEAN fQueueTooBig = TRUE;
if ((pDsiReq->dsi_PartialBuf != NULL) && (pDsiReq->dsi_PartialBuf != &pDsiReq->dsi_RespHeader[0])) { ASSERT(pDsiReq->dsi_PartialBufSize > 0); DsiFreeReqBuffer(pDsiReq->dsi_PartialBuf);
pDsiReq->dsi_PartialBuf = NULL; pDsiReq->dsi_PartialBufSize = 0; }
// if there was an Mdl we got via cache mgr, it had better be returned to system
ASSERT(pDsiReq->dsi_AfpRequest.rq_CacheMgrContext == NULL);
//
// if we came here via abnormal disconnect, this could be non-null
//
if (pDsiReq->dsi_pDsiAllocedMdl) { AfpFreeMdl(pDsiReq->dsi_pDsiAllocedMdl); }
#if DBG
RtlFillMemory(pDsiReq, sizeof(DSIREQ), 'f'); #endif
ACQUIRE_SPIN_LOCK(&DsiResourceLock, &OldIrql);
if (DsiFreeRequestListSize < DsiNumTcpConnections) { InsertTailList(&DsiFreeRequestList, &pDsiReq->dsi_Linkage); DsiFreeRequestListSize++; fQueueTooBig = FALSE; }
RELEASE_SPIN_LOCK(&DsiResourceLock, OldIrql);
if (fQueueTooBig) { AfpFreeMemory(pDsiReq); }
return; }
/*** DsiFreeReqBuffer
* * This routine allocates a buffer to hold either a header or a command * The likelihood of this function getting called is pretty slim (basically * if a packet is fragmented by TCP). So we simply make a call to alloc * * Parm IN: BufLen - length of the buffer requested * * Returns: pointer to a buffer (NULL on failure) * */ VOID DsiFreeReqBuffer( IN PBYTE pBuffer ) { ASSERT(pBuffer != NULL);
AfpFreeMemory(pBuffer);
return; }
/*** DsiDereferenceAdapter
* * This routine dereferences the adapter object. When refcount goes to 0, it * removes it from the global list of adapters. If at task time, it calls a * routine to close tcp handles and free the memory. If at dpc, it schedules * an event to do the same. * * Parm IN: pTcpAdptr - adapter context * * Returns: nothin' * */ VOID DsiDereferenceAdapter( IN PTCPADPTR pTcpAdptr ) { KIRQL OldIrql; BOOLEAN fDone=FALSE;
ASSERT(pTcpAdptr->adp_Signature == DSI_ADAPTER_SIGNATURE);
ACQUIRE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, &OldIrql);
pTcpAdptr->adp_RefCount--;
if (pTcpAdptr->adp_RefCount == 0) { fDone = TRUE; ASSERT(pTcpAdptr->adp_State & TCPADPTR_STATE_CLOSING); }
RELEASE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, OldIrql);
if (!fDone) { return; }
//
// this dude's life is over: do the needful to bid goodbye
//
// if we are at DPC, we need to do all the cleanup (file handle closing etc.)
// at task time: queue an event
if (KeGetCurrentIrql() == DISPATCH_LEVEL) { // queue an event, since we are at dpc
DsiScheduleWorkerEvent(DsiFreeAdapter, pTcpAdptr); } else { DsiFreeAdapter(pTcpAdptr); }
return; }
/*** DsiDereferenceConnection
* * This routine dereferences the connection object. When refcount goes to 0, it * removes it from the list of connections. If at task time, it calls a * routine to close tcp handles and free the memory. If at dpc, it schedules * an event to do the same. * * Parm IN: pTcpConn - connection context * * Returns: nothin' * */ VOID DsiDereferenceConnection( IN PTCPCONN pTcpConn ) { KIRQL OldIrql; BOOLEAN fDone=FALSE;
ASSERT(VALID_TCPCONN(pTcpConn));
ACQUIRE_SPIN_LOCK(&pTcpConn->con_SpinLock, &OldIrql);
pTcpConn->con_RefCount--;
if (pTcpConn->con_RefCount == 0) { fDone = TRUE; ASSERT(pTcpConn->con_State & TCPCONN_STATE_CLOSING); }
RELEASE_SPIN_LOCK(&pTcpConn->con_SpinLock, OldIrql);
if (!fDone) { return; }
//
// this dude's life is over: do the needful to bid goodbye
//
#if 0
// if we are at DPC, we need to do all the cleanup (file handle closing etc.)
// at task time: queue an event
if (KeGetCurrentIrql() == DISPATCH_LEVEL) { // queue an event, since we are at dpc
DsiScheduleWorkerEvent(DsiFreeConnection, pTcpConn); } else { DsiFreeConnection(pTcpConn); } #endif
// schedule a worker event to free this connection
DsiScheduleWorkerEvent(DsiFreeConnection, pTcpConn);
return; }
/*** DsiDestroyAdapter
* * This routine destroys the global adapter object. * * Returns: status of operation * */ NTSTATUS DsiDestroyAdapter( IN VOID ) { KIRQL OldIrql; PLIST_ENTRY pFreeList; PLIST_ENTRY pActiveList; PTCPCONN pTcpConn; BOOLEAN fAlreadyCleanedUp=FALSE;
if (DsiTcpAdapter == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiDestroyAdapter: adapter gone! How did this happen!!\n"));
// unblock the event!
KeSetEvent(&DsiShutdownEvent, IO_NETWORK_INCREMENT, False); return(STATUS_SUCCESS); }
// stop the tickle timer
if (!AfpScavengerKillEvent(DsiSendTickles, NULL)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiDestroyAdapter: TickleTimer not running or hit timing window!!\n")); }
ACQUIRE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, &OldIrql);
if (DsiTcpAdapter->adp_State & TCPADPTR_STATE_CLEANED_UP) { fAlreadyCleanedUp = TRUE; }
DsiTcpAdapter->adp_State |= TCPADPTR_STATE_CLOSING; DsiTcpAdapter->adp_State |= TCPADPTR_STATE_CLEANED_UP;
if (fAlreadyCleanedUp) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiDestroyAdapter: already destroyed!\n"));
ASSERT(IsListEmpty(&DsiTcpAdapter->adp_FreeConnHead)); ASSERT(IsListEmpty(&DsiTcpAdapter->adp_ActiveConnHead)); ASSERT(DsiTcpAdapter->adp_NumFreeConnections == 0);
RELEASE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, OldIrql); return(STATUS_SUCCESS); }
//
// free up all the connections from the Free list
//
while (!IsListEmpty(&DsiTcpAdapter->adp_FreeConnHead)) { pFreeList = DsiTcpAdapter->adp_FreeConnHead.Flink;
pTcpConn = CONTAINING_RECORD(pFreeList, TCPCONN, con_Linkage);
RemoveEntryList(&pTcpConn->con_Linkage);
ASSERT(DsiTcpAdapter->adp_NumFreeConnections > 0);
DsiTcpAdapter->adp_NumFreeConnections--;
InitializeListHead(&pTcpConn->con_Linkage);
RELEASE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, OldIrql);
pTcpConn->con_State |= TCPCONN_STATE_CLOSING; DsiDereferenceConnection(pTcpConn);
DBGREFCOUNT(("DsiDestroyAdapter: Creation dec %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState));
ACQUIRE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, &OldIrql); }
//
// kill all the connections from the Active list
//
pActiveList = DsiTcpAdapter->adp_ActiveConnHead.Flink;
while (pActiveList != &DsiTcpAdapter->adp_ActiveConnHead) { pTcpConn = CONTAINING_RECORD(pActiveList, TCPCONN, con_Linkage);
pActiveList = pActiveList->Flink;
ACQUIRE_SPIN_LOCK_AT_DPC(&pTcpConn->con_SpinLock);
// if this connection is already closing, skip it
if (pTcpConn->con_State & TCPCONN_STATE_CLOSING) { RELEASE_SPIN_LOCK_FROM_DPC(&pTcpConn->con_SpinLock); continue; }
// put ABORT refcount for now
pTcpConn->con_RefCount++;
DBGREFCOUNT(("DsiDestroyAdapter: ABORT inc %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState));
RemoveEntryList(&pTcpConn->con_Linkage); InitializeListHead(&pTcpConn->con_Linkage);
RELEASE_SPIN_LOCK_FROM_DPC(&pTcpConn->con_SpinLock);
RELEASE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, OldIrql);
DsiAbortConnection(pTcpConn);
// remove that ABORT refcount
DsiDereferenceConnection(pTcpConn);
DBGREFCOUNT(("DsiDestroyAdapter: ABORT dec %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState));
ACQUIRE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, &OldIrql);
// since we released the lock, things could have changed: start over
pActiveList = DsiTcpAdapter->adp_ActiveConnHead.Flink; }
RELEASE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, OldIrql);
// remove the creation refcount
DsiDereferenceAdapter(DsiTcpAdapter);
return(STATUS_SUCCESS);
}
/*** DsiKillConnection
* * This routine kills an active connection. * * Parm IN: pTcpConn - the connection to kill * * Returns: TRUE if we killed it, FALSE if we couldn't * */ BOOLEAN DsiKillConnection( IN PTCPCONN pTcpConn, IN DWORD DiscFlag ) { KIRQL OldIrql; NTSTATUS status; PDSIREQ pPartialDsiReq=NULL; BOOLEAN fFirstVisit=TRUE;
ACQUIRE_SPIN_LOCK(&pTcpConn->con_SpinLock, &OldIrql);
if (pTcpConn->con_State & TCPCONN_STATE_CLEANED_UP) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_WARN, ("DsiKillConnection: connection %lx already cleaned up\n",pTcpConn));
fFirstVisit = FALSE; }
pTcpConn->con_State &= ~TCPCONN_STATE_CONNECTED; pTcpConn->con_State |= (TCPCONN_STATE_CLOSING | TCPCONN_STATE_CLEANED_UP);
//
// if a request is waiting for an mdl to become available, don't touch it here.
// When afp returns with mdl (or null mdl), we'll clean up this request
//
if (pTcpConn->con_RcvState != DSI_AWAITING_WRITE_MDL) { pPartialDsiReq = pTcpConn->con_pDsiReq; pTcpConn->con_pDsiReq = NULL; }
RELEASE_SPIN_LOCK(&pTcpConn->con_SpinLock, OldIrql);
if (pPartialDsiReq) { // if we had allocated an mdl, let afp know so afp can free it
if ((pPartialDsiReq->dsi_Command == DSI_COMMAND_WRITE) && (pPartialDsiReq->dsi_AfpRequest.rq_WriteMdl != NULL)) { AfpCB_RequestNotify(STATUS_REMOTE_DISCONNECT, pTcpConn->con_pSda, &pPartialDsiReq->dsi_AfpRequest); }
DsiFreeRequest(pPartialDsiReq);
// remove the REQUEST refcount
DsiDereferenceConnection(pTcpConn);
DBGREFCOUNT(("DsiKillConnection: REQUEST dec %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); }
status = (DiscFlag == TDI_DISCONNECT_ABORT)? STATUS_LOCAL_DISCONNECT: STATUS_REMOTE_DISCONNECT;
// give AFP the bad news
DsiDisconnectWithAfp(pTcpConn, status);
// give TCP the bad news
DsiDisconnectWithTcp(pTcpConn, DiscFlag);
// remove the Creation refcount if this is the first time we're visiting
// this routine
if (fFirstVisit) { DsiDereferenceConnection(pTcpConn);
DBGREFCOUNT(("DsiKillConnection: Creation dec %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); }
return(TRUE); }
/*** DsiFreeAdapter
* * This routine frees the adapter object after closing the tcp handles * * Parm IN: pTcpAdptr - adapter object * * Returns: result of the operation * */ NTSTATUS FASTCALL DsiFreeAdapter( IN PTCPADPTR pTcpAdptr ) {
BOOLEAN fRecreateAdapter=FALSE; KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() != DISPATCH_LEVEL);
ASSERT(pTcpAdptr->adp_Signature == DSI_ADAPTER_SIGNATURE); ASSERT(pTcpAdptr->adp_State & TCPADPTR_STATE_CLOSING); ASSERT(pTcpAdptr->adp_RefCount == 0);
// close file handles
DsiCloseTdiAddress(pTcpAdptr);
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql);
DsiTcpAdapter = NULL; AfpServerBoundToTcp = FALSE;
//
// it's possible that by the time we did all the cleanup and everything,
// an ipaddress(es) became available. If that has happened, go ahead and
// create the global adapter again!
//
if (!IsListEmpty(&DsiIpAddrList)) { fRecreateAdapter = TRUE; }
// if we are shutting down, don't create the adapter again!
if ((AfpServerState == AFP_STATE_STOP_PENDING) || (AfpServerState == AFP_STATE_SHUTTINGDOWN) || (AfpServerState == AFP_STATE_STOPPED)) { fRecreateAdapter = FALSE; }
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
ASSERT(pTcpAdptr->adp_pFileObject == NULL); ASSERT(pTcpAdptr->adp_FileHandle == INVALID_HANDLE_VALUE);
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiFreeAdapter: freeing adapter %lx\n",pTcpAdptr));
AfpFreeMemory(pTcpAdptr);
// wake up that blocked thread!
KeSetEvent(&DsiShutdownEvent, IO_NETWORK_INCREMENT, False);
if (fRecreateAdapter) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiFreeAdapter: ipaddress came in, so recreating global adapter\n"));
DsiCreateAdapter(); }
return(STATUS_SUCCESS); }
/*** DsiFreeConnection
* * This routine frees the connection object after closing the tcp handles * * Parm IN: pTcpConn - connection object * * Returns: result of the operation * */ NTSTATUS FASTCALL DsiFreeConnection( IN PTCPCONN pTcpConn ) {
KIRQL OldIrql; PTCPADPTR pTcpAdptr; IPADDRESS IpAddress;
ASSERT(KeGetCurrentIrql() != DISPATCH_LEVEL);
ASSERT(pTcpConn->con_Signature == DSI_CONN_SIGNATURE); ASSERT(pTcpConn->con_State & TCPCONN_STATE_CLOSING); ASSERT(pTcpConn->con_RefCount == 0);
pTcpAdptr = pTcpConn->con_pTcpAdptr;
ASSERT(pTcpAdptr->adp_Signature == DSI_ADAPTER_SIGNATURE);
// close file handles
DsiCloseTdiConnection(pTcpConn);
// remove this puppy from the list
ACQUIRE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, &OldIrql); RemoveEntryList(&pTcpConn->con_Linkage); RELEASE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, OldIrql);
// remove the CONN refcount for this connection
DsiDereferenceAdapter(pTcpConn->con_pTcpAdptr);
ASSERT(pTcpConn->con_pFileObject == NULL); ASSERT(pTcpConn->con_FileHandle == INVALID_HANDLE_VALUE);
#if DBG
IpAddress = pTcpConn->con_DestIpAddr;
if (IpAddress) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_WARN, ("DsiFreeConnection: freeing connection on %d.%d.%d.%d (%lx)\n", (IpAddress>>24)&0xFF,(IpAddress>>16)&0xFF,(IpAddress>>8)&0xFF, IpAddress&0xFF,pTcpConn)); }
pTcpConn->con_Signature = 0xDEADBEEF; #endif
AfpFreeMemory(pTcpConn);
return(STATUS_SUCCESS); }
/*** DsiGetIpAddrBlob
* * This routine generates a 'blob' that gets plugged into the ServerInfo buffer. * Here we walk the ipaddr list and generate a blob with all the available * ipaddresses (6-byte-per-ipaddress_ * * Parm OUT: pIpAddrCount - how many ipaddresses there are in the system * ppIpAddrBlob - pointer to a pointer to a buffer * * Returns: status of operation * */ NTSTATUS DsiGetIpAddrBlob( OUT DWORD *pIpAddrCount, OUT PBYTE *ppIpAddrBlob ) { KIRQL OldIrql; PLIST_ENTRY pList; DWORD AddrCount=0; DWORD TmpCount=0; PBYTE AddrBlob; PBYTE pCurrentBlob; PIPADDRENTITY pIpaddrEntity;
*pIpAddrCount = 0; *ppIpAddrBlob = NULL;
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql);
if (!DsiTcpEnabled) { RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIpAddrBlob: Server is disabled\n"));
return(STATUS_SUCCESS); }
//
// find out how many ipaddresses are there on the list
//
AddrCount = 0; pList = DsiIpAddrList.Flink; while (pList != &DsiIpAddrList) { AddrCount++; pList = pList->Flink; }
if (AddrCount == 0) { RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIpAddrBlob: calling AfpSetServerStatus with 0 addrs\n"));
return(STATUS_SUCCESS); }
if (AddrCount > DSI_MAX_IPADDR_COUNT) { AddrCount = DSI_MAX_IPADDR_COUNT; }
AddrBlob = AfpAllocZeroedNonPagedMemory(AddrCount * DSI_NETWORK_ADDR_LEN);
if (AddrBlob == NULL) { RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIpAddrBlob: malloc failed\n"));
return(STATUS_INSUFFICIENT_RESOURCES); }
//
// create a "blob" that AfpSetServerStatus can directly copy
//
TmpCount = 0; pCurrentBlob = AddrBlob;
pList = DsiIpAddrList.Flink; while (pList != &DsiIpAddrList) { pIpaddrEntity = CONTAINING_RECORD(pList, IPADDRENTITY, Linkage);
pCurrentBlob[0] = DSI_NETWORK_ADDR_LEN; pCurrentBlob[1] = DSI_NETWORK_ADDR_IPTAG; PUTDWORD2DWORD(&pCurrentBlob[2], pIpaddrEntity->IpAddress);
pCurrentBlob += DSI_NETWORK_ADDR_LEN;
pList = pList->Flink;
TmpCount++; if (TmpCount == AddrCount) { break; } }
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
*pIpAddrCount = AddrCount; *ppIpAddrBlob = AddrBlob;
return(STATUS_SUCCESS); }
/*** DsiGetIrpForTcp
* * This routine is called when we need to pass an irp back to TCP to get the * remainint data (when it has more data than it has indicated to us). * Here we allocate an mdl if there isn't one already, and allocate and * initialize an irp ready to be sent to TCP * * Parm IN: pTcpConn - the connection object * pBuffer - buffer that TCP will copy data in * pInputMdl - if non-null, then we don't allocate a new mdl * ReadSize - how many bytes do we need * * Returns: pIrp if successful, NULL otherwise * * NOTE: pTcpConn spinlock is held on entry * */ PIRP DsiGetIrpForTcp( IN PTCPCONN pTcpConn, IN PBYTE pBuffer, IN PMDL pInputMdl, IN DWORD ReadSize ) { PDEVICE_OBJECT pDeviceObject; PIRP pIrp=NULL; PTDI_REQUEST_KERNEL_RECEIVE pRequest; PMDL pMdl;
pDeviceObject = IoGetRelatedDeviceObject(pTcpConn->con_pFileObject);
pIrp = AfpAllocIrp(pDeviceObject->StackSize); if (pIrp == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIrpForTcp: alloc irp failed!\n"));
return(NULL); }
if (pInputMdl) { pMdl = pInputMdl; } else { pMdl = AfpAllocMdl(pBuffer, ReadSize, NULL);
if (pMdl == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIrpForTcp: alloc mdl failed!\n"));
AfpFreeIrp(pIrp); return(NULL); } }
pTcpConn->con_pRcvIrp = pIrp;
pTcpConn->con_State |= TCPCONN_STATE_TCP_HAS_IRP;
// put TcpIRP refcount, removed when the irp completes
pTcpConn->con_RefCount++;
DBGREFCOUNT(("DsiGetIrpForTcp: TcpIRP inc %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState));
TdiBuildReceive(pIrp, pDeviceObject, pTcpConn->con_pFileObject, DsiTcpRcvIrpCompletion, (PVOID)pTcpConn, pMdl, TDI_RECEIVE_NORMAL, ReadSize);
//
// this irp will be returned to TCP, so do what IoSubSystem
// would have done if we had called IoCallDriver
//
IoSetNextIrpStackLocation(pIrp);
return(pIrp); }
/*** DsiMakePartialMdl
* * This routine is called when we need to reissue an Mdl (via irp) back to TCP * because TCP prematurely completed the previous irp (i.e. all the requested * bytes haven't come in yet, but say Push bit was set or something). In such * a case, we need to give a new mdl which accounts for the bytes we have got * so far (i.e. the offset has changed) * * Parm IN: pOrgMdl - the original Mdl we gave to TCp * dwOffset - what offset we want the new partial Mdl to describe * * Returns: the new partial Mdl if successful, NULL otherwise * */ PMDL DsiMakePartialMdl( IN PMDL pOrgMdl, IN DWORD dwOffset ) { PMDL pSubMdl; PMDL pPartialMdl=NULL; DWORD dwNewMdlLen; PVOID vAddr;
ASSERT(pOrgMdl != NULL); ASSERT(dwOffset > 0);
ASSERT(dwOffset < AfpMdlChainSize(pOrgMdl));
pSubMdl = pOrgMdl;
//
// get to the Mdl that is going to have this offset
//
while (dwOffset >= MmGetMdlByteCount(pSubMdl)) { dwOffset -= MmGetMdlByteCount(pSubMdl); pSubMdl = pSubMdl->Next;
ASSERT(pSubMdl != NULL); }
ASSERT(MmGetMdlByteCount(pSubMdl) > dwOffset);
vAddr = (PVOID)((PUCHAR)MmGetMdlVirtualAddress( pSubMdl ) + dwOffset);
dwNewMdlLen = MmGetMdlByteCount(pSubMdl) - dwOffset;
pPartialMdl = IoAllocateMdl(vAddr, dwNewMdlLen, FALSE, FALSE, NULL);
if (pPartialMdl == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiMakePartialMdl: IoAllocateMdl failed\n")); return(NULL); }
AFP_DBG_INC_COUNT(AfpDbgMdlsAlloced);
IoBuildPartialMdl(pSubMdl, pPartialMdl, vAddr, dwNewMdlLen);
// if there are further Mdl's down the original, link them on
pPartialMdl->Next = pSubMdl->Next;
return(pPartialMdl);
}
/*** DsiUpdateAfpStatus
* * This routine is just a wrapper function so that we can schedule an event to * call the real function AfpSetServerStatus * * Returns: status of operation * */ NTSTATUS FASTCALL DsiUpdateAfpStatus( IN PVOID Unused ) { NTSTATUS status;
status = AfpSetServerStatus();
return(status); }
/*** DsiScheduleWorkerEvent
* * This routine schedules an event for a later time. This routine is called * typically when we are at dpc but something (e.g. file handle operations) * needs to be done at passive level. This routine puts the request on the * worker queue. * * Parm IN: WorkerRoutine - the routine to be exececuted by the worker thread * Context - parameter for that routine * * Returns: result of the operation * */ NTSTATUS DsiScheduleWorkerEvent( IN DSI_WORKER WorkerRoutine, IN PVOID Context ) { PTCPWORKITEM pTWItem;
pTWItem = (PTCPWORKITEM)AfpAllocZeroedNonPagedMemory(sizeof(TCPWORKITEM)); if (pTWItem == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiScheduleWorkerEvent: alloc failed!\n")); return(STATUS_INSUFFICIENT_RESOURCES); }
AfpInitializeWorkItem(&pTWItem->tcp_WorkItem, DsiWorker, pTWItem);
pTWItem->tcp_Worker = WorkerRoutine; pTWItem->tcp_Context = Context;
AfpQueueWorkItem(&pTWItem->tcp_WorkItem);
return(STATUS_SUCCESS); }
/*** DsiScheduleWorkerEvent
* * This routine gets called by the worker thread when DsiScheduleWorkerEvent * schedules an event. This routine then calls the actual routine that was * scheduled for later time. * * Parm IN: Context - the work item * * Returns: result of the operation * */ VOID FASTCALL DsiWorker( IN PVOID Context ) { PTCPWORKITEM pTWItem; NTSTATUS status;
pTWItem = (PTCPWORKITEM)Context;
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
(*pTWItem->tcp_Worker)(pTWItem->tcp_Context);
AfpFreeMemory(pTWItem); }
/*** DsiShutdown
* * This routine is called when sfm is shutting down. We basically make sure * that all the resources are freed up, file handles closed etc. * * Returns: Nothing * */ VOID DsiShutdown( IN VOID ) { KIRQL OldIrql; PLIST_ENTRY pList; PIPADDRENTITY pIpaddrEntity; PDSIREQ pDsiReq=NULL;
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql);
while (!IsListEmpty(&DsiIpAddrList)) { pList = RemoveHeadList(&DsiIpAddrList);
pIpaddrEntity = CONTAINING_RECORD(pList, IPADDRENTITY, Linkage);
AfpFreeMemory(pIpaddrEntity); }
if (DsiStatusBuffer != NULL) { AfpFreeMemory(DsiStatusBuffer); DsiStatusBuffer = NULL; }
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
// kill the global adapter if it's around
if (DsiTcpAdapter) { KeClearEvent(&DsiShutdownEvent);
DsiDestroyAdapter();
// if the "adapter" is still hanging around, wait till it's gone
if (DsiTcpAdapter) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiShutdown: waiting for the TCP interface to go away...\n"));
AfpIoWait(&DsiShutdownEvent, NULL);
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiShutdown: ... and the wait is over!\n")); } }
ACQUIRE_SPIN_LOCK(&DsiResourceLock, &OldIrql);
while (!IsListEmpty(&DsiFreeRequestList)) { pList = RemoveHeadList(&DsiFreeRequestList); pDsiReq = CONTAINING_RECORD(pList, DSIREQ, dsi_Linkage);
AfpFreeMemory(pDsiReq); DsiFreeRequestListSize--; }
RELEASE_SPIN_LOCK(&DsiResourceLock, OldIrql);
ASSERT(DsiFreeRequestListSize == 0); }
|