COVERAGE_DEFINE(xlate_actions);
COVERAGE_DEFINE(xlate_actions_oversize);
+COVERAGE_DEFINE(xlate_actions_too_many_output);
COVERAGE_DEFINE(xlate_actions_mpls_overflow);
VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
} bond;
struct {
struct ofproto_dpif *ofproto;
- struct rule_dpif *rule;
+ struct ofputil_flow_mod *fm;
+ struct ofpbuf *ofpacts;
} learn;
struct {
struct ofproto_dpif *ofproto;
}
static bool
-xport_stp_listen_state(const struct xport *xport)
+xport_stp_should_forward_bpdu(const struct xport *xport)
{
struct stp_port *sp = xport_get_stp_port(xport);
- return stp_listen_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED);
+ return stp_should_forward_bpdu(sp ? stp_port_get_state(sp) : STP_DISABLED);
}
/* Returns true if STP should process 'flow'. Sets fields in 'wc' that
/* Save enough info to update mac learning table later. */
entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NORMAL);
- entry->u.normal.ofproto = ctx->xin->ofproto;
+ entry->u.normal.ofproto = ctx->xbridge->ofproto;
entry->u.normal.flow = xmemdup(flow, sizeof *flow);
entry->u.normal.vlan = vlan;
}
bfd_process_packet(xport->bfd, flow, packet);
/* If POLL received, immediately sends FINAL back. */
if (bfd_should_send_packet(xport->bfd)) {
- if (xport->peer) {
- ofproto_dpif_monitor_port_send_soon(xport->ofport);
- } else {
- ofproto_dpif_monitor_port_send_soon_safe(xport->ofport);
- }
+ ofproto_dpif_monitor_port_send_soon(xport->ofport);
}
}
return SLOW_BFD;
return;
} else if (check_stp) {
if (is_stp(&ctx->base_flow)) {
- if (!xport_stp_listen_state(xport)) {
+ if (!xport_stp_should_forward_bpdu(xport)) {
xlate_report(ctx, "STP not in listening state, "
"skipping bpdu output");
return;
}
static void
-xlate_learn_action(struct xlate_ctx *ctx,
- const struct ofpact_learn *learn)
+xlate_learn_action__(struct xlate_ctx *ctx, const struct ofpact_learn *learn,
+ struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
{
- uint64_t ofpacts_stub[1024 / 8];
- struct ofputil_flow_mod fm;
- struct ofpbuf ofpacts;
+ learn_execute(learn, &ctx->xin->flow, fm, ofpacts);
+ if (ctx->xin->may_learn) {
+ ofproto_dpif_flow_mod(ctx->xbridge->ofproto, fm);
+ }
+}
+static void
+xlate_learn_action(struct xlate_ctx *ctx, const struct ofpact_learn *learn)
+{
ctx->xout->has_learn = true;
-
learn_mask(learn, &ctx->xout->wc);
- if (!ctx->xin->may_learn) {
- return;
- }
-
- ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
- learn_execute(learn, &ctx->xin->flow, &fm, &ofpacts);
- ofproto_dpif_flow_mod(ctx->xbridge->ofproto, &fm);
- ofpbuf_uninit(&ofpacts);
-
if (ctx->xin->xcache) {
struct xc_entry *entry;
entry = xlate_cache_add_entry(ctx->xin->xcache, XC_LEARN);
- entry->u.learn.ofproto = ctx->xin->ofproto;
- /* Lookup the learned rule, taking a reference on it. The reference
- * is released when this cache entry is deleted. */
- rule_dpif_lookup(ctx->xbridge->ofproto, &ctx->xin->flow, NULL,
- &entry->u.learn.rule, true);
+ entry->u.learn.ofproto = ctx->xbridge->ofproto;
+ entry->u.learn.fm = xmalloc(sizeof *entry->u.learn.fm);
+ entry->u.learn.ofpacts = ofpbuf_new(64);
+ xlate_learn_action__(ctx, learn, entry->u.learn.fm,
+ entry->u.learn.ofpacts);
+ } else if (ctx->xin->may_learn) {
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofputil_flow_mod fm;
+ struct ofpbuf ofpacts;
+
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ xlate_learn_action__(ctx, learn, &fm, &ofpacts);
+ ofpbuf_uninit(&ofpacts);
}
}
ovs_rwlock_unlock(&xlate_rwlock);
}
+/* Returns the maximum number of packets that the Linux kernel is willing to
+ * queue up internally to certain kinds of software-implemented ports, or the
+ * default (and rarely modified) value if it cannot be determined. */
+static int
+netdev_max_backlog(void)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ static int max_backlog = 1000; /* The normal default value. */
+
+ if (ovsthread_once_start(&once)) {
+ static const char filename[] = "/proc/sys/net/core/netdev_max_backlog";
+ FILE *stream;
+ int n;
+
+ stream = fopen(filename, "r");
+ if (!stream) {
+ VLOG_WARN("%s: open failed (%s)", filename, ovs_strerror(errno));
+ } else {
+ if (fscanf(stream, "%d", &n) != 1) {
+ VLOG_WARN("%s: read error", filename);
+ } else if (n <= 100) {
+ VLOG_WARN("%s: unexpectedly small value %d", filename, n);
+ } else {
+ max_backlog = n;
+ }
+ fclose(stream);
+ }
+ ovsthread_once_done(&once);
+
+ VLOG_DBG("%s: using %d max_backlog", filename, max_backlog);
+ }
+
+ return max_backlog;
+}
+
+/* Counts and returns the number of OVS_ACTION_ATTR_OUTPUT actions in
+ * 'odp_actions'. */
+static int
+count_output_actions(const struct ofpbuf *odp_actions)
+{
+ const struct nlattr *a;
+ size_t left;
+ int n = 0;
+
+ NL_ATTR_FOR_EACH_UNSAFE (a, left, ofpbuf_data(odp_actions),
+ ofpbuf_size(odp_actions)) {
+ if (a->nla_type == OVS_ACTION_ATTR_OUTPUT) {
+ n++;
+ }
+ }
+ return n;
+}
+
+/* Returns true if 'odp_actions' contains more output actions than the datapath
+ * can reliably handle in one go. On Linux, this is the value of the
+ * net.core.netdev_max_backlog sysctl, which limits the maximum number of
+ * packets that the kernel is willing to queue up for processing while the
+ * datapath is processing a set of actions. */
+static bool
+too_many_output_actions(const struct ofpbuf *odp_actions)
+{
+#ifdef __linux__
+ return (ofpbuf_size(odp_actions) / NL_A_U32_SIZE > netdev_max_backlog()
+ && count_output_actions(odp_actions) > netdev_max_backlog());
+#else
+ /* OSes other than Linux might have similar limits, but we don't know how
+ * to determine them.*/
+ return false;
+#endif
+}
+
/* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
* into datapath actions in 'odp_actions', using 'ctx'.
*
* prevent the flow from being installed. */
COVERAGE_INC(xlate_actions_oversize);
ctx.xout->slow |= SLOW_ACTION;
+ } else if (too_many_output_actions(&ctx.xout->odp_actions)) {
+ COVERAGE_INC(xlate_actions_too_many_output);
+ ctx.xout->slow |= SLOW_ACTION;
}
if (mbridge_has_mirrors(ctx.xbridge->mbridge)) {
}
if (ctx.xbridge->netflow) {
- const struct ofpact *ofpacts = actions->ofpacts;
- size_t ofpacts_len = actions->ofpacts_len;
-
/* Only update netflow if we don't have controller flow. We don't
* report NetFlow expiration messages for such facets because they
* are just part of the control logic for the network, not real
break;
case XC_LEARN:
if (may_learn) {
- struct rule_dpif *rule = entry->u.learn.rule;
-
- /* Reset the modified time for a rule that is equivalent to
- * the currently cached rule. If the rule is not the exact
- * rule we have cached, update the reference that we have. */
- entry->u.learn.rule = ofproto_dpif_refresh_rule(rule);
+ ofproto_dpif_flow_mod(entry->u.learn.ofproto,
+ entry->u.learn.fm);
}
break;
case XC_NORMAL:
static void
xlate_cache_clear_netflow(struct netflow *netflow, struct flow *flow)
{
- netflow_expire(netflow, flow);
netflow_flow_clear(netflow, flow);
netflow_unref(netflow);
free(flow);
mbridge_unref(entry->u.mirror.mbridge);
break;
case XC_LEARN:
- /* 'u.learn.rule' is the learned rule. */
- rule_dpif_unref(entry->u.learn.rule);
+ free(entry->u.learn.fm);
+ ofpbuf_delete(entry->u.learn.ofpacts);
break;
case XC_NORMAL:
free(entry->u.normal.flow);