debian: Fix "make dist" by adding corekeeper.override to EXTRA_DIST.
[openvswitch] / datapath / datapath.c
index 54c9fa36bc5684c56ce4d38788720d471f80dfee..e46819876b9caf11a8748230db675bb06f3dd6ea 100644 (file)
@@ -69,8 +69,22 @@ EXPORT_SYMBOL(dp_ioctl_hook);
 static struct datapath *dps[ODP_MAX];
 static DEFINE_MUTEX(dp_mutex);
 
-/* Number of milliseconds between runs of the maintenance thread. */
-#define MAINT_SLEEP_MSECS 1000
+/* We limit the number of times that we pass into dp_process_received_packet()
+ * to avoid blowing out the stack in the event that we have a loop. */
+struct loop_counter {
+       int count;              /* Count. */
+       bool looping;           /* Loop detected? */
+};
+
+#define DP_MAX_LOOPS 5
+
+/* We use a separate counter for each CPU for both interrupt and non-interrupt
+ * context in order to keep the limit deterministic for a given packet. */
+struct percpu_loop_counters {
+       struct loop_counter counters[2];
+};
+
+static DEFINE_PER_CPU(struct percpu_loop_counters, dp_loop_counters);
 
 static int new_dp_port(struct datapath *, struct odp_port *, int port_no);
 
@@ -380,6 +394,7 @@ static int new_dp_port(struct datapath *dp, struct odp_port *odp_port, int port_
 
        p->port_no = port_no;
        p->dp = dp;
+       p->vport = vport;
        atomic_set(&p->sflow_pool, 0);
 
        err = vport_attach(vport, p);
@@ -513,6 +528,14 @@ out:
        return err;
 }
 
+static void suppress_loop(struct datapath *dp, struct sw_flow_actions *actions)
+{
+       if (net_ratelimit())
+               printk(KERN_WARNING "%s: flow looped %d times, dropping\n",
+                      dp_name(dp), DP_MAX_LOOPS);
+       actions->n_actions = 0;
+}
+
 /* Must be called with rcu_read_lock. */
 void dp_process_received_packet(struct dp_port *p, struct sk_buff *skb)
 {
@@ -521,12 +544,13 @@ void dp_process_received_packet(struct dp_port *p, struct sk_buff *skb)
        int stats_counter_off;
        struct odp_flow_key key;
        struct tbl_node *flow_node;
-
-       WARN_ON_ONCE(skb_shared(skb));
-       skb_warn_if_lro(skb);
+       struct sw_flow *flow;
+       struct sw_flow_actions *acts;
+       struct loop_counter *loop;
 
        OVS_CB(skb)->dp_port = p;
 
+       /* Extract flow from 'skb' into 'key'. */
        if (flow_extract(skb, p ? p->port_no : ODPP_NONE, &key)) {
                if (dp->drop_frags) {
                        kfree_skb(skb);
@@ -535,20 +559,44 @@ void dp_process_received_packet(struct dp_port *p, struct sk_buff *skb)
                }
        }
 
+       /* Look up flow. */
        flow_node = tbl_lookup(rcu_dereference(dp->table), &key, flow_hash(&key), flow_cmp);
-       if (flow_node) {
-               struct sw_flow *flow = flow_cast(flow_node);
-               struct sw_flow_actions *acts = rcu_dereference(flow->sf_acts);
-               flow_used(flow, skb);
-               execute_actions(dp, skb, &key, acts->actions, acts->n_actions,
-                               GFP_ATOMIC);
-               stats_counter_off = offsetof(struct dp_stats_percpu, n_hit);
-       } else {
-               stats_counter_off = offsetof(struct dp_stats_percpu, n_missed);
+       if (unlikely(!flow_node)) {
                dp_output_control(dp, skb, _ODPL_MISS_NR, OVS_CB(skb)->tun_id);
+               stats_counter_off = offsetof(struct dp_stats_percpu, n_missed);
+               goto out;
        }
 
+       flow = flow_cast(flow_node);
+       flow_used(flow, skb);
+
+       acts = rcu_dereference(flow->sf_acts);
+
+       /* Check whether we've looped too much. */
+       loop = &get_cpu_var(dp_loop_counters).counters[!!in_interrupt()];
+       if (unlikely(++loop->count > DP_MAX_LOOPS))
+               loop->looping = true;
+       if (unlikely(loop->looping)) {
+               suppress_loop(dp, acts);
+               goto out_loop;
+       }
+
+       /* Execute actions. */
+       execute_actions(dp, skb, &key, acts->actions, acts->n_actions, GFP_ATOMIC);
+       stats_counter_off = offsetof(struct dp_stats_percpu, n_hit);
+
+       /* Check whether sub-actions looped too much. */
+       if (unlikely(loop->looping))
+               suppress_loop(dp, acts);
+
+out_loop:
+       /* Decrement loop counter. */
+       if (!--loop->count)
+               loop->looping = false;
+       put_cpu_var(dp_loop_counters);
+
 out:
+       /* Update datapath statistics. */
        local_bh_disable();
        stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id());
        (*(u64 *)((u8 *)stats + stats_counter_off))++;
@@ -931,7 +979,7 @@ static void get_stats(struct sw_flow *flow, struct odp_flow_stats *stats,
 
        stats->n_packets = flow->packet_count;
        stats->n_bytes = flow->byte_count;
-       stats->ip_tos = flow->ip_tos;
+       stats->reserved = 0;
        stats->tcp_flags = flow->tcp_flags;
        stats->error = 0;
 }
@@ -940,7 +988,6 @@ static void clear_stats(struct sw_flow *flow)
 {
        flow->used = 0;
        flow->tcp_flags = 0;
-       flow->ip_tos = 0;
        flow->packet_count = 0;
        flow->byte_count = 0;
 }