From: "Igor Kovalenko" <igor.v.kovalenko@gmail.com>
To: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] qemu/hw rtl8139.c
Date: Wed, 5 Jul 2006 01:45:06 +0400 [thread overview]
Message-ID: <b2fa41d60607041445v75145c13ufc22f44d151ae789@mail.gmail.com> (raw)
In-Reply-To: <E1Fxhpc-00056f-4Y@savannah.gnu.org>
[-- Attachment #1.1: Type: text/plain, Size: 268 bytes --]
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
[-- Attachment #1.2: Type: text/html, Size: 310 bytes --]
[-- Attachment #2: rtl8139-20060705-1.diff --]
[-- Type: text/x-patch, Size: 10215 bytes --]
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)
next prev parent reply other threads:[~2006-07-04 21:45 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-07-04 10:08 [Qemu-devel] qemu/hw rtl8139.c Fabrice Bellard
2006-07-04 21:45 ` Igor Kovalenko [this message]
2006-07-09 9:20 ` Igor Kovalenko
2006-07-10 21:19 ` Igor Kovalenko
-- strict thread matches above, loose matches on Subject: below --
2008-03-13 19:17 Aurelien Jarno
2007-11-09 18:17 Thiemo Seufer
2007-08-01 13:10 Thiemo Seufer
2007-04-07 1:41 Paul Brook
2007-03-19 18:20 Thiemo Seufer
2006-07-10 21:38 Fabrice Bellard
2006-07-04 11:23 ZIGLIO, Frediano, VF-IT
2006-07-01 21:41 Fabrice Bellard
2006-07-03 14:17 ` Paul Brook
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=b2fa41d60607041445v75145c13ufc22f44d151ae789@mail.gmail.com \
--to=igor.v.kovalenko@gmail.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).