reactos/lib/drivers/ip/transport/tcp/event.c
Claudiu Mihail ddd3bd5f7a [lwIP/TCPIP]
- Fix completing IRPs with 0 bytes received when getting the data from the connection's packet queue
- Eliminate memory leaks caused by unreleased pbufs
- Get rid of some commented code

In principle the speed issue with lwIP should be pretty much solved now. There's still some minor things to iron out that testing will reveal probably. Initial tests like running opera, downloading stuff etc seem to be very encouraging however.

svn path=/branches/GSoC_2011/TcpIpDriver/; revision=52894
2011-07-26 11:25:24 +00:00

395 lines
12 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS TCP/IP protocol driver
* FILE: transport/tcp/event.c
* PURPOSE: Transmission Control Protocol
* PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
*/
#include "precomp.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/pbuf.h"
#include "lwip/tcp.h"
#include "lwip/api.h"
#include "rosip.h"
static const char * const tcp_state_str[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RCVD",
"ESTABLISHED",
"FIN_WAIT_1",
"FIN_WAIT_2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
static
VOID
BucketCompletionWorker(PVOID Context)
{
PTDI_BUCKET Bucket = (PTDI_BUCKET)Context;
PTCP_COMPLETION_ROUTINE Complete;
Complete = (PTCP_COMPLETION_ROUTINE)Bucket->Request.RequestNotifyObject;
Complete(Bucket->Request.RequestContext, Bucket->Status, Bucket->Information);
DereferenceObject(Bucket->AssociatedEndpoint);
ExFreePoolWithTag(Bucket, TDI_BUCKET_TAG);
}
VOID
CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket, BOOLEAN Synchronous)
{
ReferenceObject(Connection);
Bucket->AssociatedEndpoint = Connection;
if (Synchronous)
{
BucketCompletionWorker(Bucket);
}
else
{
ChewCreate(BucketCompletionWorker, Bucket);
}
}
VOID
FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status)
{
PTDI_BUCKET Bucket;
PLIST_ENTRY Entry;
ReferenceObject(Connection);
DbgPrint("[IP, FlushAllQueues] Flushing recv/all with status: 0x%x for Connection = 0x%x\n", Status, Connection);
while ((Entry = ExInterlockedRemoveHeadList(&Connection->ReceiveRequest, &Connection->Lock)))
{
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
TI_DbgPrint(DEBUG_TCP,
("Completing Receive request: %x %x\n",
Bucket->Request, Status));
Bucket->Status = Status;
Bucket->Information = 0;
CompleteBucket(Connection, Bucket, FALSE);
}
// Calling with Status == STATUS_SUCCESS means that we got a graceful closure
// so we don't want to kill everything else since send is still valid in this state
//
if (Status == STATUS_SUCCESS)
{
DbgPrint("[IP, FlushAllQueues] Flushed recv only after graceful closure\n");
DereferenceObject(Connection);
return;
}
while ((Entry = ExInterlockedRemoveHeadList(&Connection->ListenRequest, &Connection->Lock)))
{
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
Bucket->Status = Status;
Bucket->Information = 0;
DbgPrint("[IP, FlushAllQueues] Completing Listen request for Connection = 0x%x\n", Bucket->AssociatedEndpoint);
DereferenceObject(Bucket->AssociatedEndpoint);
CompleteBucket(Connection, Bucket, FALSE);
}
while ((Entry = ExInterlockedRemoveHeadList(&Connection->SendRequest, &Connection->Lock)))
{
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
TI_DbgPrint(DEBUG_TCP,
("Completing Send request: %x %x\n",
Bucket->Request, Status));
Bucket->Status = Status;
Bucket->Information = 0;
CompleteBucket(Connection, Bucket, FALSE);
}
while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
{
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
Bucket->Status = Status;
Bucket->Information = 0;
DbgPrint("[IP, FlushAllQueues] Completing Connection request for Connection = 0x%x\n", Bucket->AssociatedEndpoint);
CompleteBucket(Connection, Bucket, FALSE);
}
DereferenceObject(Connection);
DbgPrint("[IP, FlushAllQueues] Leaving\n");
}
VOID
TCPFinEventHandler(void *arg, err_t err)
{
PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
const NTSTATUS status = TCPTranslateError(err);
DbgPrint("[IP, TCPFinEventHandler] Called for Connection( 0x%x )-> SocketContext = pcb (0x%x)\n", Connection, Connection->SocketContext);
/* Only clear the pointer if the shutdown was caused by an error */
if ((err != ERR_OK))
{
/* We're already closed by the error so we don't want to call lwip_close */
DbgPrint("[IP, TCPFinEventHandler] MAKING Connection( 0x%x )-> SocketContext = pcb (0x%x) NULL\n", Connection, Connection->SocketContext);
Connection->SocketContext = NULL;
}
FlushAllQueues(Connection, status);
DbgPrint("[IP, TCPFinEventHandler] Done\n");
}
VOID
TCPAcceptEventHandler(void *arg, struct tcp_pcb *newpcb)
{
PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
PTDI_BUCKET Bucket;
PLIST_ENTRY Entry;
PIRP Irp;
NTSTATUS Status;
KIRQL OldIrql;
DbgPrint("[IP, TCPAcceptEventHandler] Called\n");
ReferenceObject(Connection);
while ((Entry = ExInterlockedRemoveHeadList(&Connection->ListenRequest, &Connection->Lock)))
{
PIO_STACK_LOCATION IrpSp;
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
Irp = Bucket->Request.RequestContext;
IrpSp = IoGetCurrentIrpStackLocation( Irp );
TI_DbgPrint(DEBUG_TCP,("[IP, TCPAcceptEventHandler] Getting the socket\n"));
Status = TCPCheckPeerForAccept(newpcb,
(PTDI_REQUEST_KERNEL)&IrpSp->Parameters);
TI_DbgPrint(DEBUG_TCP,("Socket: Status: %x\n", Status));
Bucket->Status = Status;
Bucket->Information = 0;
DbgPrint("[IP, TCPAcceptEventHandler] Completing accept event %x\n", Status);
if (Status == STATUS_SUCCESS)
{
DbgPrint("[IP, TCPAcceptEventHandler] newpcb->state = %s, listen_pcb->state = %s, newpcb = 0x%x\n",
tcp_state_str[newpcb->state],
tcp_state_str[((struct tcp_pcb*)Connection->SocketContext)->state],
newpcb);
LockObject(Bucket->AssociatedEndpoint, &OldIrql);
/* sanity assert...this should never be in anything else but a CLOSED state */
ASSERT(((struct tcp_pcb*)Bucket->AssociatedEndpoint->SocketContext)->state == CLOSED);
/* free socket context created in FileOpenConnection, as we're using a new one */
LibTCPClose(Bucket->AssociatedEndpoint, TRUE);
/* free previously created socket context (we don't use it, we use newpcb) */
Bucket->AssociatedEndpoint->SocketContext = newpcb;
LibTCPAccept(newpcb, (PTCP_PCB)Connection->SocketContext, Bucket->AssociatedEndpoint);
DbgPrint("[IP, TCPAcceptEventHandler] Trying to unlock Bucket->AssociatedEndpoint\n");
UnlockObject(Bucket->AssociatedEndpoint, OldIrql);
}
DereferenceObject(Bucket->AssociatedEndpoint);
DbgPrint("[IP, TCPAcceptEventHandler] Done!\n");
CompleteBucket(Connection, Bucket, FALSE);
}
DereferenceObject(Connection);
}
VOID
TCPSendEventHandler(void *arg, u16_t space)
{
PCONNECTION_ENDPOINT Connection = arg;
PTDI_BUCKET Bucket;
PLIST_ENTRY Entry;
PIRP Irp;
NTSTATUS Status;
PMDL Mdl;
DbgPrint("[IP, TCPSendEventHandler] Called\n");
ReferenceObject(Connection);
while ((Entry = ExInterlockedRemoveHeadList(&Connection->SendRequest, &Connection->Lock)))
{
UINT SendLen = 0;
PVOID SendBuffer = 0;
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
Irp = Bucket->Request.RequestContext;
Mdl = Irp->MdlAddress;
TI_DbgPrint(DEBUG_TCP,
("Getting the user buffer from %x\n", Mdl));
NdisQueryBuffer( Mdl, &SendBuffer, &SendLen );
TI_DbgPrint(DEBUG_TCP,
("Writing %d bytes to %x\n", SendLen, SendBuffer));
DbgPrint("[IP, TCPSendEventHandler] Sending out &d bytes on pcb = 0x%x\n",
Connection->SocketContext);
TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
TI_DbgPrint
(DEBUG_TCP,
("Connection->SocketContext: %x\n",
Connection->SocketContext));
Status = TCPTranslateError(LibTCPSend(Connection,
SendBuffer,
SendLen, TRUE));
TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", SendLen));
if( Status == STATUS_PENDING )
{
ExInterlockedInsertHeadList(&Connection->SendRequest,
&Bucket->Entry,
&Connection->Lock);
break;
}
else
{
TI_DbgPrint(DEBUG_TCP,
("Completing Send request: %x %x\n",
Bucket->Request, Status));
Bucket->Status = Status;
Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? SendLen : 0;
DbgPrint("Completing send req %x\n", Status);
CompleteBucket(Connection, Bucket, FALSE);
}
}
DereferenceObject(Connection);
DbgPrint("[IP, TCPSendEventHandler] Leaving\n");
}
u32_t
TCPRecvEventHandler(void *arg, struct pbuf *p)
{
PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
PTDI_BUCKET Bucket;
PLIST_ENTRY Entry;
PIRP Irp;
PMDL Mdl;
UINT Received = 0;
UINT RecvLen;
PUCHAR RecvBuffer;
ASSERT(p);
ReferenceObject(Connection);
DbgPrint("[IP, TCPRecvEventHandler] Called\n");
if ((Entry = ExInterlockedRemoveHeadList(&Connection->ReceiveRequest, &Connection->Lock)))
{
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
Irp = Bucket->Request.RequestContext;
Mdl = Irp->MdlAddress;
TI_DbgPrint(DEBUG_TCP,
("[IP, TCPRecvEventHandler] Getting the user buffer from %x\n", Mdl));
NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
TI_DbgPrint(DEBUG_TCP,
("[IP, TCPRecvEventHandler] Reading %d bytes to %x\n", RecvLen, RecvBuffer));
TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
TI_DbgPrint(DEBUG_TCP, ("[IP, TCPRecvEventHandler] Connection->SocketContext: %x\n", Connection->SocketContext));
TI_DbgPrint(DEBUG_TCP, ("[IP, TCPRecvEventHandler] RecvBuffer: %x\n", RecvBuffer));
RecvLen = MIN(p->tot_len, RecvLen);
for (Received = 0; Received < RecvLen; Received += p->len, p = p->next)
{
DbgPrint("[IP, TCPRecvEventHandler] 0x%x: Copying %d bytes to 0x%x from 0x%x\n",
p, p->len, ((PUCHAR)RecvBuffer) + Received, p->payload);
RtlCopyMemory(RecvBuffer + Received, p->payload, p->len);
}
TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", Received));
Bucket->Status = STATUS_SUCCESS;
Bucket->Information = Received;
CompleteBucket(Connection, Bucket, FALSE);
}
DereferenceObject(Connection);
DbgPrint("[IP, TCPRecvEventHandler] Leaving\n");
return Received;
}
VOID
TCPConnectEventHandler(void *arg, err_t err)
{
PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
PTDI_BUCKET Bucket;
PLIST_ENTRY Entry;
DbgPrint("[IP, TCPConnectEventHandler] Called\n");
ReferenceObject(Connection);
while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
{
Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
Bucket->Status = TCPTranslateError(err);
Bucket->Information = 0;
DbgPrint("[IP, TCPConnectEventHandler] Completing connection request! (0x%x)\n", err);
CompleteBucket(Connection, Bucket, FALSE);
}
DbgPrint("[IP, TCPConnectEventHandler] Done\n");
DereferenceObject(Connection);
}