Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e89e9cf5 authored by Ananda Raju's avatar Ananda Raju Committed by Arnaldo Carvalho de Melo
Browse files

[IPv4/IPv6]: UFO Scatter-gather approach



Attached is kernel patch for UDP Fragmentation Offload (UFO) feature.

1. This patch incorporate the review comments by Jeff Garzik.
2. Renamed USO as UFO (UDP Fragmentation Offload)
3. udp sendfile support with UFO

This patches uses scatter-gather feature of skb to generate large UDP
datagram. Below is a "how-to" on changes required in network device
driver to use the UFO interface.

UDP Fragmentation Offload (UFO) Interface:
-------------------------------------------
UFO is a feature wherein the Linux kernel network stack will offload the
IP fragmentation functionality of large UDP datagram to hardware. This
will reduce the overhead of stack in fragmenting the large UDP datagram to
MTU sized packets

1) Drivers indicate their capability of UFO using
dev->features |= NETIF_F_UFO | NETIF_F_HW_CSUM | NETIF_F_SG

NETIF_F_HW_CSUM is required for UFO over ipv6.

2) UFO packet will be submitted for transmission using driver xmit routine.
UFO packet will have a non-zero value for

"skb_shinfo(skb)->ufo_size"

skb_shinfo(skb)->ufo_size will indicate the length of data part in each IP
fragment going out of the adapter after IP fragmentation by hardware.

skb->data will contain MAC/IP/UDP header and skb_shinfo(skb)->frags[]
contains the data payload. The skb->ip_summed will be set to CHECKSUM_HW
indicating that hardware has to do checksum calculation. Hardware should
compute the UDP checksum of complete datagram and also ip header checksum of
each fragmented IP packet.

For IPV6 the UFO provides the fragment identification-id in
skb_shinfo(skb)->ip6_frag_id. The adapter should use this ID for generating
IPv6 fragments.

Signed-off-by: default avatarAnanda Raju <ananda.raju@neterion.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (forwarded)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@mandriva.com>
parent de514416
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -269,6 +269,8 @@ u32 ethtool_op_get_tso(struct net_device *dev);
int ethtool_op_set_tso(struct net_device *dev, u32 data);
int ethtool_op_set_tso(struct net_device *dev, u32 data);
int ethtool_op_get_perm_addr(struct net_device *dev, 
int ethtool_op_get_perm_addr(struct net_device *dev, 
			     struct ethtool_perm_addr *addr, u8 *data);
			     struct ethtool_perm_addr *addr, u8 *data);
u32 ethtool_op_get_ufo(struct net_device *dev);
int ethtool_op_set_ufo(struct net_device *dev, u32 data);


