diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 786494d1e85..ee8a22df9ca 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -53,6 +53,7 @@ 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); @@ -4045,6 +4046,77 @@ actions_output_to_local_port(const struct xlate_ctx *ctx) return false; } +/* 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'. * @@ -4273,6 +4345,9 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) * 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)) {