Subject: U-Boot: Add simple IP/UDP fragmentation support
From: Frank Haverkamp <[EMAIL PROTECTED]>

http://tools.ietf.org/html/rfc2348 describes the TFTP block size option
which allows larger packets than the 512 byte default. This reduces the
number of TFTP ACKs significantly and improves performance.

To get the most benefit out of the tftp block size option the support
of defragmentation of IP/UDP packet is helpful. The current
implementation
should work even with packets received out of order. To enable the large
packet size the user should set "tftp_bsize" to a value like 16352.

We experimented with different packet sizes and found that more than
those
16KiB do not contribute much to the performance anymore. Therefore I
limited
the defragmentation buffer to 16KiB as not to not waste memory.

Multicast TFTP is not yet tested with this patch. Would be nice if 
someone who has set this up could give it a try.

Signed-off-by: Frank Haverkamp <[EMAIL PROTECTED]>
Signed-off-by: Josh Boyer <[EMAIL PROTECTED]>

---
 include/net.h |   17 ++++++
 net/net.c     |  155
+++++++++++++++++++++++++++++++++++++++++++++++++---------
 net/tftp.c    |   22 ++++++++
 net/tftp.h    |   10 +++
 4 files changed, 182 insertions(+), 22 deletions(-)

--- u-boot.git.orig/include/net.h
+++ u-boot.git/include/net.h
@@ -200,6 +200,13 @@ typedef struct {
        ushort          udp_xsum;       /* Checksum                     */
 } IP_t;
 
+#define IP_OFFS                        0x1FFF  /* ip offset *= 8 */
+#define          IP_OFFS_SHIFT         3       /* in 8 byte steps */
+#define IP_FLAGS               0xE000  /* first 3 bits */
+#define          IP_FLAGS_RES          0x8000  /* reserved */
+#define          IP_FLAGS_DFRAG        0x4000  /* don't fragments */
+#define          IP_FLAGS_MFRAG        0x2000  /* more fragments */
+
 #define IP_HDR_SIZE_NO_UDP     (sizeof (IP_t) - 8)
 #define IP_HDR_SIZE            (sizeof (IP_t))
 
@@ -282,6 +289,16 @@ typedef struct icmphdr {
 #define PKTSIZE_ALIGN          1536
 /*#define PKTSIZE              608*/
 
+ /*
+  * IP/UDP Fragmentation support
+  * See: http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly
+  * MAX possible UDP packet size is 64 KiB, if there is memory
available.
+  */
+#define NET_ETH_MTU            1500
+#define NET_FRAG_BUF_SIZE      (16 * 1024) /* MAX is 64 KiB */
+#define NET_UDP_FRAG_SIZE      (NET_ETH_MTU - IP_HDR_SIZE_NO_UDP) /* 1480 */
+#define NET_FRAG_BUF_USED      (NET_FRAG_BUF_SIZE / NET_UDP_FRAG_SIZE + 1)
+
 /*
  * Maximum receive ring size; that is, the number of packets
  * we can buffer before overflow happens. Basically, this just
--- u-boot.git.orig/net/net.c
+++ u-boot.git/net/net.c
@@ -192,6 +192,15 @@ volatile uchar     PktBuf[(PKTBUFSRX+1) * PK
 
 volatile uchar *NetRxPackets[PKTBUFSRX]; /* Receive packets                    
*/
 
+/* Packet fragmentation support */
+static uint16_t ip_id = 0; /* sequence number */
+static uint16_t udp_len = 0;
+static uint16_t udp_src = 0;
+static uint16_t udp_dst = 0;
+static int max_idx = 0;
+static uchar NetFragBuf[NET_FRAG_BUF_SIZE];
+static char NetFragBufUsed[NET_FRAG_BUF_USED] = { 0, };
+
 static rxhand_f *packetHandler;                /* Current RX packet handler    
        */
 static thand_f *timeHandler;           /* Current timeout handler              
*/
 static ulong   timeStart;              /* Time base value                      
*/
@@ -288,6 +297,13 @@ NetLoop(proto_t protocol)
 {
        bd_t *bd = gd->bd;
 
+       /* Packet fragmentation support */
+       ip_id = udp_len = udp_src = udp_dst = max_idx = 0;
+       memset(NetFragBuf, 0xFF, sizeof(NetFragBuf));
+       memset(NetFragBufUsed, 0, sizeof(NetFragBufUsed));
+       printf("NetFragBuf @ %08x max tftp_bsize=%d udp_frag_size=%d\n",
+              NetFragBuf, TFTP_BLOCK_SIZE_MAX, NET_UDP_FRAG_SIZE);
+
 #ifdef CONFIG_NET_MULTI
        NetRestarted = 0;
        NetDevExists = 0;
@@ -1150,6 +1166,39 @@ static void CDPStart(void)
 }
 #endif
 
+#ifdef CONFIG_UDP_CHECKSUM
+/*
+ * @sumptr: Points to UDP data
+ * @sumlen: Size of UDP data
+ * @xsum: UDP checksum across IP source, destination address, protocol
and size
+ *
+ * Returns 0 when checksum is correct and -1 if it is not.
+ */
+static int udp_checksum(ushort *sumptr, ushort sumlen, ulong xsum)
+{
+       while (sumlen > 1) {
+              ushort sumdata;
+
+              sumdata = *sumptr++;
+              xsum += ntohs(sumdata);
+              sumlen -= 2;
+       }
+       if (sumlen > 0) {
+              ushort sumdata;
+
+              sumdata = *(unsigned char *) sumptr;
+              sumdata = (sumdata << 8) & 0xff00;
+              xsum += sumdata;
+       }
+       while ((xsum >> 16) != 0) {
+              xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 0x0000ffff);
+       }
+       if ((xsum != 0x00000000) && (xsum != 0x0000ffff))
+              return -1;
+
+       return 0;
+}
+#endif /* CONFIG_UDP_CHECKSUM */
 
 void
 NetReceive(volatile uchar * inpkt, int len)