/**
/**
 * &ethtool_ops - Alter and report network device settings
 * &ethtool_ops - Alter and report network device settings
@@ -298,6 +300,8 @@ int ethtool_op_get_perm_addr(struct net_device *dev,
 * set_sg: Turn scatter-gather on or off
 * set_sg: Turn scatter-gather on or off
 * get_tso: Report whether TCP segmentation offload is enabled
 * get_tso: Report whether TCP segmentation offload is enabled
 * set_tso: Turn TCP segmentation offload on or off
 * set_tso: Turn TCP segmentation offload on or off
 * get_ufo: Report whether UDP fragmentation offload is enabled
 * set_ufo: Turn UDP fragmentation offload on or off
 * self_test: Run specified self-tests
 * self_test: Run specified self-tests
 * get_strings: Return a set of strings that describe the requested objects 
 * get_strings: Return a set of strings that describe the requested objects 
 * phys_id: Identify the device
 * phys_id: Identify the device
@@ -364,6 +368,8 @@ struct ethtool_ops {
	int	(*get_perm_addr)(struct net_device *, struct ethtool_perm_addr *, u8 *);
	int	(*get_perm_addr)(struct net_device *, struct ethtool_perm_addr *, u8 *);
	int	(*begin)(struct net_device *);
	int	(*begin)(struct net_device *);
	void	(*complete)(struct net_device *);
	void	(*complete)(struct net_device *);
	u32     (*get_ufo)(struct net_device *);
	int     (*set_ufo)(struct net_device *, u32);
};
};


/* CMDs currently supported */
/* CMDs currently supported */
@@ -400,6 +406,8 @@ struct ethtool_ops {
#define ETHTOOL_GTSO		0x0000001e /* Get TSO enable (ethtool_value) */
#define ETHTOOL_GTSO		0x0000001e /* Get TSO enable (ethtool_value) */
#define ETHTOOL_STSO		0x0000001f /* Set TSO enable (ethtool_value) */
#define ETHTOOL_STSO		0x0000001f /* Set TSO enable (ethtool_value) */
#define ETHTOOL_GPERMADDR	0x00000020 /* Get permanent hardware address */
#define ETHTOOL_GPERMADDR	0x00000020 /* Get permanent hardware address */
#define ETHTOOL_GUFO		0x00000021 /* Get UFO enable (ethtool_value) */
#define ETHTOOL_SUFO		0x00000022 /* Set UFO enable (ethtool_value) */


/* compatibility with older code */
/* compatibility with older code */
#define SPARC_ETH_GSET		ETHTOOL_GSET
#define SPARC_ETH_GSET		ETHTOOL_GSET
+1 −0
Original line number Original line Diff line number Diff line
@@ -308,6 +308,7 @@ struct net_device
#define NETIF_F_VLAN_CHALLENGED	1024	/* Device cannot handle VLAN packets */
#define NETIF_F_VLAN_CHALLENGED	1024	/* Device cannot handle VLAN packets */
#define NETIF_F_TSO		2048	/* Can offload TCP/IP segmentation */
#define NETIF_F_TSO		2048	/* Can offload TCP/IP segmentation */
#define NETIF_F_LLTX		4096	/* LockLess TX */
#define NETIF_F_LLTX		4096	/* LockLess TX */
#define NETIF_F_UFO             8192    /* Can offload UDP Large Send*/


	struct net_device	*next_sched;
	struct net_device	*next_sched;


+7 −0
Original line number Original line Diff line number Diff line
@@ -137,6 +137,8 @@ struct skb_shared_info {
	unsigned int	nr_frags;
	unsigned int	nr_frags;
	unsigned short	tso_size;
	unsigned short	tso_size;
	unsigned short	tso_segs;
	unsigned short	tso_segs;
	unsigned short  ufo_size;
	unsigned int    ip6_frag_id;
	struct sk_buff	*frag_list;
	struct sk_buff	*frag_list;
	skb_frag_t	frags[MAX_SKB_FRAGS];
	skb_frag_t	frags[MAX_SKB_FRAGS];
};
};
@@ -341,6 +343,11 @@ extern void skb_over_panic(struct sk_buff *skb, int len,
extern void	      skb_under_panic(struct sk_buff *skb, int len,
extern void	      skb_under_panic(struct sk_buff *skb, int len,
				      void *here);
				      void *here);


extern int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
			int getfrag(void *from, char *to, int offset,
			int len,int odd, struct sk_buff *skb),
			void *from, int length);

