On Wed, Sep 3, 2014 at 1:05 PM, Andy Zhou <az...@nicira.com> wrote: > Since kernel stack is limited in size, it is not wise to using > recursive function with large stack frames. > > This patch provides an alternative implementation of recirc action > without using recursion. > > A per CPU fixed sized, 'deferred action FIFO', is used to store either > recirc or sample actions encountered during execution of an action > list. Not executing recirc or sample action in place, but rather execute > them laster as 'deferred actions' avoids recursion. > > Deferred actions are only executed after all other actions has been > executed, including the ones triggered by loopback from the kernel > network stack. > > The size of the private FIFO, currently set to 20, limits the number > of total 'deferred actions' any one packet can accumulate. > > Signed-off-by: Andy Zhou <az...@nicira.com> > > --- > v4->v5: > Reset fifo after processing deferred actions > move private data structures from actions.h to actions.c > remove action_fifo init functions, since default percpu data > will be zero.
This looks pretty close. > --- > datapath/Modules.mk | 1 + > datapath/actions.c | 175 > ++++++++++++++++++++++++++++++++++++++++++++++++---- > datapath/actions.h | 31 ++++++++++ > datapath/datapath.c | 1 + > datapath/datapath.h | 4 +- > 5 files changed, 197 insertions(+), 15 deletions(-) > create mode 100644 datapath/actions.h > > diff --git a/datapath/Modules.mk b/datapath/Modules.mk > index 90e158c..2e74f6e 100644 > --- a/datapath/Modules.mk > +++ b/datapath/Modules.mk > @@ -23,6 +23,7 @@ openvswitch_sources = \ > > openvswitch_headers = \ > compat.h \ > + actions.h \ > datapath.h \ > flow.h \ > flow_netlink.h \ > diff --git a/datapath/actions.c b/datapath/actions.c > index 0a22e55..6ad5bbe 100644 > --- a/datapath/actions.c > +++ b/datapath/actions.c > @@ -39,6 +39,74 @@ > #include "mpls.h" > #include "vlan.h" > #include "vport.h" > +#include "actions.h" > + > +struct ovs_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 OVS_DEFERRED_ACTION_FIFO_SIZE 20 > +struct ovs_action_fifo { > + int head; > + int tail; > + /* Deferred action fifo queue storage. */ > + struct ovs_deferred_action fifo[OVS_DEFERRED_ACTION_FIFO_SIZE]; > +}; > + > +static DEFINE_PER_CPU(struct ovs_action_fifo, action_fifos); > +#define OVS_EXEC_ACTIONS_COUNT_LIMIT 4 /* limit used to detect packet > + looping by the network stack */ > +static DEFINE_PER_CPU(int, ovs_exec_actions_count); > + need better name. > +static inline void action_fifo_init(struct ovs_action_fifo *fifo) > +{ > + fifo->head = 0; > + fifo->tail = 0; > +} > + > +static inline bool action_fifo_is_empty(struct ovs_action_fifo *fifo) > +{ > + return (fifo->head == fifo->tail); > +} > + > +static inline struct ovs_deferred_action * > +action_fifo_get(struct ovs_action_fifo *fifo) > +{ > + if (action_fifo_is_empty(fifo)) > + return NULL; > + > + return &fifo->fifo[fifo->tail++]; > +} > + > +static inline struct ovs_deferred_action * > +action_fifo_put(struct ovs_action_fifo *fifo) > +{ > + if (fifo->head >= OVS_DEFERRED_ACTION_FIFO_SIZE - 1) > + return NULL; > + > + return &fifo->fifo[fifo->head++]; > +} > + > +static inline struct ovs_deferred_action * > +add_deferred_actions(struct sk_buff *skb, const struct nlattr *attr) > +{ > + struct ovs_action_fifo *fifo; > + struct ovs_deferred_action *da; > + > + fifo = this_cpu_ptr(&(action_fifos)); > + da = action_fifo_put(fifo); > + > + if (da) { > + da->skb = skb; > + da->actions = attr; > + } > + > + return da; > +} > There is no need to inline any symbols in .c file, it hides compiler warnings of unused symbols. > static void flow_key_clone(struct sk_buff *skb, struct sw_flow_key *new_key) > { > @@ -689,9 +757,9 @@ 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 sample_key; > const struct nlattr *acts_list = NULL; > const struct nlattr *a; > + struct ovs_deferred_action *da; > int rem; > > for (a = nla_data(attr), rem = nla_len(attr); rem > 0; > @@ -728,10 +796,19 @@ static int sample(struct datapath *dp, struct sk_buff > *skb, > /* Skip the sample action when out of memory. */ > return 0; > > - flow_key_clone(skb, &sample_key); > + da = add_deferred_actions(skb, a); > + if (!da) { > + if (net_ratelimit()) > + pr_warn("%s: deferred actions limit reached, dropping > sample action\n", > + ovs_dp_name(dp)); > > - /* do_execute_actions() will consume the cloned skb. */ > - return do_execute_actions(dp, skb, a, rem); > + kfree_skb(skb); > + return 0; > + } > + > + flow_key_clone(skb, &da->pkt_key); > + > + return 0; > } > > static void execute_hash(struct sk_buff *skb, const struct nlattr *attr) > @@ -750,7 +827,7 @@ static void execute_hash(struct sk_buff *skb, const > struct nlattr *attr) > } > > static int execute_set_action(struct sk_buff *skb, > - const struct nlattr *nested_attr) > + const struct nlattr *nested_attr) > { > int err = 0; > > @@ -801,11 +878,10 @@ static int execute_set_action(struct sk_buff *skb, > return err; > } > > - > static int execute_recirc(struct datapath *dp, struct sk_buff *skb, > const struct nlattr *a, int rem) > { > - struct sw_flow_key recirc_key; > + struct ovs_deferred_action *da; > > if (!is_skb_flow_key_valid(skb)) { > int err; > @@ -827,17 +903,33 @@ static int execute_recirc(struct datapath *dp, struct > sk_buff *skb, > if (!skb) > return 0; > > - flow_key_clone(skb, &recirc_key); > + /* Add the skb clone to action fifo. */ > + da = add_deferred_actions(skb, NULL); > + if (!da) { > + kfree_skb(skb); > + goto fifo_full; > + } > + > + flow_key_clone(skb, &da->pkt_key); > + } else { > + if (!add_deferred_actions(skb, NULL)) > + goto fifo_full; > } > > flow_key_set_recirc_id(skb, nla_get_u32(a)); > - ovs_dp_process_packet(skb); > + return 0; > + > +fifo_full: > + 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, > - const struct nlattr *attr, int len) > + const struct nlattr *attr, int len) > { > /* Every output action needs a separate clone of 'skb', but the common > * case is just a single output action, so that doing a clone and > @@ -924,8 +1016,67 @@ static int do_execute_actions(struct datapath *dp, > struct sk_buff *skb, > return 0; > } > > +static void ovs_process_deferred_packets(struct datapath *dp) > +{ deferred_packets is bit confusing since it same packet. can we use deferred_action for function name? > + struct ovs_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 ovs_deferred_action *da = action_fifo_get(fifo); > + struct sk_buff *skb = da->skb; > + const struct nlattr *actions = da->actions; > + > + if (actions) > + do_execute_actions(dp, skb, actions, > nla_len(actions)); > + else > + ovs_dp_process_packet(skb); > + } while (!action_fifo_is_empty(fifo)); > + > + /* Reset FIFO for the next packet. */ > + action_fifo_init(fifo); > +} In OVS kernel module "ovs_" prefix is used for non static symbol, so can you remove all ovs prefix? > + > +static bool ovs_exec_actions_limit_exceeded(struct datapath *dp, int count) > +{ > + if (count >= OVS_EXEC_ACTIONS_COUNT_LIMIT) { > + if (net_ratelimit()) > + pr_warn("%s: packet loop detected, dropping.\n", > + ovs_dp_name(dp)); > + > + return true; > + } > + unlike annotations. > + return false; > +} > + > + extra line. > /* Execute a list of actions against 'skb'. */ > -int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, struct > sw_flow_actions *acts) > +int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, > + struct sw_flow_actions *acts) > { > - return do_execute_actions(dp, skb, acts->actions, acts->actions_len); > + int count = this_cpu_read(ovs_exec_actions_count); > + int err; > + > + if (ovs_exec_actions_limit_exceeded(dp, count)) { > + kfree_skb(skb); > + return -ELOOP; > + } > + > + this_cpu_inc(ovs_exec_actions_count); > + > + err = do_execute_actions(dp, skb, acts->actions, acts->actions_len); > + > + if (!count) > + ovs_process_deferred_packets(dp); > + > + this_cpu_dec(ovs_exec_actions_count); > + > + /* This return status currently does not reflect the errors > + * encounted during deferred actions execution. Probably needs to > + * be fixed in the future. */ > + return err; > } > diff --git a/datapath/actions.h b/datapath/actions.h > new file mode 100644 > index 0000000..17749c0 > --- /dev/null > +++ b/datapath/actions.h > @@ -0,0 +1,31 @@ > +/* Copyright (c) 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 > + * License as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#ifndef ACTIONS_H > +#define ACTIONS_H 1 > + > +#include <linux/kernel.h> > +#include <linux/mutex.h> > +#include <linux/netdevice.h> > +#include <linux/skbuff.h> > + > +#include "compat.h" > +#include "flow.h" > +#include "flow_table.h" > +#include "vlan.h" > +#include "vport.h" > + > +int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, > + struct sw_flow_actions *acts); > +void ovs_process_action_fifo(void); > + I do not see this symbol used anywhere. In this case the header declares ovs_execute_actions(), which was declared in datapath.h May be we do not need new header file for single function declaration. > +#endif /* actions.h */ > diff --git a/datapath/datapath.c b/datapath/datapath.c > index a668222..564bb71 100644 > --- a/datapath/datapath.c > +++ b/datapath/datapath.c > @@ -61,6 +61,7 @@ > #include "vlan.h" > #include "vport-internal_dev.h" > #include "vport-netdev.h" > +#include "actions.h" > > int ovs_net_id __read_mostly; > > diff --git a/datapath/datapath.h b/datapath/datapath.h > index eba2fc4..dbe58d4 100644 > --- a/datapath/datapath.h > +++ b/datapath/datapath.h > @@ -31,6 +31,7 @@ > #include "flow_table.h" > #include "vlan.h" > #include "vport.h" > +#include "actions.h" > > #define DP_MAX_PORTS USHRT_MAX > #define DP_VPORT_HASH_BUCKETS 1024 > @@ -196,9 +197,6 @@ int ovs_dp_upcall(struct datapath *, struct sk_buff *, > const char *ovs_dp_name(const struct datapath *dp); > struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 portid, u32 seq, > u8 cmd); > - > -int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, > - struct sw_flow_actions *acts); > void ovs_dp_notify_wq(struct work_struct *work); > > #define OVS_NLERR(fmt, ...) \ > -- > 1.9.1 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev