Hi!

Here is the remaining part of task offloading features of rtl8139 emulation - the TCP segmentation offloading.
Tested with linux (ethtool -K eth0 tx on sg on tso on).

Please apply.

I'll try to provide slirp separation later.

--
Kind Regards,
Igor V. Kovalenko

Index: hw/rtl8139.c
===================================================================
RCS file: /cvsroot/qemu/qemu/hw/rtl8139.c,v
retrieving revision 1.3
diff -u -r1.3 rtl8139.c
--- hw/rtl8139.c	4 Jul 2006 10:08:36 -0000	1.3
+++ hw/rtl8139.c	4 Jul 2006 21:38:55 -0000
@@ -33,6 +33,9 @@
  *                                  Implemented PCI timer interrupt (disabled by default)
  *                                  Implemented Tally Counters, increased VM load/save version
  *                                  Implemented IP/TCP/UDP checksum task offloading
+ *
+ *  2006-Jul-04  Igor Kovalenko :   Implemented TCP segmentation offloading
+ *                                  Fixed MTU=1500 for produced ethernet frames
  */
 
 #include "vl.h"
@@ -1732,6 +1735,25 @@
     return ret;
 }
 
+static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size, int do_interrupt)
+{
+    if (!size)
+    {
+        DEBUG_PRINT(("RTL8139: +++ empty ethernet frame\n"));
+        return;
+    }
+
+    if (TxLoopBack == (s->TxConfig & TxLoopBack))
+    {
+        DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n"));
+        rtl8139_do_receive(s, buf, size, do_interrupt);
+    }
+    else
+    {
+        qemu_send_packet(s->vc, buf, size);
+    }
+}
+
 static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
 {
     if (!rtl8139_transmitter_enabled(s))
@@ -1762,15 +1784,7 @@
     s->TxStatus[descriptor] |= TxHostOwns;
     s->TxStatus[descriptor] |= TxStatOK;
 
-    if (TxLoopBack == (s->TxConfig & TxLoopBack))
-    {
-        DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n"));
-        rtl8139_do_receive(s, txbuffer, txsize, 0);
-    }
-    else
-    {
-        qemu_send_packet(s->vc, txbuffer, txsize);
-    }
+    rtl8139_transfer_frame(s, txbuffer, txsize, 0);
 
     DEBUG_PRINT(("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor));
 
@@ -1831,6 +1845,9 @@
 #define CP_TX_LS (1<<28)
 /* large send packet flag */
 #define CP_TX_LGSEN (1<<27)
+/* large send MSS mask, bits 16...25 */
+#define CP_TC_LGSEN_MSS_MASK ((1 << 12) - 1)
+
 /* IP checksum offload flag */
 #define CP_TX_IPCS (1<<18)
 /* UDP checksum offload flag */
@@ -1885,6 +1902,8 @@
         s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE;
         s->cplus_txbuffer = malloc(s->cplus_txbuffer_len);
         s->cplus_txbuffer_offset = 0;
+
+        DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer allocated space %d\n", s->cplus_txbuffer_len));
     }
 
     while (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len)
@@ -1960,16 +1979,19 @@
         s->cplus_txbuffer_offset = 0;
         s->cplus_txbuffer_len = 0;
 
-        if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS))
+        if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN))
         {
             DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task checksum\n"));
 
             #define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
             #define ETH_HLEN    14
+            #define ETH_MTU     1500
 
             /* ip packet header */
             register struct ip *ip = 0;
             int hlen = 0;
+            u_int8_t  ip_protocol = 0;
+            u_int16_t ip_data_len = 0;
 
             struct mbuf local_m;
 
@@ -1989,6 +2011,8 @@
                     ip = NULL;
                 } else {
                     hlen = ip->ip_hl << 2;
+                    ip_protocol = ip->ip_p;
+                    ip_data_len = ntohs(ip->ip_len) - hlen;
                 }
             }
 
@@ -2010,12 +2034,118 @@
                     }
                 }
 
-                if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
+                if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IPPROTO_TCP)
                 {
-                    DEBUG_PRINT(("RTL8139: +++ C+ mode need TCP or UDP checksum\n"));
+                    int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
+
+                    DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task TSO MTU=%d IP data %d frame data %d specified MSS=%d\n",
+                                 ETH_MTU, ip_data_len, saved_size - ETH_HLEN, large_send_mss));
+
+                    int tcp_send_offset = 0;
+                    int send_count = 0;
+
+                    //ip_data_len = saved_size - ETH_HLEN;
+
+                    /* maximum IP header length is 60 bytes */
+                    uint8_t saved_ip_header[60];
+
+                    /* save IP header template; data area is used in tcp checksum calculation */
+                    memcpy(saved_ip_header, local_m.m_data, hlen);
+
+                    /* a placeholder for checksum calculation routine in tcp case */
+                    struct mbuf local_checksum_m;
+
+                    local_checksum_m.m_data = local_m.m_data + hlen - 12;
+                    local_checksum_m.m_len  = local_m.m_len  - hlen + 12;
+
+                    /* pointer to TCP header */
+                    struct tcphdr* p_tcp_hdr = (struct tcphdr*) (local_m.m_data + hlen);
+
+                    int tcp_hlen = p_tcp_hdr->th_off << 4;
+
+                    /* ETH_MTU = ip header len + tcp header len + payload */
+                    int tcp_data_len = ip_data_len - hlen - tcp_hlen;
+                    int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
+
+                    DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP data len %d TCP hlen %d TCP data len %d TCP chunk size %d\n",
+                                 ip_data_len, tcp_hlen, tcp_data_len, tcp_chunk_size));
 
-                    u_int8_t  ip_protocol = ip->ip_p;
-                    u_int16_t ip_data_len = ntohs(ip->ip_len) - hlen;
+                    /* note the cycle below overwrites IP header data,
+                       but restores it from saved_ip_header before sending packet */
+
+                    int is_last_frame = 0;
+
+                    for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
+                    {
+                        uint16_t chunk_size = tcp_chunk_size;
+
+                        /* check if this is the last frame */
+                        if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
+                        {
+                            is_last_frame = 1;
+                            chunk_size = tcp_data_len - tcp_send_offset;
+                        }
+
+                        DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP seqno %08x\n", ntohl(p_tcp_hdr->th_seq)));
+
+                        /* add 4 TCP pseudoheader fields */
+                        /* copy IP source and destination fields */
+                        memcpy(local_checksum_m.m_data, saved_ip_header + 12, 8);
+
+                        DEBUG_PRINT(("RTL8139: +++ C+ mode TSO calculating TCP checksum for packet with %d bytes data\n", tcp_hlen + chunk_size));
+
+                        if (tcp_send_offset)
+                        {
+                            memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
+                        }
+
+                        /* keep PUSH and FIN flags only for the last frame */
+                        if (!is_last_frame)
+                        {
+                            p_tcp_hdr->th_flags &= ~(TH_PUSH|TH_FIN);
+                        }
+
+                        /* recalculate TCP checksum */
+                        struct tcpiphdr * p_tcpip_hdr = (struct tcpiphdr *)local_checksum_m.m_data;
+                        p_tcpip_hdr->ti_x1 = 0;
+                        p_tcpip_hdr->ti_pr = IPPROTO_TCP;
+                        p_tcpip_hdr->ti_len = htons(tcp_hlen + chunk_size);
+
+                        p_tcp_hdr->th_sum = 0;
+
+                        int tcp_checksum = cksum(&local_checksum_m, tcp_hlen + chunk_size + 12);
+                        DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP checksum %04x\n", tcp_checksum));
+
+                        p_tcp_hdr->th_sum = tcp_checksum;
+
+                        /* restore IP header */
+                        memcpy(local_m.m_data, saved_ip_header, hlen);
+
+                        /* set IP data length and recalculate IP checksum */
+                        ip->ip_len = htons(hlen + tcp_hlen + chunk_size);
+
+                        /* increment IP id for subsequent frames */
+                        ip->ip_id = htons(tcp_send_offset/tcp_chunk_size + ntohs(ip->ip_id));
+
+                        ip->ip_sum = 0;
+                        ip->ip_sum = cksum(&local_m, hlen); 
+                        DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP header len=%d checksum=%04x\n", hlen, ip->ip_sum));
+
+                        int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
+                        DEBUG_PRINT(("RTL8139: +++ C+ mode TSO transferring packet size %d\n", tso_send_size));
+                        rtl8139_transfer_frame(s, saved_buffer, tso_send_size, 0);
+
+                        /* add transferred count to TCP sequence number */
+                        p_tcp_hdr->th_seq = htonl(chunk_size + ntohl(p_tcp_hdr->th_seq));
+                        ++send_count;
+                    }
+
+                    /* Stop sending this frame */
+                    saved_size = 0;
+                }
+                else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
+                {
+                    DEBUG_PRINT(("RTL8139: +++ C+ mode need TCP or UDP checksum\n"));
 
                     /* maximum IP header length is 60 bytes */
                     uint8_t saved_ip_header[60];
@@ -2085,16 +2215,7 @@
 
         DEBUG_PRINT(("RTL8139: +++ C+ mode transmitting %d bytes packet\n", saved_size));
 
-        if (TxLoopBack == (s->TxConfig & TxLoopBack))
-        {
-            DEBUG_PRINT(("RTL8139: +++ C+ transmit loopback mode\n"));
-            rtl8139_receive(s, saved_buffer, saved_size);
-        }
-        else
-        {
-            /* transmit the packet */
-            qemu_send_packet(s->vc, saved_buffer, saved_size);
-        }
+        rtl8139_transfer_frame(s, saved_buffer, saved_size, 1);
 
         /* restore card space if there was no recursion and reset offset */
         if (!s->cplus_txbuffer)
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to