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>
recirc_key.ovs_flow_hash = hash;
recirc_key.recirc_id = nla_get_u32(a);
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);
}
/* We limit the number of times that we pass into execute_actions()
}
/* 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)
+ u8 stack_cost; /* loop stack cost. */
bool looping; /* Loop detected? */
};
bool looping; /* Loop detected? */
};
static int loop_suppress(struct datapath *dp, struct sw_flow_actions *actions)
{
if (net_ratelimit())
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'. */
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);
{
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);
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);
loop->looping = true;
if (unlikely(loop->looping)) {
error = loop_suppress(dp, acts);
error = loop_suppress(dp, acts);
out_loop:
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;
loop->looping = false;
return error;
}
void ovs_dp_process_packet_with_key(struct sk_buff *skb,
}
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;
{
const struct vport *p = OVS_CB(skb)->input_vport;
struct datapath *dp = p->dp;
OVS_CB(skb)->flow = flow;
ovs_flow_stats_update(OVS_CB(skb)->flow, pkt_key->tp.flags, 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:
stats_counter = &stats->n_hit;
out:
- 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,
}
int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
OVS_CB(packet)->input_vport = input_vport;
local_bh_disable();
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();
local_bh_enable();
rcu_read_unlock();
void ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
void ovs_dp_process_packet_with_key(struct sk_buff *,
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 *);
void ovs_dp_detach_port(struct vport *);
int ovs_dp_upcall(struct datapath *, struct sk_buff *,
const struct dp_upcall_info *);
struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 portid, u32 seq,
u8 cmd);
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, ...) \
void ovs_dp_notify_wq(struct work_struct *work);
#define OVS_NLERR(fmt, ...) \