#include "dirs.h"
#include "dpctl.h"
#include "dpif.h"
+#include "dpif-netdev.h"
#include "dynamic-string.h"
#include "flow.h"
#include "match.h"
#include "odp-util.h"
#include "ofp-parse.h"
#include "ofpbuf.h"
+#include "ovs-numa.h"
#include "packets.h"
#include "shash.h"
#include "simap.h"
return lasterror;
}
+static void
+format_dpif_flow(struct ds *ds, const struct dpif_flow *f, struct hmap *ports,
+ struct dpctl_params *dpctl_p)
+{
+ if (dpctl_p->verbosity) {
+ if (f->ufid_present) {
+ odp_format_ufid(&f->ufid, ds);
+ ds_put_cstr(ds, ", ");
+ } else {
+ ds_put_cstr(ds, "ufid:<empty>, ");
+ }
+ }
+ odp_flow_format(f->key, f->key_len, f->mask, f->mask_len, ports, ds,
+ dpctl_p->verbosity);
+ ds_put_cstr(ds, ", ");
+
+ dpif_flow_stats_format(&f->stats, ds);
+ ds_put_cstr(ds, ", actions:");
+ format_odp_actions(ds, f->actions, f->actions_len);
+}
+
static int
dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p)
{
struct dpif_flow_dump_thread *flow_dump_thread;
struct dpif_flow_dump *flow_dump;
struct dpif_flow f;
-
+ int pmd_id = PMD_ID_NULL;
int error;
if (argc > 1 && !strncmp(argv[argc - 1], "filter=", 7)) {
}
ds_init(&ds);
- flow_dump = dpif_flow_dump_create(dpif);
+ flow_dump = dpif_flow_dump_create(dpif, false);
flow_dump_thread = dpif_flow_dump_thread_create(flow_dump);
while (dpif_flow_dump_next(flow_dump_thread, &f, 1)) {
if (filter) {
minimatch_destroy(&minimatch);
}
ds_clear(&ds);
- if (dpctl_p->verbosity) {
- if (f.ufid_present) {
- odp_format_ufid(&f.ufid, &ds);
- ds_put_cstr(&ds, ", ");
+ /* If 'pmd_id' is specified, overlapping flows could be dumped from
+ * different pmd threads. So, separates dumps from different pmds
+ * by printing a title line. */
+ if (pmd_id != f.pmd_id) {
+ if (f.pmd_id == NON_PMD_CORE_ID) {
+ ds_put_format(&ds, "flow-dump from non-dpdk interfaces:\n");
} else {
- ds_put_cstr(&ds, "ufid:<empty>, ");
+ ds_put_format(&ds, "flow-dump from pmd on cpu core: %d\n",
+ f.pmd_id);
}
+ pmd_id = f.pmd_id;
}
- odp_flow_format(f.key, f.key_len, f.mask, f.mask_len,
- &portno_names, &ds, dpctl_p->verbosity);
- ds_put_cstr(&ds, ", ");
-
- dpif_flow_stats_format(&f.stats, &ds);
- ds_put_cstr(&ds, ", actions:");
- format_odp_actions(&ds, f.actions, f.actions_len);
+ format_dpif_flow(&ds, &f, &portno_names, dpctl_p);
dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
}
dpif_flow_dump_thread_destroy(flow_dump_thread);
return error;
}
+/* Extracts the in_port from the parsed keys, and returns the reference
+ * to the 'struct netdev *' of the dpif port. On error, returns NULL.
+ * Users must call 'netdev_close()' after finish using the returned
+ * reference. */
+static struct netdev *
+get_in_port_netdev_from_key(struct dpif *dpif, const struct ofpbuf *key)
+{
+ const struct nlattr *in_port_nla;
+ struct netdev *dev = NULL;
+
+ in_port_nla = nl_attr_find(key, 0, OVS_KEY_ATTR_IN_PORT);
+ if (in_port_nla) {
+ struct dpif_port dpif_port;
+ odp_port_t port_no;
+ int error;
+
+ port_no = ODP_PORT_C(nl_attr_get_u32(in_port_nla));
+ error = dpif_port_query_by_number(dpif, port_no, &dpif_port);
+ if (error) {
+ goto out;
+ }
+
+ netdev_open(dpif_port.name, dpif_port.type, &dev);
+ dpif_port_destroy(&dpif_port);
+ }
+
+out:
+ return dev;
+}
+
static int
dpctl_put_flow(int argc, const char *argv[], enum dpif_flow_put_flags flags,
struct dpctl_params *dpctl_p)
{
const char *key_s = argv[argc - 2];
const char *actions_s = argv[argc - 1];
+ struct netdev *in_port_netdev = NULL;
struct dpif_flow_stats stats;
struct dpif_port dpif_port;
struct dpif_port_dump port_dump;
struct ofpbuf key;
struct ofpbuf mask;
struct dpif *dpif;
+ ovs_u128 ufid;
+ bool ufid_present;
char *dp_name;
struct simap port_names;
- int error;
+ int n, error;
dp_name = argc == 4 ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
if (!dp_name) {
return error;
}
+ ufid_present = false;
+ n = odp_ufid_from_string(key_s, &ufid);
+ if (n < 0) {
+ dpctl_error(dpctl_p, -n, "parsing flow ufid");
+ return -n;
+ } else if (n) {
+ key_s += n;
+ ufid_present = true;
+ }
simap_init(&port_names);
DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
dpctl_error(dpctl_p, error, "parsing actions");
goto out_freeactions;
}
- error = dpif_flow_put(dpif, flags,
- ofpbuf_data(&key), ofpbuf_size(&key),
- ofpbuf_size(&mask) == 0 ? NULL : ofpbuf_data(&mask),
- ofpbuf_size(&mask),
- ofpbuf_data(&actions), ofpbuf_size(&actions),
- NULL, dpctl_p->print_statistics ? &stats : NULL);
+
+ /* For DPDK interface, applies the operation to all pmd threads
+ * on the same numa node. */
+ in_port_netdev = get_in_port_netdev_from_key(dpif, &key);
+ if (in_port_netdev && netdev_is_pmd(in_port_netdev)) {
+ int numa_id;
+
+ numa_id = netdev_get_numa_id(in_port_netdev);
+ if (ovs_numa_numa_id_is_valid(numa_id)) {
+ struct ovs_numa_dump *dump = ovs_numa_dump_cores_on_numa(numa_id);
+ struct ovs_numa_info *iter;
+
+ FOR_EACH_CORE_ON_NUMA (iter, dump) {
+ if (ovs_numa_core_is_pinned(iter->core_id)) {
+ error = dpif_flow_put(dpif, flags,
+ ofpbuf_data(&key), ofpbuf_size(&key),
+ ofpbuf_size(&mask) == 0 ? NULL : ofpbuf_data(&mask),
+ ofpbuf_size(&mask), ofpbuf_data(&actions),
+ ofpbuf_size(&actions), ufid_present ? &ufid : NULL,
+ iter->core_id, dpctl_p->print_statistics ? &stats : NULL);
+ }
+ }
+ ovs_numa_dump_destroy(dump);
+ } else {
+ error = EINVAL;
+ }
+ } else {
+ error = dpif_flow_put(dpif, flags,
+ ofpbuf_data(&key), ofpbuf_size(&key),
+ ofpbuf_size(&mask) == 0 ? NULL : ofpbuf_data(&mask),
+ ofpbuf_size(&mask), ofpbuf_data(&actions),
+ ofpbuf_size(&actions), ufid_present ? &ufid : NULL,
+ PMD_ID_NULL, dpctl_p->print_statistics ? &stats : NULL);
+ }
if (error) {
dpctl_error(dpctl_p, error, "updating flow table");
goto out_freeactions;
ofpbuf_uninit(&mask);
ofpbuf_uninit(&key);
dpif_close(dpif);
+ netdev_close(in_port_netdev);
return error;
}
return dpctl_put_flow(argc, argv, flags, dpctl_p);
}
+static int
+dpctl_get_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p)
+{
+ const char *key_s = argv[argc - 1];
+ struct dpif_flow flow;
+ struct dpif_port dpif_port;
+ struct dpif_port_dump port_dump;
+ struct dpif *dpif;
+ char *dp_name;
+ struct hmap portno_names;
+ ovs_u128 ufid;
+ struct ofpbuf buf;
+ uint64_t stub[DPIF_FLOW_BUFSIZE / 8];
+ struct ds ds;
+ int n, error;
+
+ dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
+ if (!dp_name) {
+ return EINVAL;
+ }
+ error = parsed_dpif_open(dp_name, false, &dpif);
+ free(dp_name);
+ if (error) {
+ dpctl_error(dpctl_p, error, "opening datapath");
+ return error;
+ }
+
+ ofpbuf_use_stub(&buf, &stub, sizeof stub);
+ hmap_init(&portno_names);
+ DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
+ odp_portno_names_set(&portno_names, dpif_port.port_no, dpif_port.name);
+ }
+
+ n = odp_ufid_from_string(key_s, &ufid);
+ if (n <= 0) {
+ dpctl_error(dpctl_p, -n, "parsing flow ufid");
+ goto out;
+ }
+
+ /* Does not work for DPDK, since do not know which 'pmd' to apply the
+ * operation. So, just uses PMD_ID_NULL. */
+ error = dpif_flow_get(dpif, NULL, 0, &ufid, PMD_ID_NULL, &buf, &flow);
+ if (error) {
+ dpctl_error(dpctl_p, error, "getting flow");
+ goto out;
+ }
+
+ ds_init(&ds);
+ format_dpif_flow(&ds, &flow, &portno_names, dpctl_p);
+ dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
+ ds_destroy(&ds);
+
+out:
+ odp_portno_names_destroy(&portno_names);
+ hmap_destroy(&portno_names);
+ ofpbuf_uninit(&buf);
+ dpif_close(dpif);
+ return error;
+}
+
static int
dpctl_del_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p)
{
const char *key_s = argv[argc - 1];
+ struct netdev *in_port_netdev = NULL;
struct dpif_flow_stats stats;
struct dpif_port dpif_port;
struct dpif_port_dump port_dump;
struct ofpbuf key;
struct ofpbuf mask; /* To be ignored. */
struct dpif *dpif;
+ ovs_u128 ufid;
+ bool ufid_present;
char *dp_name;
struct simap port_names;
- int error;
+ int n, error;
dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
if (!dp_name) {
return error;
}
+ ufid_present = false;
+ n = odp_ufid_from_string(key_s, &ufid);
+ if (n < 0) {
+ dpctl_error(dpctl_p, -n, "parsing flow ufid");
+ return -n;
+ } else if (n) {
+ key_s += n;
+ ufid_present = true;
+ }
+
simap_init(&port_names);
DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
simap_put(&port_names, dpif_port.name, odp_to_u32(dpif_port.port_no));
goto out;
}
- error = dpif_flow_del(dpif,
- ofpbuf_data(&key), ofpbuf_size(&key), NULL,
- dpctl_p->print_statistics ? &stats : NULL);
+ /* For DPDK interface, applies the operation to all pmd threads
+ * on the same numa node. */
+ in_port_netdev = get_in_port_netdev_from_key(dpif, &key);
+ if (in_port_netdev && netdev_is_pmd(in_port_netdev)) {
+ int numa_id;
+
+ numa_id = netdev_get_numa_id(in_port_netdev);
+ if (ovs_numa_numa_id_is_valid(numa_id)) {
+ struct ovs_numa_dump *dump = ovs_numa_dump_cores_on_numa(numa_id);
+ struct ovs_numa_info *iter;
+
+ FOR_EACH_CORE_ON_NUMA (iter, dump) {
+ if (ovs_numa_core_is_pinned(iter->core_id)) {
+ error = dpif_flow_del(dpif, ofpbuf_data(&key),
+ ofpbuf_size(&key), ufid_present ? &ufid : NULL,
+ iter->core_id, dpctl_p->print_statistics ? &stats : NULL);
+ }
+ }
+ ovs_numa_dump_destroy(dump);
+ } else {
+ error = EINVAL;
+ }
+ } else {
+ error = dpif_flow_del(dpif, ofpbuf_data(&key), ofpbuf_size(&key),
+ ufid_present ? &ufid : NULL, PMD_ID_NULL,
+ dpctl_p->print_statistics ? &stats : NULL);
+ }
if (error) {
dpctl_error(dpctl_p, error, "deleting flow");
+ if (error == ENOENT && !ufid_present) {
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_format(&s, "Perhaps you need to specify a UFID?");
+ dpctl_print(dpctl_p, "%s\n", ds_cstr(&s));
+ ds_destroy(&s);
+ }
goto out;
}
ofpbuf_uninit(&key);
simap_destroy(&port_names);
dpif_close(dpif);
+ netdev_close(in_port_netdev);
return error;
}
{ "dump-flows", "[dp]", 0, 2, dpctl_dump_flows },
{ "add-flow", "add-flow [dp] flow actions", 2, 3, dpctl_add_flow },
{ "mod-flow", "mod-flow [dp] flow actions", 2, 3, dpctl_mod_flow },
+ { "get-flow", "get-flow [dp] ufid", 1, 2, dpctl_get_flow },
{ "del-flow", "del-flow [dp] flow", 1, 2, dpctl_del_flow },
{ "del-flows", "[dp]", 0, 1, dpctl_del_flows },
{ "help", "", 0, INT_MAX, dpctl_help },