Use lookaside lists for two most common allocation sizes (mbuf and data buffer)
[reactos.git] / reactos / drivers / lib / ip / transport / tcp / event.c
index c43987e..a30b3a0 100644 (file)
 
 #include "precomp.h"
 
-extern ULONG TCP_IPIdentification;
-extern LIST_ENTRY SleepingThreadsList;
-extern FAST_MUTEX SleepingThreadsLock;
-
 int TCPSocketState(void *ClientData,
-                  void *WhichSocket, 
+                  void *WhichSocket,
                   void *WhichConnection,
                   OSK_UINT NewState ) {
     PCONNECTION_ENDPOINT Connection = WhichConnection;
-    PTCP_COMPLETION_ROUTINE Complete;
-    PTDI_BUCKET Bucket;
-    PLIST_ENTRY Entry;
 
-    TI_DbgPrint(MID_TRACE,("Called: NewState %x (Conn %x)\n", 
-                          NewState, Connection));
+    TI_DbgPrint(MID_TRACE,("Flags: %c%c%c%c\n",
+                          NewState & SEL_CONNECT ? 'C' : 'c',
+                          NewState & SEL_READ    ? 'R' : 'r',
+                          NewState & SEL_FIN     ? 'F' : 'f',
+                          NewState & SEL_ACCEPT  ? 'A' : 'a'));
+
+    TI_DbgPrint(DEBUG_TCP,("Called: NewState %x (Conn %x) (Change %x)\n",
+                          NewState, Connection,
+                          Connection ? Connection->State ^ NewState :
+                          NewState));
 
     if( !Connection ) {
-       TI_DbgPrint(MID_TRACE,("Socket closing.\n"));
-       return 0;
+       TI_DbgPrint(DEBUG_TCP,("Socket closing.\n"));
+       Connection = FileFindConnectionByContext( WhichSocket );
+       if( !Connection )
+           return 0;
+       else
+           TI_DbgPrint(DEBUG_TCP,("Found socket %x\n", Connection));
     }
 
-    if( (NewState & SEL_CONNECT) && 
-       !(Connection->State & SEL_CONNECT) ) {
-       while( !IsListEmpty( &Connection->ConnectRequest ) ) {
-           Connection->State |= SEL_CONNECT;
-           Entry = RemoveHeadList( &Connection->ConnectRequest );
-           Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
-           Complete = Bucket->Request.RequestNotifyObject;
-           TI_DbgPrint(MID_TRACE,
-                       ("Completing Connect Request %x\n", Bucket->Request));
-           Complete( Bucket->Request.RequestContext, STATUS_SUCCESS, 0 );
-           /* Frees the bucket allocated in TCPConnect */
-           PoolFreeBuffer( Bucket );
-       }
-    } else if( (NewState & SEL_READ) || (NewState & SEL_FIN) ) {
-       TI_DbgPrint(MID_TRACE,("Readable (or closed): irp list %s\n",
-                              IsListEmpty(&Connection->ReceiveRequest) ?
-                              "empty" : "nonempty"));
-
-       while( !IsListEmpty( &Connection->ReceiveRequest ) ) {
-           PIRP Irp;
-           OSK_UINT RecvLen = 0, Received = 0;
-           OSK_PCHAR RecvBuffer = 0;
-           PMDL Mdl;
-           NTSTATUS Status;
-
-           Entry = RemoveHeadList( &Connection->ReceiveRequest );
-           Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
-           Complete = Bucket->Request.RequestNotifyObject;
-
-           TI_DbgPrint(MID_TRACE,
-                       ("Readable, Completing read request %x\n", 
-                        Bucket->Request));
-
-           Irp = Bucket->Request.RequestContext;
-           Mdl = Irp->MdlAddress;
-
-           TI_DbgPrint(MID_TRACE,
-                       ("Getting the user buffer from %x\n", Mdl));
-
-           NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
-
-           TI_DbgPrint(MID_TRACE,
-                       ("Reading %d bytes to %x\n", RecvLen, RecvBuffer));
-
-           if( NewState & SEL_FIN && !RecvLen ) {
-               Status = STATUS_END_OF_FILE;
-               Received = 0;
-           } else {
-               TI_DbgPrint(MID_TRACE, ("Connection: %x\n", Connection));
-               TI_DbgPrint
-                   (MID_TRACE, 
-                    ("Connection->SocketContext: %x\n", 
-                     Connection->SocketContext));
-               TI_DbgPrint(MID_TRACE, ("RecvBuffer: %x\n", RecvBuffer));
-
-               Status = TCPTranslateError
-                   ( OskitTCPRecv( Connection->SocketContext,
-                                   RecvBuffer,
-                                   RecvLen,
-                                   &Received,
-                                   0 ) );
-           }
+    TI_DbgPrint(MID_TRACE,("Connection signalled: %d\n",
+                          Connection->Signalled));
 
-           TI_DbgPrint(MID_TRACE,("TCP Bytes: %d\n", Received));
-
-           if( Status == STATUS_SUCCESS && Received != 0 ) {
-               TI_DbgPrint(MID_TRACE,("Received %d bytes with status %x\n",
-                                      Received, Status));
-               
-               TI_DbgPrint(MID_TRACE,
-                           ("Completing Receive Request: %x\n", 
-                            Bucket->Request));
-
-               Complete( Bucket->Request.RequestContext,
-                         STATUS_SUCCESS, Received );
-           } else if( Status == STATUS_PENDING || 
-                      (Status == STATUS_SUCCESS && Received == 0) ) {
-               InsertHeadList( &Connection->ReceiveRequest,
-                               &Bucket->Entry );
-               break;
-           } else {
-               TI_DbgPrint(MID_TRACE,
-                           ("Completing Receive request: %x %x\n",
-                            Bucket->Request, Status));
-               Complete( Bucket->Request.RequestContext, Status, 0 );
-           }
-       }
-    } 
+    Connection->SignalState |= NewState;
+    if( !Connection->Signalled ) {
+       Connection->Signalled = TRUE;
+       InsertTailList( &SignalledConnections, &Connection->SignalList );
+    }
 
     return 0;
 }
