Skip to content

Commit

Permalink
Merge branch 'net_next_ovs' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/pshelar/openvswitch

Pravin B Shelar says:

====================
Open vSwitch

Following patches adds recirculation and hash action to OVS.
First patch removes pointer to stack object. Next three patches
does code restructuring which is required for last patch.
Recirculation implementation is changed, according to comments from
David Miller, to avoid using recursive calls in OVS. It is using
queue to record recirc action and deferred recirc is executed at
the end of current actions execution.

v1-v2:
Changed subsystem name in subject to openvswitch
v2-v3:
Added patch to remove pkt_key pointer from skb->cb.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 16, 2014
2 parents e1f93eb + 971427f commit 2d9d65f
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 126 deletions.
26 changes: 26 additions & 0 deletions include/uapi/linux/openvswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ enum ovs_key_attr {
OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */
OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */
OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */
OVS_KEY_ATTR_DP_HASH, /* u32 hash value. Value 0 indicates the hash
is not computed by the datapath. */
OVS_KEY_ATTR_RECIRC_ID, /* u32 recirc id */

#ifdef __KERNEL__
OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */
Expand Down Expand Up @@ -493,6 +496,27 @@ struct ovs_action_push_vlan {
__be16 vlan_tci; /* 802.1Q TCI (VLAN ID and priority). */
};

/* Data path hash algorithm for computing Datapath hash.
*
* The algorithm type only specifies the fields in a flow
* will be used as part of the hash. Each datapath is free
* to use its own hash algorithm. The hash value will be
* opaque to the user space daemon.
*/
enum ovs_hash_alg {
OVS_HASH_ALG_L4,
};

/*
* struct ovs_action_hash - %OVS_ACTION_ATTR_HASH action argument.
* @hash_alg: Algorithm used to compute hash prior to recirculation.
* @hash_basis: basis used for computing hash.
*/
struct ovs_action_hash {
uint32_t hash_alg; /* One of ovs_hash_alg. */
uint32_t hash_basis;
};

/**
* enum ovs_action_attr - Action types.
*
Expand Down Expand Up @@ -521,6 +545,8 @@ enum ovs_action_attr {
OVS_ACTION_ATTR_PUSH_VLAN, /* struct ovs_action_push_vlan. */
OVS_ACTION_ATTR_POP_VLAN, /* No argument. */
OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */
OVS_ACTION_ATTR_RECIRC, /* u32 recirc_id. */
OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */
__OVS_ACTION_ATTR_MAX
};

Expand Down
258 changes: 222 additions & 36 deletions net/openvswitch/actions.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007-2013 Nicira, Inc.
* Copyright (c) 2007-2014 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
Expand Down Expand Up @@ -35,11 +35,78 @@
#include <net/sctp/checksum.h>

#include "datapath.h"
#include "flow.h"
#include "vport.h"

static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key,
const struct nlattr *attr, int len);

struct deferred_action {
struct sk_buff *skb;
const struct nlattr *actions;

/* Store pkt_key clone when creating deferred action. */
struct sw_flow_key pkt_key;
};

#define DEFERRED_ACTION_FIFO_SIZE 10
struct action_fifo {
int head;
int tail;
/* Deferred action fifo queue storage. */
struct deferred_action fifo[DEFERRED_ACTION_FIFO_SIZE];
};

static struct action_fifo __percpu *action_fifos;
static DEFINE_PER_CPU(int, exec_actions_level);

static void action_fifo_init(struct action_fifo *fifo)
{
fifo->head = 0;
fifo->tail = 0;
}

static bool action_fifo_is_empty(struct action_fifo *fifo)
{
return (fifo->head == fifo->tail);
}

static struct deferred_action *action_fifo_get(struct action_fifo *fifo)
{
if (action_fifo_is_empty(fifo))
return NULL;

return &fifo->fifo[fifo->tail++];
}

static struct deferred_action *action_fifo_put(struct action_fifo *fifo)
{
if (fifo->head >= DEFERRED_ACTION_FIFO_SIZE - 1)
return NULL;

return &fifo->fifo[fifo->head++];
}

