From: Paul Blakey <pa...@mellanox.com> Usage: # to dump all datapath flows (default): ovs-dpctl dump-flows
# to dump only flows that in kernel datapath: ovs-dpctl dump-flows type=ovs # to dump only flows that are offloaded: ovs-dpctl dump-flows type=offloaded Signed-off-by: Paul Blakey <pa...@mellanox.com> Reviewed-by: Roi Dayan <r...@mellanox.com> Reviewed-by: Simon Horman <simon.hor...@netronome.com> Acked-by: Flavio Leitner <f...@sysclose.org> --- lib/dpctl.c | 44 ++++++++++++++++++++++++------ lib/dpctl.man | 7 ++++- lib/dpif-netdev.c | 3 ++- lib/dpif-netlink.c | 63 ++++++++++++++++++++++++++++++++++--------- lib/dpif-provider.h | 6 +++-- lib/dpif.c | 4 +-- lib/dpif.h | 3 ++- ofproto/ofproto-dpif-upcall.c | 3 ++- ofproto/ofproto-dpif.c | 2 +- 9 files changed, 106 insertions(+), 29 deletions(-) diff --git a/lib/dpctl.c b/lib/dpctl.c index 2dfaeeb..a2ee8a2 100644 --- a/lib/dpctl.c +++ b/lib/dpctl.c @@ -754,6 +754,11 @@ format_dpif_flow(struct ds *ds, const struct dpif_flow *f, struct hmap *ports, format_odp_actions(ds, f->actions, f->actions_len); } +static char *supported_dump_types[] = { + "offloaded", + "ovs", +}; + static int dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p) { @@ -762,6 +767,7 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p) char *name; char *filter = NULL; + char *type = NULL; struct flow flow_filter; struct flow_wildcards wc_filter; @@ -773,22 +779,29 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p) struct dpif_flow_dump *flow_dump; struct dpif_flow f; int pmd_id = PMD_ID_NULL; + int lastargc = 0; int error; - if (argc > 1 && !strncmp(argv[argc - 1], "filter=", 7)) { - filter = xstrdup(argv[--argc] + 7); + while (argc > 1 && lastargc != argc) { + lastargc = argc; + if (!strncmp(argv[argc - 1], "filter=", 7) && !filter) { + filter = xstrdup(argv[--argc] + 7); + } else if (!strncmp(argv[argc - 1], "type=", 5) && !type) { + type = xstrdup(argv[--argc] + 5); + } } - name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(dpctl_p); + + name = (argc > 1) ? xstrdup(argv[1]) : get_one_dp(dpctl_p); if (!name) { error = EINVAL; - goto out_freefilter; + goto out_free; } error = parsed_dpif_open(name, false, &dpif); free(name); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); - goto out_freefilter; + goto out_free; } @@ -816,6 +829,20 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p) } } + if (type) { + error = EINVAL; + for (int i = 0; i < ARRAY_SIZE(supported_dump_types); i++) { + if (!strcmp(supported_dump_types[i], type)) { + error = 0; + break; + } + } + if (error) { + dpctl_error(dpctl_p, error, "Failed to parse type (%s)", type); + goto out_free; + } + } + /* Make sure that these values are different. PMD_ID_NULL means that the * pmd is unspecified (e.g. because the datapath doesn't have different * pmd threads), while NON_PMD_CORE_ID refers to every non pmd threads @@ -823,7 +850,7 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p) BUILD_ASSERT(PMD_ID_NULL != NON_PMD_CORE_ID); ds_init(&ds); - flow_dump = dpif_flow_dump_create(dpif, false); + flow_dump = dpif_flow_dump_create(dpif, false, (type ? type : "dpctl")); flow_dump_thread = dpif_flow_dump_thread_create(flow_dump); while (dpif_flow_dump_next(flow_dump_thread, &f, 1)) { if (filter) { @@ -874,8 +901,9 @@ out_dpifclose: odp_portno_names_destroy(&portno_names); hmap_destroy(&portno_names); dpif_close(dpif); -out_freefilter: +out_free: free(filter); + free(type); return error; } @@ -1558,7 +1586,7 @@ static const struct dpctl_command all_commands[] = { { "set-if", "dp iface...", 2, INT_MAX, dpctl_set_if, DP_RW }, { "dump-dps", "", 0, 0, dpctl_dump_dps, DP_RO }, { "show", "[dp...]", 0, INT_MAX, dpctl_show, DP_RO }, - { "dump-flows", "[dp] [filter=..]", 0, 2, dpctl_dump_flows, DP_RO }, + { "dump-flows", "[dp] [filter=..] [type=..]", 0, 3, dpctl_dump_flows, DP_RO }, { "add-flow", "[dp] flow actions", 2, 3, dpctl_add_flow, DP_RW }, { "mod-flow", "[dp] flow actions", 2, 3, dpctl_mod_flow, DP_RW }, { "get-flow", "[dp] ufid", 1, 2, dpctl_get_flow, DP_RO }, diff --git a/lib/dpctl.man b/lib/dpctl.man index f7ae311..f6e4a7a 100644 --- a/lib/dpctl.man +++ b/lib/dpctl.man @@ -99,7 +99,7 @@ default. When multiple datapaths exist, then a datapath name is required. . .TP -.DO "[\fB\-m \fR| \fB\-\-more\fR]" \*(DX\fBdump\-flows\fR "[\fIdp\fR] [\fBfilter=\fIfilter\fR]" +.DO "[\fB\-m \fR| \fB\-\-more\fR]" \*(DX\fBdump\-flows\fR "[\fIdp\fR] [\fBfilter=\fIfilter\fR] [\fBtype=\fItype\fR]" Prints to the console all flow entries in datapath \fIdp\fR's flow table. Without \fB\-m\fR or \fB\-\-more\fR, output omits match fields that a flow wildcards entirely; with \fB\-m\fR or \fB\-\-more\fR, @@ -112,6 +112,11 @@ not an OpenFlow flow: besides other differences, it never contains wildcards.) The \fIfilter\fR is also useful to match wildcarded fields in the datapath flow. As an example, \fBfilter='tcp,tp_src=100'\fR will match the datapath flow containing '\fBtcp(src=80/0xff00,dst=8080/0xff)\fR'. +.IP +If \fBtype=\fItype\fR is specified, only displays flows of a specific type. +\fItype\fR can be \fBoffloaded\fR to display only offloaded rules or \fBOVS\fR +to display only non-offloaded rules. +By default both offloaded and non-offloaded rules are displayed. . .IP "\*(DX\fBadd\-flow\fR [\fIdp\fR] \fIflow actions\fR" .TP diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 2f224db..2b65dc7 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -2659,7 +2659,8 @@ dpif_netdev_flow_dump_cast(struct dpif_flow_dump *dump) } static struct dpif_flow_dump * -dpif_netdev_flow_dump_create(const struct dpif *dpif_, bool terse) +dpif_netdev_flow_dump_create(const struct dpif *dpif_, bool terse, + char *type OVS_UNUSED) { struct dpif_netdev_flow_dump *dump; diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index 0e6ed43..e030bb0 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -1445,6 +1445,16 @@ dpif_netlink_init_flow_del(struct dpif_netlink *dpif, del->ufid, del->terse, request); } +enum { + DUMP_OVS_FLOWS_BIT = 0, + DUMP_OFFLOADED_FLOWS_BIT = 1, +}; + +enum { + DUMP_OVS_FLOWS = (1 << DUMP_OVS_FLOWS_BIT), + DUMP_OFFLOADED_FLOWS = (1 << DUMP_OFFLOADED_FLOWS_BIT), +}; + struct dpif_netlink_flow_dump { struct dpif_flow_dump up; struct nl_dump nl_dump; @@ -1453,6 +1463,7 @@ struct dpif_netlink_flow_dump { int netdev_dumps_num; /* Number of netdev_flow_dumps */ struct ovs_mutex netdev_lock; /* Guards the following. */ int netdev_current_dump OVS_GUARDED; /* Shared current dump */ + int type; /* Type of dump */ }; static struct dpif_netlink_flow_dump * @@ -1467,7 +1478,7 @@ start_netdev_dump(const struct dpif *dpif_, { ovs_mutex_init(&dump->netdev_lock); - if (!netdev_is_flow_api_enabled()) { + if (!(dump->type & DUMP_OFFLOADED_FLOWS)) { dump->netdev_dumps_num = 0; dump->netdev_dumps = NULL; return; @@ -1481,8 +1492,24 @@ start_netdev_dump(const struct dpif *dpif_, ovs_mutex_unlock(&dump->netdev_lock); } +static int +dpif_netlink_get_dump_type(char *str) { + int type = 0; + + if (!str || !strcmp(str, "ovs") || !strcmp(str, "dpctl")) { + type |= DUMP_OVS_FLOWS; + } + if ((netdev_is_flow_api_enabled() && !str) + || (str && (!strcmp(str, "offloaded") || !strcmp(str, "dpctl")))) { + type |= DUMP_OFFLOADED_FLOWS; + } + + return type; +} + static struct dpif_flow_dump * -dpif_netlink_flow_dump_create(const struct dpif *dpif_, bool terse) +dpif_netlink_flow_dump_create(const struct dpif *dpif_, bool terse, + char *type) { const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); struct dpif_netlink_flow_dump *dump; @@ -1492,16 +1519,20 @@ dpif_netlink_flow_dump_create(const struct dpif *dpif_, bool terse) dump = xmalloc(sizeof *dump); dpif_flow_dump_init(&dump->up, dpif_); - dpif_netlink_flow_init(&request); - request.cmd = OVS_FLOW_CMD_GET; - request.dp_ifindex = dpif->dp_ifindex; - request.ufid_present = false; - request.ufid_terse = terse; + dump->type = dpif_netlink_get_dump_type(type); - buf = ofpbuf_new(1024); - dpif_netlink_flow_to_ofpbuf(&request, buf); - nl_dump_start(&dump->nl_dump, NETLINK_GENERIC, buf); - ofpbuf_delete(buf); + if (dump->type & DUMP_OVS_FLOWS) { + dpif_netlink_flow_init(&request); + request.cmd = OVS_FLOW_CMD_GET; + request.dp_ifindex = dpif->dp_ifindex; + request.ufid_present = false; + request.ufid_terse = terse; + + buf = ofpbuf_new(1024); + dpif_netlink_flow_to_ofpbuf(&request, buf); + nl_dump_start(&dump->nl_dump, NETLINK_GENERIC, buf); + ofpbuf_delete(buf); + } atomic_init(&dump->status, 0); dump->up.terse = terse; @@ -1514,9 +1545,13 @@ static int dpif_netlink_flow_dump_destroy(struct dpif_flow_dump *dump_) { struct dpif_netlink_flow_dump *dump = dpif_netlink_flow_dump_cast(dump_); - unsigned int nl_status = nl_dump_done(&dump->nl_dump); + unsigned int nl_status = 0; int dump_status; + if (dump->type & DUMP_OVS_FLOWS) { + nl_status = nl_dump_done(&dump->nl_dump); + } + for (int i = 0; i < dump->netdev_dumps_num; i++) { int err = netdev_flow_dump_destroy(dump->netdev_dumps[i]); @@ -1741,6 +1776,10 @@ dpif_netlink_flow_dump_next(struct dpif_flow_dump_thread *thread_, } } + if (!(dump->type & DUMP_OVS_FLOWS)) { + return n_flows; + } + while (!n_flows || (n_flows < max_flows && thread->nl_flows.size)) { struct dpif_netlink_flow datapath_flow; diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h index a4ed01a..64ac2e2 100644 --- a/lib/dpif-provider.h +++ b/lib/dpif-provider.h @@ -281,9 +281,11 @@ struct dpif_class { * dpif_flow_dump_thread_init(), respectively. * * If 'terse' is true, then only UID and statistics will - * be returned in the dump. Otherwise, all fields will be returned. */ + * be returned in the dump. Otherwise, all fields will be returned. + * + * If 'type' isn't null, dumps only the flows of the given type. */ struct dpif_flow_dump *(*flow_dump_create)(const struct dpif *dpif, - bool terse); + bool terse, char *type); int (*flow_dump_destroy)(struct dpif_flow_dump *dump); struct dpif_flow_dump_thread *(*flow_dump_thread_create)( diff --git a/lib/dpif.c b/lib/dpif.c index 7756315..7dc0d64 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1061,9 +1061,9 @@ dpif_flow_del(struct dpif *dpif, * This function always successfully returns a dpif_flow_dump. Error * reporting is deferred to dpif_flow_dump_destroy(). */ struct dpif_flow_dump * -dpif_flow_dump_create(const struct dpif *dpif, bool terse) +dpif_flow_dump_create(const struct dpif *dpif, bool terse, char *type) { - return dpif->dpif_class->flow_dump_create(dpif, terse); + return dpif->dpif_class->flow_dump_create(dpif, terse, type); } /* Destroys 'dump', which must have been created with dpif_flow_dump_create(). diff --git a/lib/dpif.h b/lib/dpif.h index 81376c0..b1f516e 100644 --- a/lib/dpif.h +++ b/lib/dpif.h @@ -569,7 +569,8 @@ int dpif_flow_get(struct dpif *, * * All error reporting is deferred to the call to dpif_flow_dump_destroy(). */ -struct dpif_flow_dump *dpif_flow_dump_create(const struct dpif *, bool terse); +struct dpif_flow_dump *dpif_flow_dump_create(const struct dpif *, bool terse, + char *type); int dpif_flow_dump_destroy(struct dpif_flow_dump *); struct dpif_flow_dump_thread *dpif_flow_dump_thread_create( diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c index caa3288..b2f9d91 100644 --- a/ofproto/ofproto-dpif-upcall.c +++ b/ofproto/ofproto-dpif-upcall.c @@ -892,7 +892,8 @@ udpif_revalidator(void *arg) bool terse_dump; terse_dump = udpif_use_ufid(udpif); - udpif->dump = dpif_flow_dump_create(udpif->dpif, terse_dump); + udpif->dump = dpif_flow_dump_create(udpif->dpif, terse_dump, + NULL); } } diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 8d90446..8509318 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -5267,7 +5267,7 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn, } ds_init(&ds); - flow_dump = dpif_flow_dump_create(ofproto->backer->dpif, false); + flow_dump = dpif_flow_dump_create(ofproto->backer->dpif, false, NULL); flow_dump_thread = dpif_flow_dump_thread_create(flow_dump); while (dpif_flow_dump_next(flow_dump_thread, &f, 1)) { struct flow flow; -- 2.7.4 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev