Skip to content

Commit

Permalink
openvswitch: Add recirc and hash action.
Browse files Browse the repository at this point in the history
Recirc action allows a packet to reenter openvswitch processing.
currently openvswitch lookup flow for packet received and execute
set of actions on that packet, with help of recirc action we can
process/modify the packet and recirculate it back in openvswitch
for another pass.

OVS hash action calculates 5-tupple hash and set hash in flow-key
hash. This can be used along with recirculation for distributing
packets among different ports for bond devices.
For example:
OVS bonding can use following actions:
Match on: bond flow; Action: hash, recirc(id)
Match on: recirc-id == id and hash lower bits == a;
          Action: output port_bond_a

Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
  • Loading branch information
Andy Zhou authored and Pravin B Shelar committed Sep 16, 2014
1 parent 32ae87f commit 971427f
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 12 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
203 changes: 197 additions & 6 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,12 +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 @@ -485,8 +551,29 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
/* Skip the sample action when out of memory. */
return 0;

/* do_execute_actions() will consume the cloned skb. */
return do_execute_actions(dp, skb, key, a, rem);
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;
}

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 Down Expand Up @@ -535,6 +622,44 @@ 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,
Expand Down Expand Up @@ -566,6 +691,10 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
output_userspace(dp, skb, key, a);
break;

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

case OVS_ACTION_ATTR_PUSH_VLAN:
err = push_vlan(skb, nla_data(a));
if (unlikely(err)) /* skb already freed. */
Expand All @@ -576,6 +705,17 @@ 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;
Expand All @@ -601,12 +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,
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);

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 do_execute_actions(dp, skb, key,
acts->actions, acts->actions_len);
return 0;
}

void action_fifos_exit(void)
{
free_percpu(action_fifos);
}
11 changes: 9 additions & 2 deletions net/openvswitch/datapath.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex)
}

/* Must be called with rcu_read_lock or ovs_mutex. */
static const char *ovs_dp_name(const struct datapath *dp)
const char *ovs_dp_name(const struct datapath *dp)
{
struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL);
return vport->ops->get_name(vport);
Expand Down Expand Up @@ -2065,10 +2065,14 @@ static int __init dp_init(void)

pr_info("Open vSwitch switching datapath\n");

err = ovs_internal_dev_rtnl_link_register();
err = action_fifos_init();
if (err)
goto error;

err = ovs_internal_dev_rtnl_link_register();
if (err)
goto error_action_fifos_exit;

err = ovs_flow_init();
if (err)
goto error_unreg_rtnl_link;
Expand Down Expand Up @@ -2101,6 +2105,8 @@ static int __init dp_init(void)
ovs_flow_exit();
error_unreg_rtnl_link:
ovs_internal_dev_rtnl_link_unregister();
error_action_fifos_exit:
action_fifos_exit();
error:
return err;
}
Expand All @@ -2114,6 +2120,7 @@ static void dp_cleanup(void)
ovs_vport_exit();
ovs_flow_exit();
ovs_internal_dev_rtnl_link_unregister();
action_fifos_exit();
}

module_init(dp_init);
Expand Down
7 changes: 6 additions & 1 deletion net/openvswitch/datapath.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007-2012 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 @@ -189,13 +189,18 @@ void ovs_dp_detach_port(struct vport *);
int ovs_dp_upcall(struct datapath *, struct sk_buff *,
const struct dp_upcall_info *);

const char *ovs_dp_name(const struct datapath *dp);
struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
u8 cmd);

int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *);

void ovs_dp_notify_wq(struct work_struct *work);

int action_fifos_init(void);
void action_fifos_exit(void);

#define OVS_NLERR(fmt, ...) \
do { \
if (net_ratelimit()) \
Expand Down
7 changes: 6 additions & 1 deletion net/openvswitch/flow.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 @@ -606,6 +606,11 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
return 0;
}

int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key)
{
return key_extract(skb, key);
}

int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key,
struct sk_buff *skb, struct sw_flow_key *key)
{
Expand Down
Loading

0 comments on commit 971427f

Please sign in to comment.