From 0cd8a05ec036ca8f950d4d4034a80bf485581b6a Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 17 Nov 2009 19:03:27 -0800 Subject: [PATCH] datapath: Allow minimum headroom to be set when copying buffers. If we need to copy an sk_buff in order to make it writable, allow the minimum headroom to be specified. This ensures that if we need to add additional data, such as a VLAN tag, we will not have to make a second copy. Solves bug #2197 in certain situations. --- datapath/actions.c | 25 +++++++++++++++++-------- datapath/actions.h | 1 - 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index 226a4f91..15263281 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -22,12 +22,21 @@ #include "actions.h" #include "openvswitch/datapath-protocol.h" -struct sk_buff * -make_writable(struct sk_buff *skb, gfp_t gfp) +static struct sk_buff * +make_writable(struct sk_buff *skb, unsigned min_headroom, gfp_t gfp) { if (skb_shared(skb) || skb_cloned(skb)) { - struct sk_buff *nskb = skb_copy(skb, gfp); + struct sk_buff *nskb; + unsigned headroom = max(min_headroom, skb_headroom(skb)); + + nskb = skb_copy_expand(skb, headroom, skb_tailroom(skb), gfp); if (nskb) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + /* Before 2.6.24 these fields were not copied when + * doing an skb_copy_expand. */ + nskb->ip_summed = skb->ip_summed; + nskb->csum = skb->csum; +#endif #if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18) /* These fields are copied in skb_clone but not in * skb_copy or related functions. We need to manually @@ -87,7 +96,7 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb, mask = VLAN_PCP_MASK; } - skb = make_writable(skb, gfp); + skb = make_writable(skb, VLAN_HLEN, gfp); if (!skb) return ERR_PTR(-ENOMEM); @@ -175,7 +184,7 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb, static struct sk_buff *strip_vlan(struct sk_buff *skb, struct odp_flow_key *key, gfp_t gfp) { - skb = make_writable(skb, gfp); + skb = make_writable(skb, 0, gfp); if (skb) { vlan_pull_tag(skb); key->dl_vlan = htons(ODP_VLAN_NONE); @@ -187,7 +196,7 @@ static struct sk_buff *set_dl_addr(struct sk_buff *skb, const struct odp_action_dl_addr *a, gfp_t gfp) { - skb = make_writable(skb, gfp); + skb = make_writable(skb, 0, gfp); if (skb) { struct ethhdr *eh = eth_hdr(skb); memcpy(a->type == ODPAT_SET_DL_SRC ? eh->h_source : eh->h_dest, @@ -223,7 +232,7 @@ static struct sk_buff *set_nw_addr(struct sk_buff *skb, if (key->dl_type != htons(ETH_P_IP)) return skb; - skb = make_writable(skb, gfp); + skb = make_writable(skb, 0, gfp); if (skb) { struct iphdr *nh = ip_hdr(skb); u32 *f = a->type == ODPAT_SET_NW_SRC ? &nh->saddr : &nh->daddr; @@ -260,7 +269,7 @@ set_tp_port(struct sk_buff *skb, struct odp_flow_key *key, else return skb; - skb = make_writable(skb, gfp); + skb = make_writable(skb, 0, gfp); if (skb) { struct udphdr *th = udp_hdr(skb); u16 *f = a->type == ODPAT_SET_TP_SRC ? &th->source : &th->dest; diff --git a/datapath/actions.h b/datapath/actions.h index bda6d55a..065c0e40 100644 --- a/datapath/actions.h +++ b/datapath/actions.h @@ -16,7 +16,6 @@ struct sk_buff; struct odp_flow_key; union odp_action; -struct sk_buff *make_writable(struct sk_buff *, gfp_t gfp); int dp_xmit_skb(struct sk_buff *); int execute_actions(struct datapath *dp, struct sk_buff *skb, struct odp_flow_key *key, -- 2.30.2