datapath: handle recirculation loop detection
authorAndy Zhou <azhou@nicira.com>
Wed, 30 Apr 2014 23:13:27 +0000 (16:13 -0700)
committerAndy Zhou <azhou@nicira.com>
Thu, 1 May 2014 22:00:23 +0000 (15:00 -0700)
Current datapath limits the number of times same packet can loop
through action execution to avoid blowing out the kernel stack.
Recirculation also adds to action execution count, but does not use
the same amount of stack compare to other services, such as IPsec.

This patch introduces the concept of stack cost. Recirculation has a
stack cost of 1 while other services have stack cost of 4. Datapath
packet process can accommodate packets that need both services and
recirculation as long as the total stack cost does not exceed the max
stack cost. Packets exceed the limit will be treated as looped packets
and dropped.

The max stack cost is set to allow up to 4 regular services, plus up
to 3 recirculation. The behavior of packets do not recirculate does
not change.

Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
datapath/actions.c
datapath/datapath.c
datapath/datapath.h

index 5871d82..f71f1f6 100644 (file)
@@ -536,7 +536,7 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb,
        recirc_key.ovs_flow_hash = hash;
        recirc_key.recirc_id = nla_get_u32(a);
 
-       ovs_dp_process_packet_with_key(skb, &recirc_key);
+       ovs_dp_process_packet_with_key(skb, &recirc_key, true);
 
        return 0;
 }
@@ -631,11 +631,18 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 }
 
 /* We limit the number of times that we pass into execute_actions()
- * to avoid blowing out the stack in the event that we have a loop. */
-#define MAX_LOOPS 4
+ * to avoid blowing out the stack in the event that we have a loop.
+ *
+ * Each loop adds some (estimated) cost to the kernel stack.
+ * The loop terminates when the max cost is exceeded.
+ * */
+#define RECIRC_STACK_COST 1
+#define DEFAULT_STACK_COST 4
+/* Allow up to 4 regular services, and up to 3 recirculations */
+#define MAX_STACK_COST (DEFAULT_STACK_COST * 4 + RECIRC_STACK_COST * 3)
 
 struct loop_counter {
-       u8 count;               /* Count. */
+       u8 stack_cost;          /* loop stack cost. */
        bool looping;           /* Loop detected? */
 };
 
@@ -644,22 +651,24 @@ static DEFINE_PER_CPU(struct loop_counter, loop_counters);
 static int loop_suppress(struct datapath *dp, struct sw_flow_actions *actions)
 {
        if (net_ratelimit())
-               pr_warn("%s: flow looped %d times, dropping\n",
-                               ovs_dp_name(dp), MAX_LOOPS);
+               pr_warn("%s: flow loop detected, dropping\n",
+                               ovs_dp_name(dp));
        actions->actions_len = 0;
        return -ELOOP;
 }
 
 /* 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, bool recirc)
 {
        struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts);
+       const u8 stack_cost = recirc ? RECIRC_STACK_COST : DEFAULT_STACK_COST;
        struct loop_counter *loop;
        int error;
 
        /* Check whether we've looped too much. */
        loop = &__get_cpu_var(loop_counters);
-       if (unlikely(++loop->count > MAX_LOOPS))
+       loop->stack_cost += stack_cost;
+       if (unlikely(loop->stack_cost > MAX_STACK_COST))
                loop->looping = true;
        if (unlikely(loop->looping)) {
                error = loop_suppress(dp, acts);
@@ -676,8 +685,9 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb)
                error = loop_suppress(dp, acts);
 
 out_loop:
-       /* Decrement loop counter. */
-       if (!--loop->count)
+       /* Decrement loop stack cost. */
+       loop->stack_cost -= stack_cost;
+       if (!loop->stack_cost)
                loop->looping = false;
 
        return error;
index 10706f5..0611dad 100644 (file)
@@ -241,7 +241,8 @@ void ovs_dp_detach_port(struct vport *p)
 }
 
 void ovs_dp_process_packet_with_key(struct sk_buff *skb,
-               struct sw_flow_key *pkt_key)
+                                   struct sw_flow_key *pkt_key,
+                                   bool recirc)
 {
        const struct vport *p = OVS_CB(skb)->input_vport;
        struct datapath *dp = p->dp;
@@ -272,7 +273,7 @@ void ovs_dp_process_packet_with_key(struct sk_buff *skb,
        OVS_CB(skb)->flow = flow;
 
        ovs_flow_stats_update(OVS_CB(skb)->flow, pkt_key->tp.flags, skb);
-       ovs_execute_actions(dp, skb);
+       ovs_execute_actions(dp, skb, recirc);
        stats_counter = &stats->n_hit;
 
 out:
@@ -298,7 +299,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
                return;
        }
 
-       ovs_dp_process_packet_with_key(skb, &key);
+       ovs_dp_process_packet_with_key(skb, &key, false);
 }
 
 int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
@@ -601,7 +602,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        OVS_CB(packet)->input_vport = input_vport;
 
        local_bh_disable();
-       err = ovs_execute_actions(dp, packet);
+       err = ovs_execute_actions(dp, packet, false);
        local_bh_enable();
        rcu_read_unlock();
 
index 6ddf72e..a847bd9 100644 (file)
@@ -191,7 +191,7 @@ extern struct genl_multicast_group ovs_dp_vport_multicast_group;
 
 void ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
 void ovs_dp_process_packet_with_key(struct sk_buff *,
-                                   struct sw_flow_key *pkt_key);
+                                   struct sw_flow_key *pkt_key, bool recirc);
 void ovs_dp_detach_port(struct vport *);
 int ovs_dp_upcall(struct datapath *, struct sk_buff *,
                  const struct dp_upcall_info *);
@@ -200,7 +200,7 @@ 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);
+int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, bool recirc);
 void ovs_dp_notify_wq(struct work_struct *work);
 
 #define OVS_NLERR(fmt, ...)                                    \