/* Return true if fifo is not full */
static struct deferred_action *add_deferred_actions(struct sk_buff *skb,
struct sw_flow_key *key,
const struct nlattr *attr)
{
struct action_fifo *fifo;
struct deferred_action *da;

fifo = this_cpu_ptr(action_fifos);
da = action_fifo_put(fifo);
if (da) {
da->skb = skb;
da->actions = attr;
da->pkt_key = *key;
}

return da;
}

static int make_writable(struct sk_buff *skb, int write_len)
{
if (!pskb_may_pull(skb, write_len))
Expand Down Expand Up @@ -410,16 +477,14 @@ static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
}

static int output_userspace(struct datapath *dp, struct sk_buff *skb,
const struct nlattr *attr)
struct sw_flow_key *key, const struct nlattr *attr)
{
struct dp_upcall_info upcall;
const struct nlattr *a;
int rem;

BUG_ON(!OVS_CB(skb)->pkt_key);

upcall.cmd = OVS_PACKET_CMD_ACTION;
upcall.key = OVS_CB(skb)->pkt_key;
upcall.key = key;
upcall.userdata = NULL;
upcall.portid = 0;

Expand All @@ -445,11 +510,10 @@ static bool last_action(const struct nlattr *a, int rem)
}

static int sample(struct datapath *dp, struct sk_buff *skb,
const struct nlattr *attr)
struct sw_flow_key *key, const struct nlattr *attr)
{
const struct nlattr *acts_list = NULL;
const struct nlattr *a;
struct sk_buff *sample_skb;
int rem;

for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
Expand All @@ -469,31 +533,47 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
rem = nla_len(acts_list);
a = nla_data(acts_list);

/* Actions list is either empty or only contains a single user-space
* action, the latter being a special case as it is the only known
* usage of the sample action.
* In these special cases don't clone the skb as there are no
* side-effects in the nested actions.
* Otherwise, clone in case the nested actions have side effects.
/* Actions list is empty, do nothing */
if (unlikely(!rem))
return 0;

/* The only known usage of sample action is having a single user-space
* action. Treat this usage as a special case.
* The output_userspace() should clone the skb to be sent to the
* user space. This skb will be consumed by its caller.
*/
if (likely(rem == 0 || (nla_type(a) == OVS_ACTION_ATTR_USERSPACE &&
last_action(a, rem)))) {
sample_skb = skb;
skb_get(skb);
} else {
sample_skb = skb_clone(skb, GFP_ATOMIC);
if (!sample_skb) /* Skip sample action when out of memory. */
return 0;
if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE &&
last_action(a, rem)))
return output_userspace(dp, skb, key, a);

skb = skb_clone(skb, GFP_ATOMIC);
if (!skb)
/* Skip the sample action when out of memory. */
return 0;

if (!add_deferred_actions(skb, key, a)) {
if (net_ratelimit())
pr_warn("%s: deferred actions limit reached, dropping sample action\n",
ovs_dp_name(dp));

kfree_skb(skb);
}
return 0;
}

/* Note that do_execute_actions() never consumes skb.
* In the case where skb has been cloned above it is the clone that
* is consumed. Otherwise the skb_get(skb) call prevents
* consumption by do_execute_actions(). Thus, it is safe to simply
* return the error code and let the caller (also
* do_execute_actions()) free skb on error.
*/
return do_execute_actions(dp, sample_skb, a, rem);
static void execute_hash(struct sk_buff *skb, struct sw_flow_key *key,
const struct nlattr *attr)
{
struct ovs_action_hash *hash_act = nla_data(attr);
u32 hash = 0;

/* OVS_HASH_ALG_L4 is the only possible hash algorithm. */
hash = skb_get_hash(skb);
hash = jhash_1word(hash, hash_act->hash_basis);
if (!hash)
hash = 0x1;

key->ovs_flow_hash = hash;
}

