mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
xfrm: iptfs: add reusing received skb for the tunnel egress packet
Add an optimization of re-using the tunnel outer skb re-transmission of the inner packet to avoid skb allocation and copy. Signed-off-by: Christian Hopps <chopps@labn.net> Tested-by: Antony Antony <antony.antony@secunet.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
parent
0756947654
commit
3f3339885f
|
|
@ -601,12 +601,12 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data,
|
||||||
struct list_head *sublist)
|
struct list_head *sublist)
|
||||||
{
|
{
|
||||||
u8 hbytes[sizeof(struct ipv6hdr)];
|
u8 hbytes[sizeof(struct ipv6hdr)];
|
||||||
struct sk_buff *first_skb, *next, *skb;
|
struct sk_buff *defer, *first_skb, *next, *skb;
|
||||||
const unsigned char *old_mac;
|
const unsigned char *old_mac;
|
||||||
struct xfrm_iptfs_data *xtfs;
|
struct xfrm_iptfs_data *xtfs;
|
||||||
struct iphdr *iph;
|
struct iphdr *iph;
|
||||||
struct net *net;
|
struct net *net;
|
||||||
u32 remaining, iplen, iphlen, tail;
|
u32 first_iplen, iphlen, iplen, remaining, tail;
|
||||||
u32 capturelen;
|
u32 capturelen;
|
||||||
u64 seq;
|
u64 seq;
|
||||||
|
|
||||||
|
|
@ -614,6 +614,7 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data,
|
||||||
net = xs_net(x);
|
net = xs_net(x);
|
||||||
skb = skbseq->root_skb;
|
skb = skbseq->root_skb;
|
||||||
first_skb = NULL;
|
first_skb = NULL;
|
||||||
|
defer = NULL;
|
||||||
|
|
||||||
seq = __esp_seq(skb);
|
seq = __esp_seq(skb);
|
||||||
|
|
||||||
|
|
@ -688,23 +689,92 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data,
|
||||||
skb_prepare_seq_read(save, data, tail, skbseq);
|
skb_prepare_seq_read(save, data, tail, skbseq);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!first_skb)
|
if (first_skb) {
|
||||||
|
skb = NULL;
|
||||||
|
} else {
|
||||||
first_skb = skb;
|
first_skb = skb;
|
||||||
|
first_iplen = iplen;
|
||||||
|
|
||||||
capturelen = min(iplen, remaining);
|
/* We are going to skip over `data` bytes to reach the
|
||||||
skb = iptfs_pskb_extract_seq(iplen, skbseq, data, capturelen);
|
* start of the IP header of `iphlen` len for `iplen`
|
||||||
if (!skb) {
|
* inner packet.
|
||||||
/* skip to next packet or done */
|
*/
|
||||||
data += capturelen;
|
|
||||||
continue;
|
if (skb_has_frag_list(skb)) {
|
||||||
|
defer = skb;
|
||||||
|
skb = NULL;
|
||||||
|
} else if (data + iphlen <= skb_headlen(skb) &&
|
||||||
|
/* make sure our header is 32-bit aligned? */
|
||||||
|
/* ((uintptr_t)(skb->data + data) & 0x3) == 0 && */
|
||||||
|
skb_tailroom(skb) + tail - data >= iplen) {
|
||||||
|
/* Reuse the received skb.
|
||||||
|
*
|
||||||
|
* We have enough headlen to pull past any
|
||||||
|
* initial fragment data, leaving at least the
|
||||||
|
* IP header in the linear buffer space.
|
||||||
|
*
|
||||||
|
* For linear buffer space we only require that
|
||||||
|
* linear buffer space is large enough to
|
||||||
|
* eventually hold the entire reassembled
|
||||||
|
* packet (by including tailroom in the check).
|
||||||
|
*
|
||||||
|
* For non-linear tailroom is 0 and so we only
|
||||||
|
* re-use if the entire packet is present
|
||||||
|
* already.
|
||||||
|
*
|
||||||
|
* NOTE: there are many more options for
|
||||||
|
* sharing, KISS for now. Also, this can produce
|
||||||
|
* skb's with the IP header unaligned to 32
|
||||||
|
* bits. If that ends up being a problem then a
|
||||||
|
* check should be added to the conditional
|
||||||
|
* above that the header lies on a 32-bit
|
||||||
|
* boundary as well.
|
||||||
|
*/
|
||||||
|
skb_pull(skb, data);
|
||||||
|
|
||||||
|
/* our range just changed */
|
||||||
|
data = 0;
|
||||||
|
tail = skb->len;
|
||||||
|
remaining = skb->len;
|
||||||
|
|
||||||
|
skb->protocol = protocol;
|
||||||
|
skb_mac_header_rebuild(skb);
|
||||||
|
if (skb->mac_len)
|
||||||
|
eth_hdr(skb)->h_proto = skb->protocol;
|
||||||
|
|
||||||
|
/* all pointers could be changed now reset walk */
|
||||||
|
skb_abort_seq_read(skbseq);
|
||||||
|
skb_prepare_seq_read(skb, data, tail, skbseq);
|
||||||
|
} else {
|
||||||
|
/* We couldn't reuse the input skb so allocate a
|
||||||
|
* new one.
|
||||||
|
*/
|
||||||
|
defer = skb;
|
||||||
|
skb = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't trim `first_skb` until the end as we are
|
||||||
|
* walking that data now.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
skb->protocol = protocol;
|
capturelen = min(iplen, remaining);
|
||||||
if (old_mac) {
|
if (!skb) {
|
||||||
/* rebuild the mac header */
|
skb = iptfs_pskb_extract_seq(iplen, skbseq, data,
|
||||||
skb_set_mac_header(skb, -first_skb->mac_len);
|
capturelen);
|
||||||
memcpy(skb_mac_header(skb), old_mac, first_skb->mac_len);
|
if (!skb) {
|
||||||
eth_hdr(skb)->h_proto = skb->protocol;
|
/* skip to next packet or done */
|
||||||
|
data += capturelen;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb->protocol = protocol;
|
||||||
|
if (old_mac) {
|
||||||
|
/* rebuild the mac header */
|
||||||
|
skb_set_mac_header(skb, -first_skb->mac_len);
|
||||||
|
memcpy(skb_mac_header(skb), old_mac, first_skb->mac_len);
|
||||||
|
eth_hdr(skb)->h_proto = skb->protocol;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data += capturelen;
|
data += capturelen;
|
||||||
|
|
@ -735,6 +805,16 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data,
|
||||||
/* this should not happen from the above code */
|
/* this should not happen from the above code */
|
||||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINIPTFSERROR);
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINIPTFSERROR);
|
||||||
|
|
||||||
|
if (first_skb && first_iplen && !defer && first_skb != xtfs->ra_newskb) {
|
||||||
|
/* first_skb is queued b/c !defer and not partial */
|
||||||
|
if (pskb_trim(first_skb, first_iplen)) {
|
||||||
|
/* error trimming */
|
||||||
|
list_del(&first_skb->list);
|
||||||
|
defer = first_skb;
|
||||||
|
}
|
||||||
|
first_skb->ip_summed = CHECKSUM_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Send the packets! */
|
/* Send the packets! */
|
||||||
list_for_each_entry_safe(skb, next, sublist, list) {
|
list_for_each_entry_safe(skb, next, sublist, list) {
|
||||||
skb_list_del_init(skb);
|
skb_list_del_init(skb);
|
||||||
|
|
@ -742,7 +822,20 @@ static bool __input_process_payload(struct xfrm_state *x, u32 data,
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
return false;
|
skb = skbseq->root_skb;
|
||||||
|
skb_abort_seq_read(skbseq);
|
||||||
|
|
||||||
|
if (defer) {
|
||||||
|
consume_skb(defer);
|
||||||
|
} else if (!first_skb) {
|
||||||
|
/* skb is the original passed in skb, but we didn't get far
|
||||||
|
* enough to process it as the first_skb, if we had it would
|
||||||
|
* either be save in ra_newskb, trimmed and sent on as an skb or
|
||||||
|
* placed in defer to be freed.
|
||||||
|
*/
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user