struct skb_seq_state
struct skb_seq_state
{
{
	__u32		lower_offset;
	__u32		lower_offset;
+14 −0
Original line number Original line Diff line number Diff line
@@ -2717,6 +2717,20 @@ int register_netdevice(struct net_device *dev)
		       dev->name);
		       dev->name);
		dev->features &= ~NETIF_F_TSO;
		dev->features &= ~NETIF_F_TSO;
	}
	}
	if (dev->features & NETIF_F_UFO) {
		if (!(dev->features & NETIF_F_HW_CSUM)) {
			printk(KERN_ERR "%s: Dropping NETIF_F_UFO since no "
					"NETIF_F_HW_CSUM feature.\n",
							dev->name);
			dev->features &= ~NETIF_F_UFO;
		}
		if (!(dev->features & NETIF_F_SG)) {
			printk(KERN_ERR "%s: Dropping NETIF_F_UFO since no "
					"NETIF_F_SG feature.\n",
					dev->name);
			dev->features &= ~NETIF_F_UFO;
		}
	}


	/*
	/*
	 *	nil rebuild_header routine,
	 *	nil rebuild_header routine,
+53 −0
Original line number Original line Diff line number Diff line
@@ -93,6 +93,20 @@ int ethtool_op_get_perm_addr(struct net_device *dev, struct ethtool_perm_addr *a
}
}
 
 


u32 ethtool_op_get_ufo(struct net_device *dev)
{
	return (dev->features & NETIF_F_UFO) != 0;
}

int ethtool_op_set_ufo(struct net_device *dev, u32 data)
{
	if (data)
		dev->features |= NETIF_F_UFO;
	else
		dev->features &= ~NETIF_F_UFO;
	return 0;
}

/* Handlers for each ethtool command */
/* Handlers for each ethtool command */


static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
@@ -483,6 +497,11 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data)
			return err;
			return err;
	}
	}


	if (!data && dev->ethtool_ops->set_ufo) {
		err = dev->ethtool_ops->set_ufo(dev, 0);
		if (err)
			return err;
	}
	return dev->ethtool_ops->set_sg(dev, data);
	return dev->ethtool_ops->set_sg(dev, data);
}
}


@@ -569,6 +588,32 @@ static int ethtool_set_tso(struct net_device *dev, char __user *useraddr)
	return dev->ethtool_ops->set_tso(dev, edata.data);
	return dev->ethtool_ops->set_tso(dev, edata.data);
}
}


static int ethtool_get_ufo(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_value edata = { ETHTOOL_GTSO };

	if (!dev->ethtool_ops->get_ufo)
		return -EOPNOTSUPP;
	edata.data = dev->ethtool_ops->get_ufo(dev);
	if (copy_to_user(useraddr, &edata, sizeof(edata)))
		 return -EFAULT;
	return 0;
}
static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_value edata;

	if (!dev->ethtool_ops->set_ufo)
		return -EOPNOTSUPP;
	if (copy_from_user(&edata, useraddr, sizeof(edata)))
		return -EFAULT;
	if (edata.data && !(dev->features & NETIF_F_SG))
		return -EINVAL;
	if (edata.data && !(dev->features & NETIF_F_HW_CSUM))
		return -EINVAL;
	return dev->ethtool_ops->set_ufo(dev, edata.data);
}

static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
{
{
	struct ethtool_test test;
	struct ethtool_test test;
@@ -854,6 +899,12 @@ int dev_ethtool(struct ifreq *ifr)
	case ETHTOOL_GPERMADDR:
	case ETHTOOL_GPERMADDR:
		rc = ethtool_get_perm_addr(dev, useraddr);
		rc = ethtool_get_perm_addr(dev, useraddr);
		break;
		break;
	case ETHTOOL_GUFO:
		rc = ethtool_get_ufo(dev, useraddr);
		break;
	case ETHTOOL_SUFO:
		rc = ethtool_set_ufo(dev, useraddr);
		break;
	default:
	default:
		rc =  -EOPNOTSUPP;
		rc =  -EOPNOTSUPP;
	}
	}
@@ -882,3 +933,5 @@ EXPORT_SYMBOL(ethtool_op_set_sg);
EXPORT_SYMBOL(ethtool_op_set_tso);
EXPORT_SYMBOL(ethtool_op_set_tso);
EXPORT_SYMBOL(ethtool_op_set_tx_csum);
EXPORT_SYMBOL(ethtool_op_set_tx_csum);
EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
EXPORT_SYMBOL(ethtool_op_set_ufo);
EXPORT_SYMBOL(ethtool_op_get_ufo);
Loading