datapath-windows: set the nlBuf tail properly
[cascardo/ovs.git] / lib / dpctl.c
index 9adf9c8..4c2614b 100644 (file)
@@ -31,6 +31,7 @@
 #include "dirs.h"
 #include "dpctl.h"
 #include "dpif.h"
+#include "dpif-netdev.h"
 #include "dynamic-string.h"
 #include "flow.h"
 #include "match.h"
@@ -39,6 +40,7 @@
 #include "odp-util.h"
 #include "ofp-parse.h"
 #include "ofpbuf.h"
+#include "ovs-numa.h"
 #include "packets.h"
 #include "shash.h"
 #include "simap.h"
@@ -689,6 +691,27 @@ dpctl_dump_dps(int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
     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)
 {
@@ -708,7 +731,7 @@ 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)) {
@@ -747,7 +770,7 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p)
     }
 
     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) {
@@ -771,21 +794,19 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p)
             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);
@@ -806,12 +827,43 @@ out_freefilter:
     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;
@@ -819,9 +871,11 @@ dpctl_put_flow(int argc, const char *argv[], enum dpif_flow_put_flags flags,
     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) {
@@ -834,6 +888,15 @@ dpctl_put_flow(int argc, const char *argv[], enum dpif_flow_put_flags flags,
         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) {
@@ -855,12 +918,40 @@ dpctl_put_flow(int argc, const char *argv[], enum dpif_flow_put_flags flags,
         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;
@@ -881,6 +972,7 @@ out_freekeymask:
     ofpbuf_uninit(&mask);
     ofpbuf_uninit(&key);
     dpif_close(dpif);
+    netdev_close(in_port_netdev);
     return error;
 }
 
@@ -906,19 +998,82 @@ dpctl_mod_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p)
     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) {
@@ -931,6 +1086,16 @@ dpctl_del_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p)
         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));
@@ -945,11 +1110,43 @@ dpctl_del_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p)
         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;
     }
 
@@ -967,6 +1164,7 @@ out:
     ofpbuf_uninit(&key);
     simap_destroy(&port_names);
     dpif_close(dpif);
+    netdev_close(in_port_netdev);
     return error;
 }
 
@@ -1306,6 +1504,7 @@ static const struct dpctl_command all_commands[] = {
     { "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 },