|
@@ -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
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
@@ -35,11 +35,78 @@
|
|
#include <net/sctp/checksum.h>
|
|
#include <net/sctp/checksum.h>
|
|
|
|
|
|
#include "datapath.h"
|
|
#include "datapath.h"
|
|
|
|
+#include "flow.h"
|
|
#include "vport.h"
|
|
#include "vport.h"
|
|
|
|
|
|
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|
|
|
+ struct sw_flow_key *key,
|
|
const struct nlattr *attr, int len);
|
|
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)
|
|
static int make_writable(struct sk_buff *skb, int write_len)
|
|
{
|
|
{
|
|
if (!pskb_may_pull(skb, write_len))
|
|
if (!pskb_may_pull(skb, write_len))
|
|
@@ -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,
|
|
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;
|
|
struct dp_upcall_info upcall;
|
|
const struct nlattr *a;
|
|
const struct nlattr *a;
|
|
int rem;
|
|
int rem;
|
|
|
|
|
|
- BUG_ON(!OVS_CB(skb)->pkt_key);
|
|
|
|
-
|
|
|
|
upcall.cmd = OVS_PACKET_CMD_ACTION;
|
|
upcall.cmd = OVS_PACKET_CMD_ACTION;
|
|
- upcall.key = OVS_CB(skb)->pkt_key;
|
|
|
|
|
|
+ upcall.key = key;
|
|
upcall.userdata = NULL;
|
|
upcall.userdata = NULL;
|
|
upcall.portid = 0;
|
|
upcall.portid = 0;
|
|
|
|
|
|
@@ -445,11 +510,10 @@ static bool last_action(const struct nlattr *a, int rem)
|
|
}
|
|
}
|
|
|
|
|
|
static int sample(struct datapath *dp, struct sk_buff *skb,
|
|
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 *acts_list = NULL;
|
|
const struct nlattr *a;
|
|
const struct nlattr *a;
|
|
- struct sk_buff *sample_skb;
|
|
|
|
int rem;
|
|
int rem;
|
|
|
|
|
|
for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
|
|
for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
|
|
@@ -469,31 +533,47 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
|
|
rem = nla_len(acts_list);
|
|
rem = nla_len(acts_list);
|
|
a = nla_data(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,
|
|
static int execute_set_action(struct sk_buff *skb,
|
|
@@ -511,7 +591,7 @@ static int execute_set_action(struct sk_buff *skb,
|
|
break;
|
|
break;
|
|
|
|
|
|
case OVS_KEY_ATTR_IPV4_TUNNEL:
|
|
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;
|
|
break;
|
|
|
|
|
|
case OVS_KEY_ATTR_ETHERNET:
|
|
case OVS_KEY_ATTR_ETHERNET:
|
|
@@ -542,8 +622,47 @@ static int execute_set_action(struct sk_buff *skb,
|
|
return err;
|
|
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'. */
|
|
/* Execute a list of actions against 'skb'. */
|
|
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|
|
|
+ struct sw_flow_key *key,
|
|
const struct nlattr *attr, int len)
|
|
const struct nlattr *attr, int len)
|
|
{
|
|
{
|
|
/* Every output action needs a separate clone of 'skb', but the common
|
|
/* Every output action needs a separate clone of 'skb', but the common
|
|
@@ -569,7 +688,11 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|
break;
|
|
break;
|
|
|
|
|
|
case OVS_ACTION_ATTR_USERSPACE:
|
|
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;
|
|
break;
|
|
|
|
|
|
case OVS_ACTION_ATTR_PUSH_VLAN:
|
|
case OVS_ACTION_ATTR_PUSH_VLAN:
|
|
@@ -582,12 +705,23 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|
err = pop_vlan(skb);
|
|
err = pop_vlan(skb);
|
|
break;
|
|
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:
|
|
case OVS_ACTION_ATTR_SET:
|
|
err = execute_set_action(skb, nla_data(a));
|
|
err = execute_set_action(skb, nla_data(a));
|
|
break;
|
|
break;
|
|
|
|
|
|
case OVS_ACTION_ATTR_SAMPLE:
|
|
case OVS_ACTION_ATTR_SAMPLE:
|
|
- err = sample(dp, skb, a);
|
|
|
|
|
|
+ err = sample(dp, skb, key, a);
|
|
if (unlikely(err)) /* skb already freed. */
|
|
if (unlikely(err)) /* skb already freed. */
|
|
return err;
|
|
return err;
|
|
break;
|
|
break;
|
|
@@ -607,11 +741,63 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|
return 0;
|
|
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'. */
|
|
/* 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);
|
|
}
|
|
}
|