All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Warren <biggerbadderben@gmail.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH] Add simple IP/UDP fragmentation support
Date: Wed, 27 Aug 2008 00:39:44 -0700	[thread overview]
Message-ID: <48B504C0.5060702@gmail.com> (raw)
In-Reply-To: <1218473412.10489.5.camel@localhost.localdomain>

Hi Josh & Frank,

A few nits.  Overall a nice patch.

One question: Will multicast TFTP still work when this is applied?

Josh Boyer wrote:
> From: Frank Haverkamp <haver@vnet.ibm.com>
>
> http://tools.ietf.org/html/rfc2348 describes the TFTP block size option
> which allows larger packtes than the 512 byte default. This reduces the
>   
s/packtes/packets/
> number of TFTP ACKs significantly and improves performance.
>
> To get the most benefit out of the tftp block size option the support
> of defragementation of IP/UDP packet is helpful. The current implemenation
>   
s/defragementation/defragmentation/
s/implemenation/implementation/

> should work even with packets received out of order. To enable the large
> packet size the user should set "tftp_block_size" so a value like 16352.
>
>   
s/so/to/
> We experimented with different packet sizes and found that more than those
> 16KiB do not contribute much to the performance anymore. Therefor I limited
>   
s/Therefor/Therefore/
> the defragmentation buffer to 16KiB no too waste memory.
>
>   
so as to not waste memory
> Signed-off-by: Frank Haverkamp <haver@vnet.ibm.com>
> Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
>
> ---
>  include/net.h |   17 ++++++
>  net/net.c     |  156 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
>  net/tftp.c    |   22 ++++++++
>  net/tftp.h    |   10 +++
>  4 files changed, 185 insertions(+), 20 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 */
> +
>   
Please align these better.  After applying, it's:

#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_block_size=%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
>   
Can you return -1 on failure?  Not a big deal, just more conventional.
> + */
> +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 off;		/* ip_off for fragmentation */
>   
Can you pick a better variable name than 'off'?  'offset' maybe?
>  
>  #ifdef ET_DEBUG
>  	printf("packet received\n");
> @@ -1404,9 +1454,11 @@ NetReceive(volatile uchar * inpkt, int l
>  		if ((ip->ip_hl_v & 0xf0) != 0x40) {
>  			return;
>  		}
> +#if 0		/* Obsolete after adding the fragmentation support */
>  		if (ip->ip_off & htons(0x1fff)) { /* Can't deal w/ fragments */
>  			return;
>  		}
> +#endif
>   
Please delete this block if it's now dead code.
>  		/* can't deal with headers > 20 bytes */
>  		if ((ip->ip_hl_v & 0x0f) > 0x05) {
>  			return;
> @@ -1422,6 +1474,88 @@ NetReceive(volatile uchar * inpkt, int l
>  #endif
>  			return;
>  		}
> +
> +		/*
> +		 * Fragmentation support. We need to check the ip_id
> +		 * and if all fragments were received correctly.
> +		 */
> +		off = (ntohs(ip->ip_off) & IP_OFFS) << IP_OFFS_SHIFT;
> +		if ((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 = 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 (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 + off, start, size);
> +
> +			/*
> +			 * When last fragement has been received we
>   
s/fragement/fragment/
> +			 * know the number of fragments we expect. If
> +			 * all have arrived we process the packet.
> +			 */
> +			if (((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);
>   
Can some of these arguments share lines?
> +			return;
> +		}
> +
>  		/*
>  		 * watch for ICMP host redirects
>  		 *
> @@ -1502,26 +1636,8 @@ 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_block_size */
> +	if ((s = getenv("tftp_block_size")) != NULL) {
>   
Kind of a long environment variable name.  Maybe "tftp_bs"?
> +		err = NULL;
> +
> +		TftpBlkSizeOption = simple_strtoul(s, &err, 10);
> +		if (*err) {
> +			printf("ERR: \"tftp_block_size\" 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_block_sizes 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 */
>  
>
>   
regards,
Ben

  parent reply	other threads:[~2008-08-27  7:39 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-08-11 16:50 [U-Boot] [PATCH] Add simple IP/UDP fragmentation support Josh Boyer
2008-08-11 17:40 ` Ben Warren
2008-08-27  7:39 ` Ben Warren [this message]
2008-09-03 16:50   ` Frank Haverkamp
2008-10-27 23:06     ` Wolfgang Denk
2008-10-27 23:27       ` Ben Warren
2008-10-17  5:58 ` Ben Warren
2008-10-28  6:25 ` Ben Warren

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=48B504C0.5060702@gmail.com \
    --to=biggerbadderben@gmail.com \
    --cc=u-boot@lists.denx.de \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.