diff options
Diffstat (limited to 'net/openvswitch/actions.c')
| -rw-r--r-- | net/openvswitch/actions.c | 79 | 
1 files changed, 57 insertions, 22 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 1ecbd7715f6d..4e03f64709bc 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -71,6 +71,8 @@ struct ovs_frag_data {  static DEFINE_PER_CPU(struct ovs_frag_data, ovs_frag_data_storage);  #define DEFERRED_ACTION_FIFO_SIZE 10 +#define OVS_RECURSION_LIMIT 5 +#define OVS_DEFERRED_ACTION_THRESHOLD (OVS_RECURSION_LIMIT - 2)  struct action_fifo {  	int head;  	int tail; @@ -78,7 +80,12 @@ struct action_fifo {  	struct deferred_action fifo[DEFERRED_ACTION_FIFO_SIZE];  }; +struct recirc_keys { +	struct sw_flow_key key[OVS_DEFERRED_ACTION_THRESHOLD]; +}; +  static struct action_fifo __percpu *action_fifos; +static struct recirc_keys __percpu *recirc_keys;  static DEFINE_PER_CPU(int, exec_actions_level);  static void action_fifo_init(struct action_fifo *fifo) @@ -153,7 +160,7 @@ static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr,  static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,  		     const struct ovs_action_push_mpls *mpls)  { -	__be32 *new_mpls_lse; +	struct mpls_shim_hdr *new_mpls_lse;  	/* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */  	if (skb->encapsulation) @@ -162,19 +169,23 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,  	if (skb_cow_head(skb, MPLS_HLEN) < 0)  		return -ENOMEM; +	if (!skb->inner_protocol) { +		skb_set_inner_network_header(skb, skb->mac_len); +		skb_set_inner_protocol(skb, skb->protocol); +	} +  	skb_push(skb, MPLS_HLEN);  	memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb),  		skb->mac_len);  	skb_reset_mac_header(skb); +	skb_set_network_header(skb, skb->mac_len); -	new_mpls_lse = (__be32 *)skb_mpls_header(skb); -	*new_mpls_lse = mpls->mpls_lse; +	new_mpls_lse = mpls_hdr(skb); +	new_mpls_lse->label_stack_entry = mpls->mpls_lse;  	skb_postpush_rcsum(skb, new_mpls_lse, MPLS_HLEN);  	update_ethertype(skb, eth_hdr(skb), mpls->mpls_ethertype); -	if (!skb->inner_protocol) -		skb_set_inner_protocol(skb, skb->protocol);  	skb->protocol = mpls->mpls_ethertype;  	invalidate_flow_key(key); @@ -191,18 +202,19 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,  	if (unlikely(err))  		return err; -	skb_postpull_rcsum(skb, skb_mpls_header(skb), MPLS_HLEN); +	skb_postpull_rcsum(skb, mpls_hdr(skb), MPLS_HLEN);  	memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),  		skb->mac_len);  	__skb_pull(skb, MPLS_HLEN);  	skb_reset_mac_header(skb); +	skb_set_network_header(skb, skb->mac_len); -	/* skb_mpls_header() is used to locate the ethertype -	 * field correctly in the presence of VLAN tags. +	/* mpls_hdr() is used to locate the ethertype field correctly in the +	 * presence of VLAN tags.  	 */ -	hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN); +	hdr = (struct ethhdr *)((void *)mpls_hdr(skb) - ETH_HLEN);  	update_ethertype(skb, hdr, ethertype);  	if (eth_p_mpls(skb->protocol))  		skb->protocol = ethertype; @@ -214,7 +226,7 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,  static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key,  		    const __be32 *mpls_lse, const __be32 *mask)  { -	__be32 *stack; +	struct mpls_shim_hdr *stack;  	__be32 lse;  	int err; @@ -222,16 +234,16 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key,  	if (unlikely(err))  		return err; -	stack = (__be32 *)skb_mpls_header(skb); -	lse = OVS_MASKED(*stack, *mpls_lse, *mask); +	stack = mpls_hdr(skb); +	lse = OVS_MASKED(stack->label_stack_entry, *mpls_lse, *mask);  	if (skb->ip_summed == CHECKSUM_COMPLETE) { -		__be32 diff[] = { ~(*stack), lse }; +		__be32 diff[] = { ~(stack->label_stack_entry), lse };  		skb->csum = ~csum_partial((char *)diff, sizeof(diff),  					  ~skb->csum);  	} -	*stack = lse; +	stack->label_stack_entry = lse;  	flow_key->mpls.top_lse = lse;  	return 0;  } @@ -241,20 +253,24 @@ static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)  	int err;  	err = skb_vlan_pop(skb); -	if (skb_vlan_tag_present(skb)) +	if (skb_vlan_tag_present(skb)) {  		invalidate_flow_key(key); -	else -		key->eth.tci = 0; +	} else { +		key->eth.vlan.tci = 0; +		key->eth.vlan.tpid = 0; +	}  	return err;  }  static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,  		     const struct ovs_action_push_vlan *vlan)  { -	if (skb_vlan_tag_present(skb)) +	if (skb_vlan_tag_present(skb)) {  		invalidate_flow_key(key); -	else -		key->eth.tci = vlan->vlan_tci; +	} else { +		key->eth.vlan.tci = vlan->vlan_tci; +		key->eth.vlan.tpid = vlan->vlan_tpid; +	}  	return skb_vlan_push(skb, vlan->vlan_tpid,  			     ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);  } @@ -1011,6 +1027,7 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb,  			  const struct nlattr *a, int rem)  {  	struct deferred_action *da; +	int level;  	if (!is_flow_key_valid(key)) {  		int err; @@ -1034,6 +1051,18 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb,  			return 0;  	} +	level = this_cpu_read(exec_actions_level); +	if (level <= OVS_DEFERRED_ACTION_THRESHOLD) { +		struct recirc_keys *rks = this_cpu_ptr(recirc_keys); +		struct sw_flow_key *recirc_key = &rks->key[level - 1]; + +		*recirc_key = *key; +		recirc_key->recirc_id = nla_get_u32(a); +		ovs_dp_process_packet(skb, recirc_key); + +		return 0; +	} +  	da = add_deferred_actions(skb, key, NULL);  	if (da) {  		da->pkt_key.recirc_id = nla_get_u32(a); @@ -1200,11 +1229,10 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,  			const struct sw_flow_actions *acts,  			struct sw_flow_key *key)  { -	static const int ovs_recursion_limit = 5;  	int err, level;  	level = __this_cpu_inc_return(exec_actions_level); -	if (unlikely(level > ovs_recursion_limit)) { +	if (unlikely(level > OVS_RECURSION_LIMIT)) {  		net_crit_ratelimited("ovs: recursion limit reached on datapath %s, probable configuration error\n",  				     ovs_dp_name(dp));  		kfree_skb(skb); @@ -1229,10 +1257,17 @@ int action_fifos_init(void)  	if (!action_fifos)  		return -ENOMEM; +	recirc_keys = alloc_percpu(struct recirc_keys); +	if (!recirc_keys) { +		free_percpu(action_fifos); +		return -ENOMEM; +	} +  	return 0;  }  void action_fifos_exit(void)  {  	free_percpu(action_fifos); +	free_percpu(recirc_keys);  }  |