@@ -126,24 +51,20 @@ int TCPSocketState(void *ClientData,
 void TCPPacketSendComplete( PVOID Context,
                            PNDIS_PACKET NdisPacket,
                            NDIS_STATUS NdisStatus ) {
-    TI_DbgPrint(MID_TRACE,("called %x\n", NdisPacket));
+    TI_DbgPrint(DEBUG_TCP,("called %x\n", NdisPacket));
     FreeNdisPacket(NdisPacket);
-    TI_DbgPrint(MID_TRACE,("done\n"));
+    TI_DbgPrint(DEBUG_TCP,("done\n"));
 }
 
 #define STRINGIFY(x) #x
 
 int TCPPacketSend(void *ClientData, OSK_PCHAR data, OSK_UINT len ) {
-    NTSTATUS Status;
     NDIS_STATUS NdisStatus;
-    ROUTE_CACHE_NODE *RCN;
+    PNEIGHBOR_CACHE_ENTRY NCE;
     IP_PACKET Packet = { 0 };
     IP_ADDRESS RemoteAddress, LocalAddress;
     PIPv4_HEADER Header;
 
-    TI_DbgPrint(MID_TRACE,("TCP OUTPUT (%x:%d):\n", data, len));
-    OskitDumpBuffer( data, len );
-
     if( *data == 0x45 ) { /* IPv4 */
        Header = (PIPv4_HEADER)data;
        LocalAddress.Type = IP_ADDRESS_V4;
@@ -151,29 +72,27 @@ int TCPPacketSend(void *ClientData, OSK_PCHAR data, OSK_UINT len ) {
        RemoteAddress.Type = IP_ADDRESS_V4;
        RemoteAddress.Address.IPv4Address = Header->DstAddr;
     } else {
-       DbgPrint("Don't currently handle IPv6\n");
-       KeBugCheck(4);
+       TI_DbgPrint(MIN_TRACE,("Outgoing packet is not IPv4\n"));
+       OskitDumpBuffer( data, len );
+       return OSK_EINVAL;
     }
 
     RemoteAddress.Type = LocalAddress.Type = IP_ADDRESS_V4;
 
-    DbgPrint("OSKIT SENDING PACKET *** %x -> %x\n",
-            LocalAddress.Address.IPv4Address,
-            RemoteAddress.Address.IPv4Address);
-    
-    Status = RouteGetRouteToDestination( &RemoteAddress, NULL, &RCN );
-    
-    if( !NT_SUCCESS(Status) || !RCN ) return OSK_EADDRNOTAVAIL;
+    if(!(NCE = RouteGetRouteToDestination( &RemoteAddress ))) {
+       TI_DbgPrint(MIN_TRACE,("No route to %s\n", A2S(&RemoteAddress)));
+       return OSK_EADDRNOTAVAIL;
+    }
 
-    NdisStatus = AllocatePacketWithBuffer( &Packet.NdisPacket, NULL, 
+    NdisStatus = AllocatePacketWithBuffer( &Packet.NdisPacket, NULL,
                                           MaxLLHeaderSize + len );
-    
+
     if (NdisStatus != NDIS_STATUS_SUCCESS) {
-       TI_DbgPrint(MAX_TRACE, ("Error from NDIS: %08x\n", NdisStatus));
-       return STATUS_NO_MEMORY;
+       TI_DbgPrint(DEBUG_TCP, ("Error from NDIS: %08x\n", NdisStatus));
+       return OSK_ENOBUFS;
     }
 
-    GetDataPtr( Packet.NdisPacket, MaxLLHeaderSize, 
+    GetDataPtr( Packet.NdisPacket, MaxLLHeaderSize,
                (PCHAR *)&Packet.Header, &Packet.ContigSize );
 
     RtlCopyMemory( Packet.Header, data, len );
@@ -183,30 +102,17 @@ int TCPPacketSend(void *ClientData, OSK_PCHAR data, OSK_UINT len ) {
     Packet.SrcAddr = LocalAddress;
     Packet.DstAddr = RemoteAddress;
 
-    IPSendDatagram( &Packet, RCN, TCPPacketSendComplete, NULL );
+    IPSendDatagram( &Packet, NCE, TCPPacketSendComplete, NULL );
 
     if( !NT_SUCCESS(NdisStatus) ) return OSK_EINVAL;
     else return 0;
 }
 
-void *TCPMalloc( void *ClientData,
-                OSK_UINT Bytes, OSK_PCHAR File, OSK_UINT Line ) {
-    void *v = PoolAllocateBuffer( Bytes );
-    if( v ) TrackWithTag( FOURCC('f','b','s','d'), v, File, Line );
-    return v;
-}
-
-void TCPFree( void *ClientData,
-             void *data, OSK_PCHAR File, OSK_UINT Line ) {
-    UntrackFL( File, Line, data );
-    PoolFreeBuffer( data );
-}
-
 int TCPSleep( void *ClientData, void *token, int priority, char *msg,
              int tmio ) {
     PSLEEPING_THREAD SleepingThread;
-    
-    TI_DbgPrint(MID_TRACE,
+
+    TI_DbgPrint(DEBUG_TCP,
                ("Called TSLEEP: tok = %x, pri = %d, wmesg = %s, tmio = %x\n",
                 token, priority, msg, tmio));
 
@@ -219,7 +125,7 @@ int TCPSleep( void *ClientData, void *token, int priority, char *msg,
        InsertTailList( &SleepingThreadsList, &SleepingThread->Entry );
        TcpipReleaseFastMutex( &SleepingThreadsLock );
 
-       TI_DbgPrint(MID_TRACE,("Waiting on %x\n", token));
+       TI_DbgPrint(DEBUG_TCP,("Waiting on %x\n", token));
        KeWaitForSingleObject( &SleepingThread->Event,
                               WrSuspended,
                               KernelMode,
@@ -232,7 +138,7 @@ int TCPSleep( void *ClientData, void *token, int priority, char *msg,
 
        PoolFreeBuffer( SleepingThread );
     }
-    TI_DbgPrint(MID_TRACE,("Waiting finished: %x\n", token));
+    TI_DbgPrint(DEBUG_TCP,("Waiting finished: %x\n", token));
     return 0;
 }
 
@@ -244,12 +150,158 @@ void TCPWakeup( void *ClientData, void *token ) {
     Entry = SleepingThreadsList.Flink;
     while( Entry != &SleepingThreadsList ) {
        SleepingThread = CONTAINING_RECORD(Entry, SLEEPING_THREAD, Entry);
-       TI_DbgPrint(MID_TRACE,("Sleeper @ %x\n", SleepingThread));
+       TI_DbgPrint(DEBUG_TCP,("Sleeper @ %x\n", SleepingThread));
        if( SleepingThread->SleepToken == token ) {
-           TI_DbgPrint(MID_TRACE,("Setting event to wake %x\n", token));
+           TI_DbgPrint(DEBUG_TCP,("Setting event to wake %x\n", token));
            KeSetEvent( &SleepingThread->Event, IO_NETWORK_INCREMENT, FALSE );
        }
        Entry = Entry->Flink;
     }
     TcpipReleaseFastMutex( &SleepingThreadsLock );
 }
+
+/* Memory management routines
+ *
+ * By far the most requests for memory are either for 128 or 2048 byte blocks,
+ * so we want to satisfy those from lookaside lists. Unfortunately, the
+ * TCPFree() function doesn't pass the size of the block to be freed, so we
+ * need to keep track of it ourselves. We do it by prepending each block with
+ * 4 bytes, indicating if this is a 'L'arge (2048), 'S'mall (128) or 'O'ther
+ * block.
+ */
+
+/* Set to some non-zero value to get a profile of memory allocation sizes */
+#define MEM_PROFILE 0
+
+#define SMALL_SIZE 128
+#define LARGE_SIZE 2048
+
+#define SIGNATURE_LARGE TAG('L','L','L','L')
+#define SIGNATURE_SMALL TAG('S','S','S','S')
+#define SIGNATURE_OTHER TAG('O','O','O','O')
+#define TCP_TAG TAG('T','C','P',' ')
+
+static NPAGED_LOOKASIDE_LIST LargeLookasideList;
+static NPAGED_LOOKASIDE_LIST SmallLookasideList;
+
+NTSTATUS
+TCPMemStartup( void )
+{
+    ExInitializeNPagedLookasideList( &LargeLookasideList,
+                                     NULL,
+                                     NULL,
+                                     0,
+                                     LARGE_SIZE + sizeof( ULONG ),
+                                     TCP_TAG,
+                                     0 );
+    ExInitializeNPagedLookasideList( &SmallLookasideList,
+                                     NULL,
+                                     NULL,
+                                     0,
+                                     SMALL_SIZE + sizeof( ULONG ),
+                                     TCP_TAG,
+                                     0 );
+
+    return STATUS_SUCCESS;
+}
+
+void *TCPMalloc( void *ClientData,
+                OSK_UINT Bytes, OSK_PCHAR File, OSK_UINT Line ) {
+    void *v;
+    ULONG Signature;
+
+#if 0 != MEM_PROFILE
+    static OSK_UINT *Sizes = NULL, *Counts = NULL, ArrayAllocated = 0;
+    static OSK_UINT ArrayUsed = 0, AllocationCount = 0;
+    OSK_UINT i, NewSize, *NewArray;
+    int Found;
+
+    Found = 0;
+    for ( i = 0; i < ArrayUsed && ! Found; i++ ) {
+       Found = ( Sizes[i] == Bytes );
+       if ( Found ) {
+           Counts[i]++;
+       }
+    }
+    if ( ! Found ) {
+       if ( ArrayAllocated <= ArrayUsed ) {
+           NewSize = ( 0 == ArrayAllocated ? 16 : 2 * ArrayAllocated );
+           NewArray = PoolAllocateBuffer( 2 * NewSize * sizeof( OSK_UINT ) );
+           if ( NULL != NewArray ) {
+               if ( 0 != ArrayAllocated ) {
+                   memcpy( NewArray, Sizes,
+                           ArrayAllocated * sizeof( OSK_UINT ) );
+                   PoolFreeBuffer( Sizes );
+                   memcpy( NewArray + NewSize, Counts,
+                           ArrayAllocated * sizeof( OSK_UINT ) );
+                   PoolFreeBuffer( Counts );
+               }
+               Sizes = NewArray;
+               Counts = NewArray + NewSize;
+               ArrayAllocated = NewSize;
+           } else if ( 0 != ArrayAllocated ) {
+               PoolFreeBuffer( Sizes );
+               PoolFreeBuffer( Counts );
+               ArrayAllocated = 0;
+           }
+       }
+       if ( ArrayUsed < ArrayAllocated ) {
+           Sizes[ArrayUsed] = Bytes;
+           Counts[ArrayUsed] = 1;
+           ArrayUsed++;
+       }
+    }
+
+    if ( 0 == (++AllocationCount % MEM_PROFILE) ) {
+       TI_DbgPrint(DEBUG_TCP, ("Memory allocation size profile:\n"));
+       for ( i = 0; i < ArrayUsed; i++ ) {
+           TI_DbgPrint(DEBUG_TCP,
+                       ("Size %4u Count %5u\n", Sizes[i], Counts[i]));
+       }
+       TI_DbgPrint(DEBUG_TCP, ("End of memory allocation size profile\n"));
+    }
+#endif /* MEM_PROFILE */
+
+    if ( SMALL_SIZE == Bytes ) {
+       v = ExAllocateFromNPagedLookasideList( &SmallLookasideList );
+       Signature = SIGNATURE_SMALL;
+    } else if ( LARGE_SIZE == Bytes ) {
+       v = ExAllocateFromNPagedLookasideList( &LargeLookasideList );
+       Signature = SIGNATURE_LARGE;
+    } else {
+       v = PoolAllocateBuffer( Bytes + sizeof(ULONG) );
+       Signature = SIGNATURE_OTHER;
+    }
+    if( v ) {
+       *((ULONG *) v) = Signature;
+       v = (void *)((char *) v + sizeof(ULONG));
+       TrackWithTag( FOURCC('f','b','s','d'), v, (PCHAR)File, Line );
+    }
+
+    return v;
+}
+
+void TCPFree( void *ClientData,
+             void *data, OSK_PCHAR File, OSK_UINT Line ) {
+    ULONG Signature;
+
+    UntrackFL( (PCHAR)File, Line, data );
+    data = (void *)((char *) data - sizeof(ULONG));
+    Signature = *((ULONG *) data);
+    if ( SIGNATURE_SMALL == Signature ) {
+       ExFreeToNPagedLookasideList( &SmallLookasideList, data );
+    } else if ( SIGNATURE_LARGE == Signature ) {
+       ExFreeToNPagedLookasideList( &LargeLookasideList, data );
+    } else if ( SIGNATURE_OTHER == Signature ) {
+       PoolFreeBuffer( data );
+    } else {
+       ASSERT( FALSE );
+    }
+}
+
+void
+TCPMemShutdown( void )
+{
+    ExDeleteNPagedLookasideList( &SmallLookasideList );
+    ExDeleteNPagedLookasideList( &LargeLookasideList );
+}