Commit d2b2b46f authored by Vikram Narayanan's avatar Vikram Narayanan

net/udp: Add support for chaining of skbs

If the packet size is greater than mtu and the device supports chaining of
skbs, the skbs are constructed and chained in the next pointer. Then the head
skb is sent once to the driver and the driver asynchronously dispatches every
skb in the chain. This works for isolated drivers that supports async dispatch.
For this work ufo needs to be off. In recent kernels, ufo support is entirely
removed.
Signed-off-by: Vikram Narayanan's avatarVikram Narayanan <vikram186@gmail.com>
parent 273a998f
......@@ -75,6 +75,7 @@ enum {
NETIF_F_HW_TC_BIT, /* Offload TC infrastructure */
NETIF_F_PRIV_DATA_POOL_BIT, /* private pool for skb->data */
NETIF_F_CHAIN_SKB_BIT,
/*
* Add your fresh new feature above and remember to update
* netdev_features_strings[] in net/core/ethtool.c and maybe
......@@ -137,6 +138,7 @@ enum {
#define NETIF_F_BUSY_POLL __NETIF_F(BUSY_POLL)
#define NETIF_F_HW_TC __NETIF_F(HW_TC)
#define NETIF_F_PRIV_DATA_POOL __NETIF_F(PRIV_DATA_POOL)
#define NETIF_F_CHAIN_SKB __NETIF_F(CHAIN_SKB)
#define for_each_netdev_feature(mask_addr, bit) \
for_each_set_bit(bit, (unsigned long *)mask_addr, NETDEV_FEATURE_COUNT)
......
......@@ -734,6 +734,7 @@ struct sk_buff {
__u8 ipvs_property:1;
__u8 inner_protocol_type:1;
__u8 remcsum_offload:1;
__u8 chain_skb:1;
/* 3 or 5 bit hole */
#ifdef CONFIG_NET_SCHED
......
......@@ -2922,7 +2922,7 @@ static int xmit_one(struct sk_buff *skb, struct net_device *dev,
return rc;
}
struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
struct sk_buff *__dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
struct netdev_queue *txq, int *ret)
{
struct sk_buff *skb = first;
......@@ -2944,10 +2944,44 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *de
break;
}
}
out:
*ret = rc;
return skb;
}
/* send it by calling xmit once. In the KLCD driver, let's do do finish */
struct noinline sk_buff *__dev_hard_start_xmit_async(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret)
{
int rc = NETDEV_TX_OK;
skb->chain_skb = true;
rc = xmit_one(skb, dev, txq, skb->next != NULL);
if (unlikely(!dev_xmit_complete(rc))) {
goto out;
}
if (netif_xmit_stopped(txq) && skb) {
rc = NETDEV_TX_BUSY;
goto out;
}
out:
*ret = rc;
return skb;
}
struct noinline sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
struct netdev_queue *txq, int *ret)
{
if (dev->features & NETIF_F_CHAIN_SKB)
if (first->next)
return __dev_hard_start_xmit_async(first, dev, txq, ret);
return __dev_hard_start_xmit(first, dev, txq, ret);
}
static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
......
......@@ -529,6 +529,8 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
__be16 not_last_frag;
struct rtable *rt = skb_rtable(skb);
int err = 0;
bool first = true, chain = false;
struct sk_buff *head_skb, *oskb;
dev = rt->dst.dev;
......@@ -565,6 +567,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
if (skb_has_frag_list(skb)) {
struct sk_buff *frag, *frag2;
int first_len = skb_pagelen(skb);
int loop = 0;
if (first_len - hlen > mtu ||
((first_len - hlen) & 7) ||
......@@ -603,6 +606,11 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
iph->frag_off = htons(IP_MF);
ip_send_check(iph);
if (dev->features & NETIF_F_CHAIN_SKB) {
chain = first = true;
head_skb = oskb = skb;
}
for (;;) {
/* Prepare header of the next frame,
* before previous one went down. */
......@@ -625,17 +633,59 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
ip_send_check(iph);
}
err = output(net, sk, skb);
if (!err)
IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
if (err || !frag)
break;
pr_debug("%s:%u, calling ip_output %d | skb %p | skb->len %d | skb->next %p | frag %p | frag->len %d | frag->next %p \n", __func__,
__LINE__, ++loop, skb, skb ? skb->len : 0, skb ? skb->next : 0,
frag, frag ? frag->len : 0, frag ? frag->next : 0);
if (!chain) {
err = output(net, sk, skb);
skb = frag;
frag = skb->next;
skb->next = NULL;
if (!err)
IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
if (err || !frag)
break;
skb = frag;
frag = skb->next;
skb->next = NULL;
} else {
if (!frag)
break;
skb = frag;
frag = skb->next;
skb->next = NULL;
if (first) {
head_skb->next = skb;
oskb = skb;
first = false;
} else {
oskb->next = skb;
oskb = skb;
}
}
}
/* set the final skb to null */
if (chain) {
//skb->next = NULL;
pr_debug("%s, sending the chained skb\n", __func__);
/* send the head of this skb chain */
err = output(net, sk, head_skb);
if (err)
goto err_fast;
oskb = head_skb;
/* for all the packets, inc the stats */
while (oskb) {
IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
oskb = oskb->next;
}
} // if
if (err == 0) {
IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS);
......@@ -647,6 +697,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
kfree_skb(frag);
frag = skb;
}
err_fast:
IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
return err;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment