mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 06:15:47 +00:00
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>
This commit is contained in:
@@ -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;
|
||||
@@ -360,6 +414,42 @@ parse_odp_action(const char *s, const struct shash *port_names,
|
||||
cookie.type = USER_ACTION_COOKIE_SFLOW;
|
||||
cookie.sflow.vlan_tci = htons(tci);
|
||||
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="
|
||||
|
Reference in New Issue
Block a user