static int execute_set_action(struct sk_buff *skb,
Expand All @@ -511,7 +591,7 @@ static int execute_set_action(struct sk_buff *skb,
break;

case OVS_KEY_ATTR_IPV4_TUNNEL:
OVS_CB(skb)->tun_key = nla_data(nested_attr);
OVS_CB(skb)->egress_tun_key = nla_data(nested_attr);
break;

case OVS_KEY_ATTR_ETHERNET:
Expand Down Expand Up @@ -542,8 +622,47 @@ static int execute_set_action(struct sk_buff *skb,
return err;
}

static int execute_recirc(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key,
const struct nlattr *a, int rem)
{
struct deferred_action *da;
int err;

err = ovs_flow_key_update(skb, key);
if (err)
return err;

if (!last_action(a, rem)) {
/* Recirc action is the not the last action
* of the action list, need to clone the skb.
*/
skb = skb_clone(skb, GFP_ATOMIC);

/* Skip the recirc action when out of memory, but
* continue on with the rest of the action list.
*/
if (!skb)
return 0;
}

da = add_deferred_actions(skb, key, NULL);
if (da) {
da->pkt_key.recirc_id = nla_get_u32(a);
} else {
kfree_skb(skb);

if (net_ratelimit())
pr_warn("%s: deferred action limit reached, drop recirc action\n",
ovs_dp_name(dp));
}

return 0;
}

/* Execute a list of actions against 'skb'. */
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key,
const struct nlattr *attr, int len)
{
/* Every output action needs a separate clone of 'skb', but the common
Expand All @@ -569,7 +688,11 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
break;

case OVS_ACTION_ATTR_USERSPACE:
output_userspace(dp, skb, a);
output_userspace(dp, skb, key, a);
break;

case OVS_ACTION_ATTR_HASH:
execute_hash(skb, key, a);
break;

case OVS_ACTION_ATTR_PUSH_VLAN:
Expand All @@ -582,12 +705,23 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
err = pop_vlan(skb);
break;

case OVS_ACTION_ATTR_RECIRC:
err = execute_recirc(dp, skb, key, a, rem);
if (last_action(a, rem)) {
/* If this is the last action, the skb has
* been consumed or freed.
* Return immediately.
*/
return err;
}
break;

case OVS_ACTION_ATTR_SET:
err = execute_set_action(skb, nla_data(a));
break;

case OVS_ACTION_ATTR_SAMPLE:
err = sample(dp, skb, a);
err = sample(dp, skb, key, a);
if (unlikely(err)) /* skb already freed. */
return err;
break;
Expand All @@ -607,11 +741,63 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
return 0;
}

static void process_deferred_actions(struct datapath *dp)
{
struct action_fifo *fifo = this_cpu_ptr(action_fifos);

/* Do not touch the FIFO in case there is no deferred actions. */
if (action_fifo_is_empty(fifo))
return;

/* Finishing executing all deferred actions. */
do {
struct deferred_action *da = action_fifo_get(fifo);
struct sk_buff *skb = da->skb;
struct sw_flow_key *key = &da->pkt_key;
const struct nlattr *actions = da->actions;

if (actions)
do_execute_actions(dp, skb, key, actions,
nla_len(actions));
else
ovs_dp_process_packet(skb, key);
} while (!action_fifo_is_empty(fifo));

/* Reset FIFO for the next packet. */
action_fifo_init(fifo);
}

/* Execute a list of actions against 'skb'. */
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb)
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key)
{
struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts);
int level = this_cpu_read(exec_actions_level);
struct sw_flow_actions *acts;
int err;

acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts);

this_cpu_inc(exec_actions_level);
err = do_execute_actions(dp, skb, key,
acts->actions, acts->actions_len);

OVS_CB(skb)->tun_key = NULL;
return do_execute_actions(dp, skb, acts->actions, acts->actions_len);
if (!level)
process_deferred_actions(dp);

this_cpu_dec(exec_actions_level);
return err;
}

int action_fifos_init(void)
{
action_fifos = alloc_percpu(struct action_fifo);
if (!action_fifos)
return -ENOMEM;

return 0;
}

void action_fifos_exit(void)
{
free_percpu(action_fifos);
}
Loading

0 comments on commit 2d9d65f

Please sign in to comment.