aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2012-05-04 14:52:36 -0700
committerBen Pfaff <blp@nicira.com>2012-05-09 13:28:38 -0700
commitf586278a4668786e4cea3dd0184059da2e948eb9 (patch)
treeebdac2bf66d09e35919edc243cd1d7fcacc958e2 /lib
parentac5b53f6b7531257ada83e9c61e4a3dd2fd9f2a4 (diff)
ofproto-dpif: Introduce "slow path" datapath flows.
Most exact-match flows can be handled directly in the datapath, but for various reasons, some cannot: every packet in these flows must be sent separately to userspace. Until now, flows that cannot be handled entirely in the kernel have been allowed to miss each time in the datapath. This is generally OK, but it has a few disadvantages: * It can make troubleshooting at the level where one must look at datapath flows a bit confusing in some cases, because datapath misses due to genuinely new flows are mixed in with datapath misses for known flows that cannot be set up. * It means that the kernel-to-userspace packets for a given input port always go to a single kernel-to-userspace queue, even if we'd like to segregate out some of the packets for known flows. (An upcoming commit has examples.) This commit therefore introduces the concept of a "slow path" flow, one that is installed in the datapath with a single action that sends the flow's packets to userspace. To make troubleshooting easier, the action includes a reason code (displayed by "ovs-dpctl dump-flows") that explains why the flow has been slow-pathed. Bug #7550. Signed-off-by: Ben Pfaff <blp@nicira.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/odp-util.c98
-rw-r--r--lib/odp-util.h37
2 files changed, 128 insertions, 7 deletions
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 7bfbadeb..88986a2e 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -166,6 +166,52 @@ format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
ds_put_format(ds, "))");
}
+static const char *
+slow_path_reason_to_string(enum slow_path_reason bit)
+{
+ switch (bit) {
+ case SLOW_CFM:
+ return "cfm";
+ case SLOW_LACP:
+ return "lacp";
+ case SLOW_STP:
+ return "stp";
+ case SLOW_IN_BAND:
+ return "in_band";
+ case SLOW_CONTROLLER:
+ return "controller";
+ case SLOW_MATCH:
+ return "match";
+ default:
+ return NULL;
+ }
+}
+
+static void
+format_slow_path_reason(struct ds *ds, uint32_t slow)
+{
+ uint32_t bad = 0;
+
+ while (slow) {
+ uint32_t bit = rightmost_1bit(slow);
+ const char *s;
+
+ s = slow_path_reason_to_string(bit);
+ if (s) {
+ ds_put_format(ds, "%s,", s);
+ } else {
+ bad |= bit;
+ }
+
+ slow &= ~bit;
+ }
+
+ if (bad) {
+ ds_put_format(ds, "0x%"PRIx32",", bad);
+ }
+ ds_chomp(ds, ',');
+}
+
static void
format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
{
@@ -191,13 +237,21 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
switch (cookie.type) {
case USER_ACTION_COOKIE_SFLOW:
- ds_put_format(ds, ",sFlow,"
- "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32,
+ ds_put_format(ds, ",sFlow("
+ "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
vlan_tci_to_vid(cookie.sflow.vlan_tci),
vlan_tci_to_pcp(cookie.sflow.vlan_tci),
cookie.sflow.output);
break;
+ case USER_ACTION_COOKIE_SLOW_PATH:
+ ds_put_cstr(ds, ",slow_path(");
+ if (cookie.slow_path.reason) {
+ format_slow_path_reason(ds, cookie.slow_path.reason);
+ }
+ ds_put_char(ds, ')');
+ break;
+
case USER_ACTION_COOKIE_UNSPEC:
default:
ds_put_format(ds, ",userdata=0x%"PRIx64, userdata);
@@ -346,8 +400,8 @@ parse_odp_action(const char *s, const struct shash *port_names,
if (sscanf(s, "userspace(pid=%lli)%n", &pid, &n) > 0 && n > 0) {
odp_put_userspace_action(pid, NULL, actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,sFlow,vid=%i,"
- "pcp=%i,output=%lli)%n",
+ } else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i,"
+ "pcp=%i,output=%lli))%n",
&pid, &vid, &pcp, &output, &n) > 0 && n > 0) {
union user_action_cookie cookie;
uint16_t tci;
@@ -362,6 +416,42 @@ parse_odp_action(const char *s, const struct shash *port_names,
cookie.sflow.output = output;
odp_put_userspace_action(pid, &cookie, actions);
return n;
+ } else if (sscanf(s, "userspace(pid=%lli,slow_path(%n", &pid, &n) > 0
+ && n > 0) {
+ union user_action_cookie cookie;
+
+ cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
+ cookie.slow_path.unused = 0;
+ cookie.slow_path.reason = 0;
+
+ while (s[n] != ')') {
+ uint32_t bit;
+
+ for (bit = 1; bit; bit <<= 1) {
+ const char *reason = slow_path_reason_to_string(bit);
+ size_t len = strlen(reason);
+
+ if (reason
+ && !strncmp(s + n, reason, len)
+ && (s[n + len] == ',' || s[n + len] == ')'))
+ {
+ cookie.slow_path.reason |= bit;
+ n += len + (s[n + len] == ',');
+ break;
+ }
+ }
+
+ if (!bit) {
+ return -EINVAL;
+ }
+ }
+ if (s[n + 1] != ')') {
+ return -EINVAL;
+ }
+ n += 2;
+
+ odp_put_userspace_action(pid, &cookie, actions);
+ return n;
} else if (sscanf(s, "userspace(pid=%lli,userdata="
"%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s,
&n) > 0 && n > 0) {
diff --git a/lib/odp-util.h b/lib/odp-util.h
index c52e4709..08b0a2a4 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -116,9 +116,20 @@ enum odp_key_fitness odp_flow_key_to_flow(const struct nlattr *, size_t,
struct flow *);
const char *odp_key_fitness_to_string(enum odp_key_fitness);
+void commit_odp_actions(const struct flow *, struct flow *base,
+ struct ofpbuf *odp_actions);
+
+/* ofproto-dpif interface.
+ *
+ * The following types and functions are logically part of ofproto-dpif.
+ * ofproto-dpif puts values of these types into the flows that it installs in
+ * the kernel datapath, though, so ovs-dpctl needs to interpret them so that
+ * it can print flows in a more human-readable manner. */
+
enum user_action_cookie_type {
USER_ACTION_COOKIE_UNSPEC,
USER_ACTION_COOKIE_SFLOW, /* Packet for sFlow sampling. */
+ USER_ACTION_COOKIE_SLOW_PATH /* Userspace must process this flow. */
};
/* user_action_cookie is passed as argument to OVS_ACTION_ATTR_USERSPACE.
@@ -131,14 +142,34 @@ union user_action_cookie {
ovs_be16 vlan_tci; /* Destination VLAN TCI. */
uint32_t output; /* SFL_FLOW_SAMPLE_TYPE 'output' value. */
} sflow;
-};
+ struct {
+ uint16_t type; /* USER_ACTION_COOKIE_SLOW_PATH. */
+ uint16_t unused;
+ uint32_t reason; /* enum slow_path_reason. */
+ } slow_path;
+};
BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 8);
size_t odp_put_userspace_action(uint32_t pid,
const union user_action_cookie *,
struct ofpbuf *odp_actions);
-void commit_odp_actions(const struct flow *, struct flow *base,
- struct ofpbuf *odp_actions);
+/* Reasons why a subfacet might not be fast-pathable. */
+enum slow_path_reason {
+ /* These reasons are mutually exclusive. */
+ SLOW_CFM = 1 << 0, /* CFM packets need per-packet processing. */
+ SLOW_LACP = 1 << 1, /* LACP packets need per-packet processing. */
+ SLOW_STP = 1 << 2, /* STP packets need per-packet processing. */
+ SLOW_IN_BAND = 1 << 3, /* In-band control needs every packet. */
+
+ /* Mutually exclusive with SLOW_CFM, SLOW_LACP, SLOW_STP.
+ * Could possibly appear with SLOW_IN_BAND. */
+ SLOW_CONTROLLER = 1 << 4, /* Packets must go to OpenFlow controller. */
+
+ /* This can appear on its own, or, theoretically at least, along with any
+ * other combination of reasons. */
+ SLOW_MATCH = 1 << 5, /* Datapath can't match specifically enough. */
+};
+
#endif /* odp-util.h */