@@ -1164,6 +1213,7 @@ NetReceive(volatile uchar * inpkt, int l
        int iscdp;
 #endif
        ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
+       uint32_t ip_off;        /* ip_off for fragmentation */
 
 #ifdef ET_DEBUG
        printf("packet received\n");
@@ -1404,9 +1454,7 @@ NetReceive(volatile uchar * inpkt, int l
                if ((ip->ip_hl_v & 0xf0) != 0x40) {
                        return;
                }
-               if (ip->ip_off & htons(0x1fff)) { /* Can't deal w/ fragments */
-                       return;
-               }
+
                /* can't deal with headers > 20 bytes */
                if ((ip->ip_hl_v & 0x0f) > 0x05) {
                        return;
@@ -1422,6 +1470,86 @@ NetReceive(volatile uchar * inpkt, int l
 #endif
                        return;
                }
+
+               /*
+                * Fragmentation support. We need to check the ip_id
+                * and if all fragments were received correctly.
+                */
+               ip_off = (ntohs(ip->ip_off) & IP_OFFS) << IP_OFFS_SHIFT;
+               if ((ip_off != 0) || (ip->ip_off & htons(IP_FLAGS_MFRAG))) {
+                       int size, idx, complete;
+                       char *start;
+
+                       /* New fragmented packet arrived, clear data. */
+                       if (ntohs(ip->ip_id) != ip_id) {
+                               ip_id = ntohs(ip->ip_id);
+                               memset(NetFragBufUsed, 0, 
sizeof(NetFragBufUsed));
+                               udp_len = udp_src = udp_dst = max_idx = 0;
+                       }
+
+                       idx = ip_off / NET_UDP_FRAG_SIZE;
+
+                       /* Packet does not fit into IP/UDP fragmentation buf */
+                       if (idx >= NET_FRAG_BUF_USED) {
+                               return;
+                       }
+
+                       NetFragBufUsed[idx] = 1;
+
+                       /* Copy the UDP hdr with the data for 1st
+                          fragment, else copy just payload */
+                       if (ip_off == 0) {
+                               udp_len = ntohs(ip->udp_len);
+                               udp_src = ntohs(ip->udp_src);
+                               udp_dst = ntohs(ip->udp_dst);
+                       }
+                       size = ntohs(ip->ip_len) - IP_HDR_SIZE_NO_UDP;
+                       start = (char *)ip + IP_HDR_SIZE_NO_UDP;
+                       memcpy(NetFragBuf + ip_off, start, size);
+
+                       /*
+                        * When last fragment has been received we
+                        * know the number of fragments we expect. If
+                        * all have arrived we process the packet.
+                        */
+                       if (((ip_off != 0) && !(ip->ip_off & 
htons(IP_FLAGS_MFRAG))))
+                               max_idx = idx;
+
+                       if (max_idx == 0)
+                               return;
+
+                       complete = 1;
+                       for (idx = 0; idx < max_idx; idx++) {
+                               if (NetFragBufUsed[idx] == 0) {
+                                       complete = 0;
+                                       break;
+                               }
+                       }
+                       if (!complete)
+                               return;
+#ifdef CONFIG_UDP_CHECKSUM
+                       if (ip->udp_xsum != 0) {
+                               ulong xsum = ip->ip_p;
+                               uint16_t *sumptr;
+
+                               xsum += udp_len;
+                               xsum += (ntohl(ip->ip_src) >> 16) & 0xffff;
+                               xsum += (ntohl(ip->ip_src) >>  0) & 0xffff;
+                               xsum += (ntohl(ip->ip_dst) >> 16) & 0xffff;
+                               xsum += (ntohl(ip->ip_dst) >>  0) & 0xffff;
+                               sumptr = (ushort *)NetFragBuf;
+
+                               if (udp_checksum(sumptr, udp_len, xsum)) {
+                                       putc('U');
+                                       return;
+                               }
+                       }
+#endif /* CONFIG_UDP_CHECKSUM */
+                       (*packetHandler)(NetFragBuf + 8, udp_dst, udp_src,
+                                        udp_len - 8);
+                       return;
+               }
+
                /*
                 * watch for ICMP host redirects
                 *
@@ -1502,26 +1630,9 @@ NetReceive(volatile uchar * inpkt, int l
                        sumlen = ntohs(ip->udp_len);
                        sumptr = (ushort *) &(ip->udp_src);
 
-                       while (sumlen > 1) {
-                               ushort sumdata;
-
-                               sumdata = *sumptr++;
-                               xsum += ntohs(sumdata);
-                               sumlen -= 2;
-                       }
-                       if (sumlen > 0) {
-                               ushort sumdata;
 
-                               sumdata = *(unsigned char *) sumptr;
-                               sumdata = (sumdata << 8) & 0xff00;
-                               xsum += sumdata;
-                       }
-                       while ((xsum >> 16) != 0) {
-                               xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 
0x0000ffff);
-                       }
-                       if ((xsum != 0x00000000) && (xsum != 0x0000ffff)) {
-                               printf(" UDP wrong checksum %08lx %08x\n",
-                                       xsum, ntohs(ip->udp_xsum));
+                       if (udp_checksum(sumptr, sumlen, xsum)) {
+                               putc('U');
                                return;
                        }
                }
--- u-boot.git.orig/net/tftp.c
+++ u-boot.git/net/tftp.c
@@ -456,6 +456,7 @@ TftpTimeout (void)
 void
 TftpStart (void)
 {
+       char *s, *err;
 #ifdef CONFIG_TFTP_PORT
        char *ep;             /* Environment pointer */
 #endif
@@ -518,6 +519,27 @@ TftpStart (void)
 
        puts ("Loading: *\b");
 
+       /* Get alternate tftp_bsize */
+       if ((s = getenv("tftp_bsize")) != NULL) {
+               err = NULL;
+
+               TftpBlkSizeOption = simple_strtoul(s, &err, 10);
+               if (*err) {
+                       printf("ERR: \"tftp_bsize\" is not a number\n");
+                       TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+               }
+               /*
+                * Reject values which require extensive handling.
+                * block size of 1428 octets (Ethernet MTU, less
+                * the TFTP, UDP and IP header lengths).
+                */
+               if (TftpBlkSizeOption > TFTP_BLOCK_SIZE_MAX) {
+                       printf("ERR: tftp_bsize larger than %d not supported\n",
+                              TFTP_BLOCK_SIZE_MAX);
+                       TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+               }
+       }
+
        NetSetTimeout (TIMEOUT * CFG_HZ, TftpTimeout);
        NetSetHandler (TftpHandler);
 
--- u-boot.git.orig/net/tftp.h
+++ u-boot.git/net/tftp.h
@@ -8,11 +8,21 @@
 #ifndef __TFTP_H__
 #define __TFTP_H__
 
+#include <net.h>
+
 /**********************************************************************/
 /*
  *     Global functions and variables.
  */
 
+/*
+ * Maximum TFTP block size bound to max size of fragmented IP/UDP
+ * packets minus TFTP and UDP/IP overhead. TFTP overhead is 2 byte
+ * opcode and 2 byte block-number.
+ */
+#define TFTP_BLOCK_SIZE_MAX     (NET_FRAG_BUF_SIZE - sizeof(IP_t) - 4)
+
+
 /* tftp.c */
 extern void    TftpStart (void);       /* Begin TFTP get */
 

-- 
IBM Systems & Technology Group, Integrated Systems Development / Open
Systems Firmware Development, IBM Deutschland Schoenaicher Str. 220
71032 Boeblingen Phone: +49-7031-16-5133 E-Mail: [EMAIL PROTECTED]

IBM Deutschland Research & Development GmbH / Vorsitzender des
Aufsichtsrats: Martin Jetter, Geschäftsführung: Erich Baier, Sitz der
Gesellschaft: Böblingen / Registergericht: Amtsgericht Stuttgart, HRB
243294

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to