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

Commit f4c50d99 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller
Browse files

[NET]: Add software TSOv4



This patch adds the GSO implementation for IPv4 TCP.

Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f6a78bfc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1297,6 +1297,7 @@ extern void skb_split(struct sk_buff *skb,
				 struct sk_buff *skb1, const u32 len);

extern void	       skb_release_data(struct sk_buff *skb);
extern struct sk_buff *skb_segment(struct sk_buff *skb, int sg);

static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
				       int len, void *buffer)
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
struct net_protocol {
	int			(*handler)(struct sk_buff *skb);
	void			(*err_handler)(struct sk_buff *skb, u32 info);
	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb, int sg);
	int			no_policy;
};

+2 −0
Original line number Diff line number Diff line
@@ -1086,6 +1086,8 @@ extern struct request_sock_ops tcp_request_sock_ops;

extern int tcp_v4_destroy_sock(struct sock *sk);

extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg);

#ifdef CONFIG_PROC_FS
extern int  tcp4_proc_init(void);
extern void tcp4_proc_exit(void);
+126 −0
Original line number Diff line number Diff line
@@ -1842,6 +1842,132 @@ unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len)

EXPORT_SYMBOL_GPL(skb_pull_rcsum);

/**
 *	skb_segment - Perform protocol segmentation on skb.
 *	@skb: buffer to segment
 *	@sg: whether scatter-gather can be used for generated segments
 *
 *	This function performs segmentation on the given skb.  It returns
 *	the segment at the given position.  It returns NULL if there are
 *	no more segments to generate, or when an error is encountered.
 */
struct sk_buff *skb_segment(struct sk_buff *skb, int sg)
{
	struct sk_buff *segs = NULL;
	struct sk_buff *tail = NULL;
	unsigned int mss = skb_shinfo(skb)->gso_size;
	unsigned int doffset = skb->data - skb->mac.raw;
	unsigned int offset = doffset;
	unsigned int headroom;
	unsigned int len;
	int nfrags = skb_shinfo(skb)->nr_frags;
	int err = -ENOMEM;
	int i = 0;
	int pos;

	__skb_push(skb, doffset);
	headroom = skb_headroom(skb);
	pos = skb_headlen(skb);

	do {
		struct sk_buff *nskb;
		skb_frag_t *frag;
		int hsize, nsize;
		int k;
		int size;

		len = skb->len - offset;
		if (len > mss)
			len = mss;

		hsize = skb_headlen(skb) - offset;
		if (hsize < 0)
			hsize = 0;
		nsize = hsize + doffset;
		if (nsize > len + doffset || !sg)
			nsize = len + doffset;

		nskb = alloc_skb(nsize + headroom, GFP_ATOMIC);
		if (unlikely(!nskb))
			goto err;

		if (segs)
			tail->next = nskb;
		else
			segs = nskb;
		tail = nskb;

		nskb->dev = skb->dev;
		nskb->priority = skb->priority;
		nskb->protocol = skb->protocol;
		nskb->dst = dst_clone(skb->dst);
		memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
		nskb->pkt_type = skb->pkt_type;
		nskb->mac_len = skb->mac_len;

		skb_reserve(nskb, headroom);
		nskb->mac.raw = nskb->data;
		nskb->nh.raw = nskb->data + skb->mac_len;
		nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw);
		memcpy(skb_put(nskb, doffset), skb->data, doffset);

		if (!sg) {
			nskb->csum = skb_copy_and_csum_bits(skb, offset,
							    skb_put(nskb, len),
							    len, 0);
			continue;
		}

		frag = skb_shinfo(nskb)->frags;
		k = 0;

		nskb->ip_summed = CHECKSUM_HW;
		nskb->csum = skb->csum;
		memcpy(skb_put(nskb, hsize), skb->data + offset, hsize);

		while (pos < offset + len) {
			BUG_ON(i >= nfrags);

			*frag = skb_shinfo(skb)->frags[i];
			get_page(frag->page);
			size = frag->size;

			if (pos < offset) {
				frag->page_offset += offset - pos;
				frag->size -= offset - pos;
			}

			k++;

			if (pos + size <= offset + len) {
				i++;
				pos += size;
			} else {
				frag->size -= pos + size - (offset + len);
				break;
			}

			frag++;
		}

		skb_shinfo(nskb)->nr_frags = k;
		nskb->data_len = len - hsize;
		nskb->len += nskb->data_len;
		nskb->truesize += nskb->data_len;
	} while ((offset += len) < skb->len);

	return segs;

err:
	while ((skb = segs)) {
		segs = skb->next;
		kfree(skb);
	}
	return ERR_PTR(err);
}

EXPORT_SYMBOL_GPL(skb_segment);

void __init skb_init(void)
{
	skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
+51 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@
 */

#include <linux/config.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -1096,6 +1097,54 @@ int inet_sk_rebuild_header(struct sock *sk)

EXPORT_SYMBOL(inet_sk_rebuild_header);

static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int sg)
{
	struct sk_buff *segs = ERR_PTR(-EINVAL);
	struct iphdr *iph;
	struct net_protocol *ops;
	int proto;
	int ihl;
	int id;

	if (!pskb_may_pull(skb, sizeof(*iph)))
		goto out;

	iph = skb->nh.iph;
	ihl = iph->ihl * 4;
	if (ihl < sizeof(*iph))
		goto out;

	if (!pskb_may_pull(skb, ihl))
		goto out;

	skb->h.raw = __skb_pull(skb, ihl);
	iph = skb->nh.iph;
	id = ntohs(iph->id);
	proto = iph->protocol & (MAX_INET_PROTOS - 1);
	segs = ERR_PTR(-EPROTONOSUPPORT);

	rcu_read_lock();
	ops = rcu_dereference(inet_protos[proto]);
	if (ops && ops->gso_segment)
		segs = ops->gso_segment(skb, sg);
	rcu_read_unlock();

	if (IS_ERR(segs))
		goto out;

	skb = segs;
	do {
		iph = skb->nh.iph;
		iph->id = htons(id++);
		iph->tot_len = htons(skb->len - skb->mac_len);
		iph->check = 0;
		iph->check = ip_fast_csum(skb->nh.raw, iph->ihl);
	} while ((skb = skb->next));

out:
	return segs;
}

#ifdef CONFIG_IP_MULTICAST
static struct net_protocol igmp_protocol = {
	.handler =	igmp_rcv,
@@ -1105,6 +1154,7 @@ static struct net_protocol igmp_protocol = {
static struct net_protocol tcp_protocol = {
	.handler =	tcp_v4_rcv,
	.err_handler =	tcp_v4_err,
	.gso_segment =	tcp_tso_segment,
	.no_policy =	1,
};

@@ -1150,6 +1200,7 @@ static int ipv4_proc_init(void);
static struct packet_type ip_packet_type = {
	.type = __constant_htons(ETH_P_IP),
	.func = ip_rcv,
	.gso_segment = inet_gso_segment,
};

static int __init inet_init(void)
Loading