Skip to content

Commit

Permalink
datapath: move vlan pop/push functions into common code
Browse files Browse the repository at this point in the history
So it can be used from out of openvswitch code.
Did couple of cosmetic changes on the way, namely variable naming and
adding support for 8021AD proto.

Note on backwards compatability:
Unlike the upstream version, the backport of skb_vlan_push() does not
support translating a hardware accelerated 8021AD tag to software.
This is not a problem though as it preserves existing behaviour.

Upstream: 93515d53 ("net: move vlan pop/push functions into common code")
Signed-off-by: Thomas Graf <tgraf@noironetworks.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
  • Loading branch information
Thomas Graf committed Jan 7, 2015
1 parent 5cce04b commit 9789437
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 73 deletions.
2 changes: 2 additions & 0 deletions acinclude.m4
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
[OVS_DEFINE([HAVE_SKB_ZEROCOPY])])
OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [l4_rxhash])
OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_ensure_writable])
OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_pop])
OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_push])
OVS_GREP_IFELSE([$KSRC/include/linux/types.h], [bool],
[OVS_DEFINE([HAVE_BOOL_TYPE])])
Expand Down
83 changes: 10 additions & 73 deletions datapath/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,90 +212,29 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key,
return 0;
}

/* remove VLAN header from packet and update csum accordingly. */
static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
{
struct vlan_hdr *vhdr;
int err;

err = skb_ensure_writable(skb, VLAN_ETH_HLEN);
if (unlikely(err))
return err;

if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->csum = csum_sub(skb->csum, csum_partial(skb->data
+ (2 * ETH_ALEN), VLAN_HLEN, 0));

vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
*current_tci = vhdr->h_vlan_TCI;

memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
__skb_pull(skb, VLAN_HLEN);

vlan_set_encap_proto(skb, vhdr);
skb->mac_header += VLAN_HLEN;
/* Update mac_len for subsequent MPLS actions */
skb->mac_len -= VLAN_HLEN;

return 0;
}

static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)
{
__be16 tci;
int err;

if (likely(vlan_tx_tag_present(skb))) {
vlan_set_tci(skb, 0);
} else {
if (unlikely(skb->protocol != htons(ETH_P_8021Q) ||
skb->len < VLAN_ETH_HLEN))
return 0;

err = __pop_vlan_tci(skb, &tci);
if (err)
return err;
}
/* move next vlan tag to hw accel tag */
if (likely(skb->protocol != htons(ETH_P_8021Q) ||
skb->len < VLAN_ETH_HLEN)) {
err = skb_vlan_pop(skb);
if (vlan_tx_tag_present(skb))
invalidate_flow_key(key);
else
key->eth.tci = 0;
return 0;
}

invalidate_flow_key(key);
err = __pop_vlan_tci(skb, &tci);
if (unlikely(err))
return err;

__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(tci));
return 0;
return err;
}

static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
const struct ovs_action_push_vlan *vlan)
{
if (unlikely(vlan_tx_tag_present(skb))) {
u16 current_tag;

/* push down current VLAN tag */
current_tag = vlan_tx_tag_get(skb);

if (!vlan_insert_tag_set_proto(skb, skb->vlan_proto, current_tag))
return -ENOMEM;
/* Update mac_len for subsequent MPLS actions */
skb->mac_len += VLAN_HLEN;

if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->csum = csum_add(skb->csum, csum_partial(skb->data
+ (2 * ETH_ALEN), VLAN_HLEN, 0));

if (vlan_tx_tag_present(skb))
invalidate_flow_key(key);
} else {
else
key->eth.tci = vlan->vlan_tci;
}
__vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
return 0;

return skb_vlan_push(skb, vlan->vlan_tpid,
ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
}

static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *key,
Expand Down Expand Up @@ -865,8 +804,6 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,

case OVS_ACTION_ATTR_PUSH_VLAN:
err = push_vlan(skb, key, nla_data(a));
if (unlikely(err)) /* skb already freed. */
return err;
break;

case OVS_ACTION_ATTR_POP_VLAN:
Expand Down
10 changes: 10 additions & 0 deletions datapath/linux/compat/include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,14 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
int skb_ensure_writable(struct sk_buff *skb, int write_len);
#endif

#ifndef HAVE_SKB_VLAN_POP
#define skb_vlan_pop rpl_skb_vlan_pop
int skb_vlan_pop(struct sk_buff *skb);
#endif

#ifndef HAVE_SKB_VLAN_PUSH
#define skb_vlan_push rpl_skb_vlan_push
int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci);
#endif

#endif
97 changes: 97 additions & 0 deletions datapath/linux/compat/skbuff-openvswitch.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if_vlan.h>

#if !defined(HAVE_SKB_WARN_LRO) && defined(NETIF_F_LRO)

Expand Down Expand Up @@ -137,3 +138,99 @@ int skb_ensure_writable(struct sk_buff *skb, int write_len)
return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
}
#endif

#ifndef HAVE_SKB_VLAN_POP
/* remove VLAN header from packet and update csum accordingly. */
static int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci)
{
struct vlan_hdr *vhdr;
unsigned int offset = skb->data - skb_mac_header(skb);
int err;

__skb_push(skb, offset);
err = skb_ensure_writable(skb, VLAN_ETH_HLEN);
if (unlikely(err))
goto pull;

skb_postpull_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN);

vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
*vlan_tci = ntohs(vhdr->h_vlan_TCI);

memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
__skb_pull(skb, VLAN_HLEN);

vlan_set_encap_proto(skb, vhdr);
skb->mac_header += VLAN_HLEN;

if (skb_network_offset(skb) < ETH_HLEN)
skb_set_network_header(skb, ETH_HLEN);

skb_reset_mac_len(skb);
pull:
__skb_pull(skb, offset);

return err;
}

int skb_vlan_pop(struct sk_buff *skb)
{
u16 vlan_tci;
__be16 vlan_proto;
int err;

if (likely(vlan_tx_tag_present(skb))) {
skb->vlan_tci = 0;
} else {
if (unlikely((skb->protocol != htons(ETH_P_8021Q) &&
skb->protocol != htons(ETH_P_8021AD)) ||
skb->len < VLAN_ETH_HLEN))
return 0;

err = __skb_vlan_pop(skb, &vlan_tci);
if (err)
return err;
}
/* move next vlan tag to hw accel tag */
if (likely((skb->protocol != htons(ETH_P_8021Q) &&
skb->protocol != htons(ETH_P_8021AD)) ||
skb->len < VLAN_ETH_HLEN))
return 0;

vlan_proto = htons(ETH_P_8021Q);
err = __skb_vlan_pop(skb, &vlan_tci);
if (unlikely(err))
return err;

__vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci);
return 0;
}
#endif

#ifndef HAVE_SKB_VLAN_PUSH
int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci)
{
if (vlan_tx_tag_present(skb)) {
unsigned int offset = skb->data - skb_mac_header(skb);
int err;

/* __vlan_insert_tag expect skb->data pointing to mac header.
* So change skb->data before calling it and change back to
* original position later
*/
__skb_push(skb, offset);
err = __vlan_insert_tag(skb, skb->vlan_proto,
vlan_tx_tag_get(skb));
if (err)
return err;
skb->mac_len += VLAN_HLEN;
__skb_pull(skb, offset);

if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->csum = csum_add(skb->csum, csum_partial(skb->data
+ (2 * ETH_ALEN), VLAN_HLEN, 0));
}
__vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci);
return 0;
}
#endif

0 comments on commit 9789437

Please sign in to comment.