aboutsummaryrefslogtreecommitdiff
path: root/datapath/actions.c
diff options
context:
space:
mode:
authorJesse Gross <jesse@nicira.com>2009-11-17 19:03:27 -0800
committerJesse Gross <jesse@nicira.com>2009-11-18 13:51:55 -0800
commit0cd8a05ec036ca8f950d4d4034a80bf485581b6a (patch)
treef900560d8fc42051bcbb5f62460a25270efc0093 /datapath/actions.c
parent5ef800a6909fabfb2331a2da90450c23ca61aa06 (diff)
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.
Diffstat (limited to 'datapath/actions.c')
-rw-r--r--datapath/actions.c25
1 files changed, 17 insertions, 8 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;