]> git.proxmox.com Git - mirror_ovs.git/commitdiff
dpif: Index flows using unique identifiers.
authorJoe Stringer <joestringer@nicira.com>
Wed, 24 Sep 2014 04:26:35 +0000 (16:26 +1200)
committerJoe Stringer <joestringer@nicira.com>
Tue, 2 Dec 2014 22:10:23 +0000 (14:10 -0800)
This patch modifies the dpif interface to allow flows to be manipulated
using a 128-bit identifier. This allows revalidator threads to perform
datapath operations faster, as they do not need to serialise the entire
flow key for operations like flow_get and flow_delete. In conjunction
with a future patch to simplify the dump interface, this provides a
significant performance benefit for revalidation.

When handlers assemble flow_put operations, they specify a unique
identifier (UFID) for each flow as it is passed down to the datapath to
be stored with the flow. The UFID is currently provided to handlers
by the dpif during upcall processing.

When revalidators assemble flow_get or flow_del operations, they may
specify the UFID for the flow along with the key. The dpif will decide
whether to send only the UFID to the datapath, or both the UFID and flow
key. The former is preferred for newer datapaths that support UFID,
while the latter is used for backwards compatibility.

Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
12 files changed:
lib/dpctl.c
lib/dpif-netdev.c
lib/dpif-netlink.c
lib/dpif.c
lib/dpif.h
lib/odp-util.c
lib/odp-util.h
ofproto/ofproto-dpif-upcall.c
ofproto/ofproto-dpif.c
tests/dpif-netdev.at
tests/ofproto-dpif.at
tests/ofproto-macros.at

index 2d9144b20eb80b681f64ab000333941291609c85..9adf9c860e53ad5147a59306e8be3c5838cfff98 100644 (file)
@@ -771,6 +771,14 @@ 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, ", ");
+            } else {
+                ds_put_cstr(&ds, "ufid:<empty>, ");
+            }
+        }
         odp_flow_format(f.key, f.key_len, f.mask, f.mask_len,
                         &portno_names, &ds, dpctl_p->verbosity);
         ds_put_cstr(&ds, ", ");
@@ -852,7 +860,7 @@ dpctl_put_flow(int argc, const char *argv[], enum dpif_flow_put_flags flags,
                           ofpbuf_size(&mask) == 0 ? NULL : ofpbuf_data(&mask),
                           ofpbuf_size(&mask),
                           ofpbuf_data(&actions), ofpbuf_size(&actions),
-                          dpctl_p->print_statistics ? &stats : NULL);
+                          NULL, dpctl_p->print_statistics ? &stats : NULL);
     if (error) {
         dpctl_error(dpctl_p, error, "updating flow table");
         goto out_freeactions;
@@ -938,7 +946,7 @@ dpctl_del_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p)
     }
 
     error = dpif_flow_del(dpif,
-                          ofpbuf_data(&key), ofpbuf_size(&key),
+                          ofpbuf_data(&key), ofpbuf_size(&key), NULL,
                           dpctl_p->print_statistics ? &stats : NULL);
     if (error) {
         dpctl_error(dpctl_p, error, "deleting flow");
index 52331301e945248f93954547e12f8ac3089983af..fb3a77419eed9326163e7b359f6f1f31092a73ce 100644 (file)
@@ -304,6 +304,7 @@ struct dp_netdev_flow {
 
     /* Hash table index by unmasked flow. */
     const struct cmap_node node; /* In owning dp_netdev's 'flow_table'. */
+    const ovs_u128 ufid;         /* Unique flow identifier. */
     const struct flow flow;      /* Unmasked flow that created this entry. */
 
     /* Number of references.
@@ -327,6 +328,8 @@ struct dp_netdev_flow {
 
 static void dp_netdev_flow_unref(struct dp_netdev_flow *);
 static bool dp_netdev_flow_ref(struct dp_netdev_flow *);
+static int dpif_netdev_flow_from_nlattrs(const struct nlattr *, uint32_t,
+                                         struct flow *);
 
 /* Contained by struct dp_netdev_flow's 'stats' member.  */
 struct dp_netdev_flow_stats {
@@ -1156,6 +1159,12 @@ static void dp_netdev_flow_unref(struct dp_netdev_flow *flow)
     }
 }
 
+static uint32_t
+dp_netdev_flow_hash(const ovs_u128 *ufid)
+{
+    return ufid->u32[0];
+}
+
 static void
 dp_netdev_remove_flow(struct dp_netdev *dp, struct dp_netdev_flow *flow)
     OVS_REQUIRES(dp->flow_mutex)
@@ -1163,7 +1172,7 @@ dp_netdev_remove_flow(struct dp_netdev *dp, struct dp_netdev_flow *flow)
     struct cmap_node *node = CONST_CAST(struct cmap_node *, &flow->node);
 
     dpcls_remove(&dp->cls, &flow->cr);
-    cmap_remove(&dp->flow_table, node, flow_hash(&flow->flow, 0));
+    cmap_remove(&dp->flow_table, node, dp_netdev_flow_hash(&flow->ufid));
     flow->dead = true;
 
     dp_netdev_flow_unref(flow);
@@ -1531,14 +1540,26 @@ dp_netdev_lookup_flow(const struct dp_netdev *dp,
 }
 
 static struct dp_netdev_flow *
-dp_netdev_find_flow(const struct dp_netdev *dp, const struct flow *flow)
+dp_netdev_find_flow(const struct dp_netdev *dp, const ovs_u128 *ufidp,
+                    const struct nlattr *key, size_t key_len)
 {
     struct dp_netdev_flow *netdev_flow;
+    struct flow flow;
+    ovs_u128 ufid;
+
+    /* If a UFID is not provided, determine one based on the key. */
+    if (!ufidp && key && key_len
+        && !dpif_netdev_flow_from_nlattrs(key, key_len, &flow)) {
+        dpif_flow_hash(dp->dpif, &flow, sizeof flow, &ufid);
+        ufidp = &ufid;
+    }
 
-    CMAP_FOR_EACH_WITH_HASH (netdev_flow, node, flow_hash(flow, 0),
-                             &dp->flow_table) {
-        if (flow_equal(&netdev_flow->flow, flow)) {
-            return netdev_flow;
+    if (ufidp) {
+        CMAP_FOR_EACH_WITH_HASH (netdev_flow, node, dp_netdev_flow_hash(ufidp),
+                                 &dp->flow_table) {
+            if (ovs_u128_equal(&netdev_flow->ufid, ufidp)) {
+                return netdev_flow;
+            }
         }
     }
 
@@ -1568,8 +1589,7 @@ get_dpif_flow_stats(const struct dp_netdev_flow *netdev_flow,
  * 'mask_buf'. Actions will be returned without copying, by relying on RCU to
  * protect them. */
 static void
-dp_netdev_flow_to_dpif_flow(const struct dpif *dpif,
-                            const struct dp_netdev_flow *netdev_flow,
+dp_netdev_flow_to_dpif_flow(const struct dp_netdev_flow *netdev_flow,
                             struct ofpbuf *key_buf, struct ofpbuf *mask_buf,
                             struct dpif_flow *flow)
 {
@@ -1599,8 +1619,8 @@ dp_netdev_flow_to_dpif_flow(const struct dpif *dpif,
     flow->actions = actions->actions;
     flow->actions_len = actions->size;
 
-    dpif_flow_hash(dpif, &netdev_flow->flow, sizeof netdev_flow->flow,
-                   &flow->ufid);
+    flow->ufid = netdev_flow->ufid;
+    flow->ufid_present = true;
     get_dpif_flow_stats(netdev_flow, &flow->stats);
 }
 
@@ -1701,20 +1721,13 @@ dpif_netdev_flow_get(const struct dpif *dpif, const struct dpif_flow_get *get)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     struct dp_netdev_flow *netdev_flow;
-    struct flow key;
-    int error;
-
-    error = dpif_netdev_flow_from_nlattrs(get->key, get->key_len, &key);
-    if (error) {
-        return error;
-    }
-
-    netdev_flow = dp_netdev_find_flow(dp, &key);
+    int error = 0;
 
+    netdev_flow = dp_netdev_find_flow(dp, get->ufid, get->key, get->key_len);
     if (netdev_flow) {
-        dp_netdev_flow_to_dpif_flow(dpif, netdev_flow, get->buffer,
-                                    get->buffer, get->flow);
-     } else {
+        dp_netdev_flow_to_dpif_flow(netdev_flow, get->buffer, get->buffer,
+                                    get->flow);
+    } else {
         error = ENOENT;
     }
 
@@ -1723,6 +1736,7 @@ dpif_netdev_flow_get(const struct dpif *dpif, const struct dpif_flow_get *get)
 
 static struct dp_netdev_flow *
 dp_netdev_flow_add(struct dp_netdev *dp, struct match *match,
+                   const ovs_u128 *ufid,
                    const struct nlattr *actions, size_t actions_len)
     OVS_REQUIRES(dp->flow_mutex)
 {
@@ -1737,13 +1751,14 @@ dp_netdev_flow_add(struct dp_netdev *dp, struct match *match,
     flow = xmalloc(sizeof *flow - sizeof flow->cr.flow.mf + mask.len);
     flow->dead = false;
     *CONST_CAST(struct flow *, &flow->flow) = match->flow;
+    *CONST_CAST(ovs_u128 *, &flow->ufid) = *ufid;
     ovs_refcount_init(&flow->ref_cnt);
     ovsthread_stats_init(&flow->stats);
     ovsrcu_set(&flow->actions, dp_netdev_actions_create(actions, actions_len));
 
     cmap_insert(&dp->flow_table,
                 CONST_CAST(struct cmap_node *, &flow->node),
-                flow_hash(&flow->flow, 0));
+                dp_netdev_flow_hash(&flow->ufid));
     netdev_flow_key_init_masked(&flow->cr.flow, &match->flow, &mask);
     dpcls_insert(&dp->cls, &flow->cr, &mask);
 
@@ -1755,6 +1770,8 @@ dp_netdev_flow_add(struct dp_netdev *dp, struct match *match,
         miniflow_expand(&flow->cr.mask->mf, &match.wc.masks);
 
         ds_put_cstr(&ds, "flow_add: ");
+        odp_format_ufid(ufid, &ds);
+        ds_put_cstr(&ds, " ");
         match_format(&match, &ds, OFP_DEFAULT_PRIORITY);
         ds_put_cstr(&ds, ", actions:");
         format_odp_actions(&ds, actions, actions_len);
@@ -1790,6 +1807,7 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
     struct dp_netdev_flow *netdev_flow;
     struct netdev_flow_key key;
     struct match match;
+    ovs_u128 ufid;
     int error;
 
     error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &match.flow);
@@ -1808,6 +1826,12 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
      * for upcall processing any more. */
     netdev_flow_key_from_flow(&key, &match.flow);
 
+    if (put->ufid) {
+        ufid = *put->ufid;
+    } else {
+        dpif_flow_hash(dpif, &match.flow, sizeof match.flow, &ufid);
+    }
+
     ovs_mutex_lock(&dp->flow_mutex);
     netdev_flow = dp_netdev_lookup_flow(dp, &key);
     if (!netdev_flow) {
@@ -1816,7 +1840,8 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
                 if (put->stats) {
                     memset(put->stats, 0, sizeof *put->stats);
                 }
-                dp_netdev_flow_add(dp, &match, put->actions, put->actions_len);
+                dp_netdev_flow_add(dp, &match, &ufid, put->actions,
+                                   put->actions_len);
                 error = 0;
             } else {
                 error = EFBIG;
@@ -1861,16 +1886,10 @@ dpif_netdev_flow_del(struct dpif *dpif, const struct dpif_flow_del *del)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     struct dp_netdev_flow *netdev_flow;
-    struct flow key;
-    int error;
-
-    error = dpif_netdev_flow_from_nlattrs(del->key, del->key_len, &key);
-    if (error) {
-        return error;
-    }
+    int error = 0;
 
     ovs_mutex_lock(&dp->flow_mutex);
-    netdev_flow = dp_netdev_find_flow(dp, &key);
+    netdev_flow = dp_netdev_find_flow(dp, del->ufid, del->key, del->key_len);
     if (netdev_flow) {
         if (del->stats) {
             get_dpif_flow_stats(netdev_flow, del->stats);
@@ -1994,7 +2013,7 @@ dpif_netdev_flow_dump_next(struct dpif_flow_dump_thread *thread_,
 
         ofpbuf_use_stack(&key, keybuf, sizeof *keybuf);
         ofpbuf_use_stack(&mask, maskbuf, sizeof *maskbuf);
-        dp_netdev_flow_to_dpif_flow(&dpif->dpif, netdev_flow, &key, &mask, f);
+        dp_netdev_flow_to_dpif_flow(netdev_flow, &key, &mask, f);
     }
 
     return n_flows;
@@ -2900,7 +2919,7 @@ fast_path_processing(struct dp_netdev_pmd_thread *pmd,
                 ovs_mutex_lock(&dp->flow_mutex);
                 netdev_flow = dp_netdev_lookup_flow(dp, &keys[i]);
                 if (OVS_LIKELY(!netdev_flow)) {
-                    netdev_flow = dp_netdev_flow_add(dp, &match,
+                    netdev_flow = dp_netdev_flow_add(dp, &match, &ufid,
                                                      ofpbuf_data(add_actions),
                                                      ofpbuf_size(add_actions));
                 }
index f290cc655acc3e3ce2ea53fbc31c1df249e7e2cc..5278059c2c864b6c98b4e7e9603140cf8eac7c5d 100644 (file)
@@ -116,6 +116,9 @@ struct dpif_netlink_flow {
     size_t mask_len;
     const struct nlattr *actions;       /* OVS_FLOW_ATTR_ACTIONS. */
     size_t actions_len;
+    ovs_u128 ufid;                      /* OVS_FLOW_ATTR_FLOW_ID. */
+    bool ufid_present;                  /* Is there a UFID? */
+    bool ufid_terse;                    /* Skip serializing key/mask/acts? */
     const struct ovs_flow_stats *stats; /* OVS_FLOW_ATTR_STATS. */
     const uint8_t *tcp_flags;           /* OVS_FLOW_ATTR_TCP_FLAGS. */
     const ovs_32aligned_u64 *used;      /* OVS_FLOW_ATTR_USED. */
@@ -135,6 +138,7 @@ static void dpif_netlink_flow_get_stats(const struct dpif_netlink_flow *,
                                         struct dpif_flow_stats *);
 static void dpif_netlink_flow_to_dpif_flow(struct dpif *, struct dpif_flow *,
                                            const struct dpif_netlink_flow *);
+static bool dpif_netlink_check_ufid(struct dpif *dpif);
 
 /* One of the dpif channels between the kernel and userspace. */
 struct dpif_channel {
@@ -185,6 +189,11 @@ struct dpif_netlink {
     /* Change notification. */
     struct nl_sock *port_notifier; /* vport multicast group subscriber. */
     bool refresh_channels;
+
+    /* If the datapath supports indexing flows using unique identifiers, then
+     * we can reduce the size of netlink messages by omitting fields like the
+     * flow key during flow operations. */
+    bool ufid_supported;
 };
 
 static void report_loss(struct dpif_netlink *, struct dpif_channel *,
@@ -301,6 +310,7 @@ open_dpif(const struct dpif_netlink_dp *dp, struct dpif **dpifp)
               dp->dp_ifindex, dp->dp_ifindex);
 
     dpif->dp_ifindex = dp->dp_ifindex;
+    dpif->ufid_supported = dpif_netlink_check_ufid(&dpif->dpif);
     *dpifp = &dpif->dpif;
 
     return 0;
@@ -1210,28 +1220,63 @@ dpif_netlink_port_poll_wait(const struct dpif *dpif_)
 }
 
 static void
-dpif_netlink_init_flow_get(const struct dpif_netlink *dpif,
-                           const struct nlattr *key, size_t key_len,
-                           struct dpif_netlink_flow *request)
+dpif_netlink_flow_init_ufid(struct dpif_netlink_flow *request,
+                            const ovs_u128 *ufid, bool terse)
+{
+    if (ufid) {
+        request->ufid = *ufid;
+        request->ufid_present = true;
+    } else {
+        request->ufid_present = false;
+    }
+    request->ufid_terse = terse;
+}
+
+static void
+dpif_netlink_init_flow_get__(const struct dpif_netlink *dpif,
+                             const struct nlattr *key, size_t key_len,
+                             const ovs_u128 *ufid, bool terse,
+                             struct dpif_netlink_flow *request)
 {
     dpif_netlink_flow_init(request);
     request->cmd = OVS_FLOW_CMD_GET;
     request->dp_ifindex = dpif->dp_ifindex;
     request->key = key;
     request->key_len = key_len;
+    dpif_netlink_flow_init_ufid(request, ufid, terse);
+}
+
+static void
+dpif_netlink_init_flow_get(const struct dpif_netlink *dpif,
+                           const struct dpif_flow_get *get,
+                           struct dpif_netlink_flow *request)
+{
+    dpif_netlink_init_flow_get__(dpif, get->key, get->key_len, get->ufid,
+                                 false, request);
 }
 
 static int
-dpif_netlink_flow_get(const struct dpif_netlink *dpif,
-                      const struct nlattr *key, size_t key_len,
-                      struct dpif_netlink_flow *reply, struct ofpbuf **bufp)
+dpif_netlink_flow_get__(const struct dpif_netlink *dpif,
+                        const struct nlattr *key, size_t key_len,
+                        const ovs_u128 *ufid, bool terse,
+                        struct dpif_netlink_flow *reply, struct ofpbuf **bufp)
 {
     struct dpif_netlink_flow request;
 
-    dpif_netlink_init_flow_get(dpif, key, key_len, &request);
+    dpif_netlink_init_flow_get__(dpif, key, key_len, ufid, terse, &request);
     return dpif_netlink_flow_transact(&request, reply, bufp);
 }
 
+static int
+dpif_netlink_flow_get(const struct dpif_netlink *dpif,
+                      const struct dpif_netlink_flow *flow,
+                      struct dpif_netlink_flow *reply, struct ofpbuf **bufp)
+{
+    return dpif_netlink_flow_get__(dpif, flow->key, flow->key_len,
+                                   flow->ufid_present ? &flow->ufid : NULL,
+                                   false, reply, bufp);
+}
+
 static void
 dpif_netlink_init_flow_put(struct dpif_netlink *dpif,
                            const struct dpif_flow_put *put,
@@ -1247,6 +1292,8 @@ dpif_netlink_init_flow_put(struct dpif_netlink *dpif,
     request->key_len = put->key_len;
     request->mask = put->mask;
     request->mask_len = put->mask_len;
+    dpif_netlink_flow_init_ufid(request, put->ufid, false);
+
     /* Ensure that OVS_FLOW_ATTR_ACTIONS will always be included. */
     request->actions = (put->actions
                         ? put->actions
@@ -1262,15 +1309,40 @@ dpif_netlink_init_flow_put(struct dpif_netlink *dpif,
 }
 
 static void
-dpif_netlink_init_flow_del(struct dpif_netlink *dpif,
-                           const struct dpif_flow_del *del,
-                           struct dpif_netlink_flow *request)
+dpif_netlink_init_flow_del__(struct dpif_netlink *dpif,
+                             const struct nlattr *key, size_t key_len,
+                             const ovs_u128 *ufid, bool terse,
+                             struct dpif_netlink_flow *request)
 {
     dpif_netlink_flow_init(request);
     request->cmd = OVS_FLOW_CMD_DEL;
     request->dp_ifindex = dpif->dp_ifindex;
-    request->key = del->key;
-    request->key_len = del->key_len;
+    request->key = key;
+    request->key_len = key_len;
+    dpif_netlink_flow_init_ufid(request, ufid, terse);
+}
+
+static void
+dpif_netlink_init_flow_del(struct dpif_netlink *dpif,
+                           const struct dpif_flow_del *del,
+                           struct dpif_netlink_flow *request)
+{
+    return dpif_netlink_init_flow_del__(dpif, del->key, del->key_len,
+                                        del->ufid, dpif->ufid_supported,
+                                        request);
+}
+
+static int
+dpif_netlink_flow_del(struct dpif_netlink *dpif,
+                      const struct nlattr *key, size_t key_len,
+                      const ovs_u128 *ufid, bool terse)
+{
+    struct dpif_netlink_flow request;
+
+    dpif_netlink_init_flow_del__(dpif, key, key_len, ufid, terse, &request);
+
+    /* Ignore stats */
+    return dpif_netlink_flow_transact(&request, NULL, NULL);
 }
 
 struct dpif_netlink_flow_dump {
@@ -1373,8 +1445,14 @@ dpif_netlink_flow_to_dpif_flow(struct dpif *dpif, struct dpif_flow *dpif_flow,
     dpif_flow->mask_len = datapath_flow->mask_len;
     dpif_flow->actions = datapath_flow->actions;
     dpif_flow->actions_len = datapath_flow->actions_len;
-    dpif_flow_hash(dpif, datapath_flow->key, datapath_flow->key_len,
-                   &dpif_flow->ufid);
+    dpif_flow->ufid_present = datapath_flow->ufid_present;
+    if (datapath_flow->ufid_present) {
+        dpif_flow->ufid = datapath_flow->ufid;
+    } else {
+        ovs_assert(datapath_flow->key && datapath_flow->key_len);
+        dpif_flow_hash(dpif, datapath_flow->key, datapath_flow->key_len,
+                       &dpif_flow->ufid);
+    }
     dpif_netlink_flow_get_stats(datapath_flow, &dpif_flow->stats);
 }
 
@@ -1417,8 +1495,7 @@ dpif_netlink_flow_dump_next(struct dpif_flow_dump_thread *thread_,
         } else {
             /* Rare case: the flow does not include actions.  Retrieve this
              * individual flow again to get the actions. */
-            error = dpif_netlink_flow_get(dpif, datapath_flow.key,
-                                          datapath_flow.key_len,
+            error = dpif_netlink_flow_get(dpif, &datapath_flow,
                                           &datapath_flow, &thread->nl_actions);
             if (error == ENOENT) {
                 VLOG_DBG("dumped flow disappeared on get");
@@ -1538,7 +1615,7 @@ dpif_netlink_operate__(struct dpif_netlink *dpif,
 
         case DPIF_OP_FLOW_GET:
             get = &op->u.flow_get;
-            dpif_netlink_init_flow_get(dpif, get->key, get->key_len, &flow);
+            dpif_netlink_init_flow_get(dpif, get, &flow);
             aux->txn.reply = get->buffer;
             dpif_netlink_flow_to_ofpbuf(&flow, &aux->request);
             break;
@@ -1660,6 +1737,61 @@ dpif_netlink_handler_uninit(struct dpif_handler *handler)
 }
 #endif
 
+/* Checks support for unique flow identifiers. */
+static bool
+dpif_netlink_check_ufid(struct dpif *dpif_)
+{
+    struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
+    struct flow flow;
+    struct odputil_keybuf keybuf;
+    struct ofpbuf key, *replybuf;
+    struct dpif_netlink_flow reply;
+    ovs_u128 ufid;
+    int error;
+    bool enable_ufid = false;
+
+    memset(&flow, 0, sizeof flow);
+    flow.dl_type = htons(0x1234);
+
+    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+    odp_flow_key_from_flow(&key, &flow, NULL, 0, true);
+    dpif_flow_hash(dpif_, ofpbuf_data(&key), ofpbuf_size(&key), &ufid);
+    error = dpif_flow_put(dpif_, DPIF_FP_CREATE | DPIF_FP_PROBE,
+                          ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, NULL,
+                          0, &ufid, NULL);
+
+    if (error && error != EEXIST) {
+        VLOG_WARN("%s: UFID feature probe failed (%s).",
+                  dpif_name(dpif_), ovs_strerror(error));
+        goto done;
+    }
+
+    error = dpif_netlink_flow_get__(dpif, NULL, 0, &ufid, true, &reply,
+                                    &replybuf);
+    if (!error && reply.ufid_present && ovs_u128_equal(&ufid, &reply.ufid)) {
+        enable_ufid = true;
+    }
+    ofpbuf_delete(replybuf);
+
+    error = dpif_netlink_flow_del(dpif, ofpbuf_data(&key), ofpbuf_size(&key),
+                                  &ufid, false);
+    if (error) {
+        VLOG_WARN("%s: failed to delete UFID feature probe flow",
+                  dpif_name(dpif_));
+    }
+
+done:
+    if (enable_ufid) {
+        VLOG_INFO("%s: Datapath supports userspace flow ids",
+                  dpif_name(dpif_));
+    } else {
+        VLOG_INFO("%s: Datapath does not support userspace flow ids",
+                  dpif_name(dpif_));
+    }
+
+    return enable_ufid;
+}
+
 /* Synchronizes 'channels' in 'dpif->handlers'  with the set of vports
  * currently in 'dpif' in the kernel, by adding a new set of channels for
  * any kernel vport that lacks one and deleting any channels that have no
@@ -2602,16 +2734,19 @@ static int
 dpif_netlink_flow_from_ofpbuf(struct dpif_netlink_flow *flow,
                               const struct ofpbuf *buf)
 {
-    static const struct nl_policy ovs_flow_policy[] = {
-        [OVS_FLOW_ATTR_KEY] = { .type = NL_A_NESTED },
+    static const struct nl_policy ovs_flow_policy[__OVS_FLOW_ATTR_MAX] = {
+        [OVS_FLOW_ATTR_KEY] = { .type = NL_A_NESTED, .optional = true },
         [OVS_FLOW_ATTR_MASK] = { .type = NL_A_NESTED, .optional = true },
         [OVS_FLOW_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
         [OVS_FLOW_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_flow_stats),
                                   .optional = true },
         [OVS_FLOW_ATTR_TCP_FLAGS] = { .type = NL_A_U8, .optional = true },
         [OVS_FLOW_ATTR_USED] = { .type = NL_A_U64, .optional = true },
+        [OVS_FLOW_ATTR_UFID] = { .type = NL_A_UNSPEC, .optional = true,
+                                 .min_len = sizeof(ovs_u128) },
         /* The kernel never uses OVS_FLOW_ATTR_CLEAR. */
         /* The kernel never uses OVS_FLOW_ATTR_PROBE. */
+        /* The kernel never uses OVS_FLOW_ATTR_UFID_FLAGS. */
     };
 
     struct nlattr *a[ARRAY_SIZE(ovs_flow_policy)];
@@ -2632,12 +2767,25 @@ dpif_netlink_flow_from_ofpbuf(struct dpif_netlink_flow *flow,
                             ARRAY_SIZE(ovs_flow_policy))) {
         return EINVAL;
     }
+    if (!a[OVS_FLOW_ATTR_KEY] && !a[OVS_FLOW_ATTR_UFID]) {
+        return EINVAL;
+    }
 
     flow->nlmsg_flags = nlmsg->nlmsg_flags;
     flow->dp_ifindex = ovs_header->dp_ifindex;
-    flow->key = nl_attr_get(a[OVS_FLOW_ATTR_KEY]);
-    flow->key_len = nl_attr_get_size(a[OVS_FLOW_ATTR_KEY]);
+    if (a[OVS_FLOW_ATTR_KEY]) {
+        flow->key = nl_attr_get(a[OVS_FLOW_ATTR_KEY]);
+        flow->key_len = nl_attr_get_size(a[OVS_FLOW_ATTR_KEY]);
+    }
 
+    if (a[OVS_FLOW_ATTR_UFID]) {
+        const ovs_u128 *ufid;
+
+        ufid = nl_attr_get_unspec(a[OVS_FLOW_ATTR_UFID],
+                                  nl_attr_get_size(a[OVS_FLOW_ATTR_UFID]));
+        flow->ufid = *ufid;
+        flow->ufid_present = true;
+    }
     if (a[OVS_FLOW_ATTR_MASK]) {
         flow->mask = nl_attr_get(a[OVS_FLOW_ATTR_MASK]);
         flow->mask_len = nl_attr_get_size(a[OVS_FLOW_ATTR_MASK]);
@@ -2673,6 +2821,15 @@ dpif_netlink_flow_to_ofpbuf(const struct dpif_netlink_flow *flow,
     ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header);
     ovs_header->dp_ifindex = flow->dp_ifindex;
 
+    if (flow->ufid_present) {
+        nl_msg_put_unspec(buf, OVS_FLOW_ATTR_UFID, &flow->ufid,
+                          sizeof flow->ufid);
+    }
+    if (flow->ufid_terse) {
+        nl_msg_put_u32(buf, OVS_FLOW_ATTR_UFID_FLAGS,
+                       OVS_UFID_F_OMIT_KEY | OVS_UFID_F_OMIT_MASK
+                       | OVS_UFID_F_OMIT_ACTIONS);
+    }
     if (flow->key_len) {
         nl_msg_put_unspec(buf, OVS_FLOW_ATTR_KEY, flow->key, flow->key_len);
     }
index 36c7d91dc0ac90125b518cb9dc33a0604f0fe305..133cf3779a8e99f589a7e5e99ca34b74fc0d4944 100644 (file)
@@ -91,6 +91,7 @@ static void log_flow_message(const struct dpif *dpif, int error,
                              const char *operation,
                              const struct nlattr *key, size_t key_len,
                              const struct nlattr *mask, size_t mask_len,
+                             const ovs_u128 *ufid,
                              const struct dpif_flow_stats *stats,
                              const struct nlattr *actions, size_t actions_len);
 static void log_operation(const struct dpif *, const char *operation,
@@ -862,7 +863,7 @@ dpif_flow_flush(struct dpif *dpif)
 /* A dpif_operate() wrapper for performing a single DPIF_OP_FLOW_GET. */
 int
 dpif_flow_get(struct dpif *dpif,
-              const struct nlattr *key, size_t key_len,
+              const struct nlattr *key, size_t key_len, const ovs_u128 *ufid,
               struct ofpbuf *buf, struct dpif_flow *flow)
 {
     struct dpif_op *opp;
@@ -871,10 +872,14 @@ dpif_flow_get(struct dpif *dpif,
     op.type = DPIF_OP_FLOW_GET;
     op.u.flow_get.key = key;
     op.u.flow_get.key_len = key_len;
+    op.u.flow_get.ufid = ufid;
     op.u.flow_get.buffer = buf;
+
+    memset(flow, 0, sizeof *flow);
     op.u.flow_get.flow = flow;
     op.u.flow_get.flow->key = key;
     op.u.flow_get.flow->key_len = key_len;
+    op.u.flow_get.flow->ufid = *ufid;
 
     opp = &op;
     dpif_operate(dpif, &opp, 1);
@@ -888,7 +893,7 @@ dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
               const struct nlattr *key, size_t key_len,
               const struct nlattr *mask, size_t mask_len,
               const struct nlattr *actions, size_t actions_len,
-              struct dpif_flow_stats *stats)
+              const ovs_u128 *ufid, struct dpif_flow_stats *stats)
 {
     struct dpif_op *opp;
     struct dpif_op op;
@@ -901,6 +906,7 @@ dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
     op.u.flow_put.mask_len = mask_len;
     op.u.flow_put.actions = actions;
     op.u.flow_put.actions_len = actions_len;
+    op.u.flow_put.ufid = ufid;
     op.u.flow_put.stats = stats;
 
     opp = &op;
@@ -912,7 +918,7 @@ dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
 /* A dpif_operate() wrapper for performing a single DPIF_OP_FLOW_DEL. */
 int
 dpif_flow_del(struct dpif *dpif,
-              const struct nlattr *key, size_t key_len,
+              const struct nlattr *key, size_t key_len, const ovs_u128 *ufid,
               struct dpif_flow_stats *stats)
 {
     struct dpif_op *opp;
@@ -921,6 +927,7 @@ dpif_flow_del(struct dpif *dpif,
     op.type = DPIF_OP_FLOW_DEL;
     op.u.flow_del.key = key;
     op.u.flow_del.key_len = key_len;
+    op.u.flow_del.ufid = ufid;
     op.u.flow_del.stats = stats;
 
     opp = &op;
@@ -1002,7 +1009,7 @@ dpif_flow_dump_next(struct dpif_flow_dump_thread *thread,
         for (f = flows; f < &flows[n] && should_log_flow_message(0); f++) {
             log_flow_message(dpif, 0, "flow_dump",
                              f->key, f->key_len, f->mask, f->mask_len,
-                             &f->stats, f->actions, f->actions_len);
+                             &f->ufid, &f->stats, f->actions, f->actions_len);
         }
     } else {
         VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all flows", dpif_name(dpif));
@@ -1522,7 +1529,7 @@ static void
 log_flow_message(const struct dpif *dpif, int error, const char *operation,
                  const struct nlattr *key, size_t key_len,
                  const struct nlattr *mask, size_t mask_len,
-                 const struct dpif_flow_stats *stats,
+                 const ovs_u128 *ufid, const struct dpif_flow_stats *stats,
                  const struct nlattr *actions, size_t actions_len)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
@@ -1534,6 +1541,10 @@ log_flow_message(const struct dpif *dpif, int error, const char *operation,
     if (error) {
         ds_put_format(&ds, "(%s) ", ovs_strerror(error));
     }
+    if (ufid) {
+        odp_format_ufid(ufid, &ds);
+        ds_put_cstr(&ds, " ");
+    }
     odp_flow_format(key, key_len, mask, mask_len, NULL, &ds, true);
     if (stats) {
         ds_put_cstr(&ds, ", ");
@@ -1567,7 +1578,8 @@ log_flow_put_message(struct dpif *dpif, const struct dpif_flow_put *put,
         }
         log_flow_message(dpif, error, ds_cstr(&s),
                          put->key, put->key_len, put->mask, put->mask_len,
-                         put->stats, put->actions, put->actions_len);
+                         put->ufid, put->stats, put->actions,
+                         put->actions_len);
         ds_destroy(&s);
     }
 }
@@ -1578,7 +1590,8 @@ log_flow_del_message(struct dpif *dpif, const struct dpif_flow_del *del,
 {
     if (should_log_flow_message(error)) {
         log_flow_message(dpif, error, "flow_del", del->key, del->key_len,
-                         NULL, 0, !error ? del->stats : NULL, NULL, 0);
+                         NULL, 0, del->ufid, !error ? del->stats : NULL,
+                         NULL, 0);
     }
 }
 
@@ -1634,7 +1647,7 @@ log_flow_get_message(const struct dpif *dpif, const struct dpif_flow_get *get,
         log_flow_message(dpif, error, "flow_get",
                          get->key, get->key_len,
                          get->flow->mask, get->flow->mask_len,
-                         &get->flow->stats,
+                         get->ufid, &get->flow->stats,
                          get->flow->actions, get->flow->actions_len);
     }
 }
index 9e49b20403b729bb12b377618d168ac22a3aac55..36fdda3185f62120df51280af3183afcf45821f3 100644 (file)
@@ -522,12 +522,14 @@ int dpif_flow_put(struct dpif *, enum dpif_flow_put_flags,
                   const struct nlattr *key, size_t key_len,
                   const struct nlattr *mask, size_t mask_len,
                   const struct nlattr *actions, size_t actions_len,
-                  struct dpif_flow_stats *);
+                  const ovs_u128 *ufid, struct dpif_flow_stats *);
+
 int dpif_flow_del(struct dpif *,
                   const struct nlattr *key, size_t key_len,
-                  struct dpif_flow_stats *);
+                  const ovs_u128 *ufid, struct dpif_flow_stats *);
 int dpif_flow_get(struct dpif *,
                   const struct nlattr *key, size_t key_len,
+                  const ovs_u128 *ufid,
                   struct ofpbuf *, struct dpif_flow *);
 \f
 /* Flow dumping interface
@@ -574,6 +576,7 @@ struct dpif_flow {
     const struct nlattr *actions; /* Actions, as OVS_ACTION_ATTR_ */
     size_t actions_len;           /* 'actions' length in bytes. */
     ovs_u128 ufid;                /* Unique flow identifier. */
+    bool ufid_present;            /* True if 'ufid' was provided by datapath.*/
     struct dpif_flow_stats stats; /* Flow statistics. */
 };
 int dpif_flow_dump_next(struct dpif_flow_dump_thread *,
@@ -625,6 +628,7 @@ struct dpif_flow_put {
     size_t mask_len;                /* Length of 'mask' in bytes. */
     const struct nlattr *actions;   /* Actions to perform on flow. */
     size_t actions_len;             /* Length of 'actions' in bytes. */
+    const ovs_u128 *ufid;           /* Optional unique flow identifier. */
 
     /* Output. */
     struct dpif_flow_stats *stats;  /* Optional flow statistics. */
@@ -633,8 +637,14 @@ struct dpif_flow_put {
 /* Delete a flow.
  *
  * The flow is specified by the Netlink attributes with types OVS_KEY_ATTR_* in
- * the 'key_len' bytes starting at 'key'.  Succeeds with status 0 if the flow
- * is deleted, or fails with ENOENT if the dpif does not contain such a flow.
+ * the 'key_len' bytes starting at 'key', or the unique identifier 'ufid'. If
+ * the flow was created using 'ufid', then 'ufid' must be specified to delete
+ * the flow. If both are specified, 'key' will be ignored for flow deletion.
+ * Succeeds with status 0 if the flow is deleted, or fails with ENOENT if the
+ * dpif does not contain such a flow.
+ *
+ * Callers should always provide the 'key' to improve dpif logging in the event
+ * of errors or unexpected behaviour.
  *
  * If the operation succeeds, then 'stats', if nonnull, will be set to the
  * flow's statistics before its deletion. */
@@ -642,6 +652,7 @@ struct dpif_flow_del {
     /* Input. */
     const struct nlattr *key;       /* Flow to delete. */
     size_t key_len;                 /* Length of 'key' in bytes. */
+    const ovs_u128 *ufid;           /* UID of flow to delete. */
 
     /* Output. */
     struct dpif_flow_stats *stats;  /* Optional flow statistics. */
@@ -676,8 +687,11 @@ struct dpif_execute {
 /* Queries the dpif for a flow entry.
  *
  * The flow is specified by the Netlink attributes with types OVS_KEY_ATTR_* in
- * the 'key_len' bytes starting at 'key'. 'buffer' must point to an initialized
- * buffer, with a recommended size of DPIF_FLOW_BUFSIZE bytes.
+ * the 'key_len' bytes starting at 'key', or the unique identifier 'ufid'. If
+ * the flow was created using 'ufid', then 'ufid' must be specified to fetch
+ * the flow. If both are specified, 'key' will be ignored for the flow query.
+ * 'buffer' must point to an initialized buffer, with a recommended size of
+ * DPIF_FLOW_BUFSIZE bytes.
  *
  * On success, 'flow' will be populated with the mask, actions and stats for
  * the datapath flow corresponding to 'key'. The mask and actions may point
@@ -685,6 +699,9 @@ struct dpif_execute {
  * that wish to hold these over quiescent periods must make a copy of these
  * fields before quiescing.
  *
+ * Callers should always provide 'key' to improve dpif logging in the event of
+ * errors or unexpected behaviour.
+ *
  * Succeeds with status 0 if the flow is fetched, or fails with ENOENT if no
  * such flow exists. Other failures are indicated with a positive errno value.
  */
@@ -692,6 +709,7 @@ struct dpif_flow_get {
     /* Input. */
     const struct nlattr *key;       /* Flow to get. */
     size_t key_len;                 /* Length of 'key' in bytes. */
+    const ovs_u128 *ufid;            /* UID of flow to get. */
     struct ofpbuf *buffer;          /* Storage for output parameters. */
 
     /* Output. */
index 633919a391f5f79191aa3b2f1a86bac00ca6e258..a89d5f8bd35d928c6bd268bafb95b552a947bf8c 100644 (file)
@@ -1999,6 +1999,13 @@ generate_all_wildcard_mask(struct ofpbuf *ofp, const struct nlattr *key)
     return ofpbuf_base(ofp);
 }
 
+void
+odp_format_ufid(const ovs_u128 *ufid, struct ds *ds)
+{
+    ds_put_format(ds, "ufid:%016"PRIx64"%016"PRIx64, ufid->u64.lo,
+                  ufid->u64.hi);
+}
+
 /* Appends to 'ds' a string representation of the 'key_len' bytes of
  * OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
  * 'mask_len' bytes of 'mask' which apply to 'key'. If 'portno_names' is
index 00dbf7b8cc455746a62ec13e10f08c8618b7243d..27a5ca7fe985c672bee31645e1fb105c2af06387 100644 (file)
@@ -145,6 +145,7 @@ struct odputil_keybuf {
 enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *,
                                            struct flow_tnl *);
 
+void odp_format_ufid(const ovs_u128 *ufid, struct ds *);
 void odp_flow_format(const struct nlattr *key, size_t key_len,
                      const struct nlattr *mask, size_t mask_len,
                      const struct hmap *portno_names, struct ds *,
index 0b1b597dec69bb8d59ac5d39853037eff5bc2962..526bb3744c0b6ca9c6a86bee57570cddccd75836 100644 (file)
@@ -208,6 +208,7 @@ struct udpif_key {
     size_t mask_len;               /* Length of 'mask'. */
     struct ofpbuf *actions;        /* Datapath flow actions as nlattrs. */
     ovs_u128 ufid;                 /* Unique flow identifier. */
+    bool ufid_present;             /* True if 'ufid' is in datapath. */
     uint32_t hash;                 /* Pre-computed hash for 'key'. */
 
     struct ovs_mutex mutex;                   /* Guards the following. */
@@ -645,7 +646,8 @@ recv_upcalls(struct handler *handler)
                  * while traffic is being received.  Print a rate-limited
                  * message in case it happens frequently. */
                 dpif_flow_put(udpif->dpif, DPIF_FP_CREATE, dupcall->key,
-                              dupcall->key_len, NULL, 0, NULL, 0, NULL);
+                              dupcall->key_len, NULL, 0, NULL, 0,
+                              &dupcall->ufid, NULL);
                 VLOG_INFO_RL(&rl, "received packet on unassociated datapath "
                              "port %"PRIu32, flow->in_port.odp_port);
             }
@@ -1167,6 +1169,7 @@ handle_upcalls(struct udpif *udpif, struct upcall *upcalls,
             op->dop.u.flow_put.key_len = ukey->key_len;
             op->dop.u.flow_put.mask = ukey->mask;
             op->dop.u.flow_put.mask_len = ukey->mask_len;
+            op->dop.u.flow_put.ufid = upcall->ufid;
             op->dop.u.flow_put.stats = NULL;
             op->dop.u.flow_put.actions = ofpbuf_data(ukey->actions);
             op->dop.u.flow_put.actions_len = ofpbuf_size(ukey->actions);
@@ -1238,7 +1241,8 @@ ukey_lookup(struct udpif *udpif, const ovs_u128 *ufid)
 static struct udpif_key *
 ukey_create__(const struct nlattr *key, size_t key_len,
               const struct nlattr *mask, size_t mask_len,
-              const ovs_u128 *ufid, const struct ofpbuf *actions,
+              bool ufid_present, const ovs_u128 *ufid,
+              const struct ofpbuf *actions,
               uint64_t dump_seq, uint64_t reval_seq, long long int used)
     OVS_NO_THREAD_SAFETY_ANALYSIS
 {
@@ -1250,6 +1254,7 @@ ukey_create__(const struct nlattr *key, size_t key_len,
     memcpy(&ukey->maskbuf, mask, mask_len);
     ukey->mask = &ukey->maskbuf.nla;
     ukey->mask_len = mask_len;
+    ukey->ufid_present = ufid_present;
     ukey->ufid = *ufid;
     ukey->hash = get_ufid_hash(&ukey->ufid);
     ukey->actions = ofpbuf_clone(actions);
@@ -1296,8 +1301,8 @@ ukey_create_from_upcall(const struct upcall *upcall)
 
     return ukey_create__(ofpbuf_data(&keybuf), ofpbuf_size(&keybuf),
                          ofpbuf_data(&maskbuf), ofpbuf_size(&maskbuf),
-                         upcall->ufid, &upcall->put_actions, upcall->dump_seq,
-                         upcall->reval_seq, 0);
+                         true, upcall->ufid, &upcall->put_actions,
+                         upcall->dump_seq, upcall->reval_seq, 0);
 }
 
 static struct udpif_key *
@@ -1311,8 +1316,9 @@ ukey_create_from_dpif_flow(const struct udpif *udpif,
     reval_seq = seq_read(udpif->reval_seq);
     ofpbuf_use_const(&actions, &flow->actions, flow->actions_len);
     return ukey_create__(flow->key, flow->key_len,
-                         flow->mask, flow->mask_len, &flow->ufid, &actions,
-                         dump_seq, reval_seq, flow->stats.used);
+                         flow->mask, flow->mask_len, flow->ufid_present,
+                         &flow->ufid, &actions, dump_seq, reval_seq,
+                         flow->stats.used);
 }
 
 /* Attempts to insert a ukey into the shared ukey maps.
@@ -1340,8 +1346,12 @@ ukey_install_start(struct udpif *udpif, struct udpif_key *new_ukey)
         } else {
             struct ds ds = DS_EMPTY_INITIALIZER;
 
+            odp_format_ufid(&old_ukey->ufid, &ds);
+            ds_put_cstr(&ds, " ");
             odp_flow_key_format(old_ukey->key, old_ukey->key_len, &ds);
             ds_put_cstr(&ds, "\n");
+            odp_format_ufid(&new_ukey->ufid, &ds);
+            ds_put_cstr(&ds, " ");
             odp_flow_key_format(new_ukey->key, new_ukey->key_len, &ds);
 
             VLOG_WARN_RL(&rl, "Conflicting ukey for flows:\n%s", ds_cstr(&ds));
@@ -1629,6 +1639,7 @@ delete_op_init(struct ukey_op *op, struct udpif_key *ukey)
     op->dop.type = DPIF_OP_FLOW_DEL;
     op->dop.u.flow_del.key = ukey->key;
     op->dop.u.flow_del.key_len = ukey->key_len;
+    op->dop.u.flow_del.ufid = ukey->ufid_present ? &ukey->ufid : NULL;
     op->dop.u.flow_del.stats = &op->stats;
 }
 
index c8e11c21e034017e11c065dd58f241a16ca5bb2a..a16d6cb25f77e37cfb3cab6eb940319c4b88e636 100644 (file)
@@ -1021,7 +1021,7 @@ check_recirc(struct dpif_backer *backer)
 
     error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_PROBE,
                           ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, NULL,
-                          0, NULL);
+                          0, NULL, NULL);
     if (error && error != EEXIST) {
         if (error != EINVAL) {
             VLOG_WARN("%s: Reciculation flow probe failed (%s)",
@@ -1031,7 +1031,7 @@ check_recirc(struct dpif_backer *backer)
     }
 
     error = dpif_flow_del(backer->dpif, ofpbuf_data(&key), ofpbuf_size(&key),
-                          NULL);
+                          NULL, NULL);
     if (error) {
         VLOG_WARN("%s: failed to delete recirculation feature probe flow",
                   dpif_name(backer->dpif));
@@ -1150,7 +1150,7 @@ check_max_mpls_depth(struct dpif_backer *backer)
 
         error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_PROBE,
                               ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0,
-                              NULL, 0, NULL);
+                              NULL, 0, NULL, NULL);
         if (error && error != EEXIST) {
             if (error != EINVAL) {
                 VLOG_WARN("%s: MPLS stack length feature probe failed (%s)",
@@ -1160,7 +1160,7 @@ check_max_mpls_depth(struct dpif_backer *backer)
         }
 
         error = dpif_flow_del(backer->dpif, ofpbuf_data(&key),
-                              ofpbuf_size(&key), NULL);
+                              ofpbuf_size(&key), NULL, NULL);
         if (error) {
             VLOG_WARN("%s: failed to delete MPLS feature probe flow",
                       dpif_name(backer->dpif));
@@ -5001,6 +5001,10 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
             continue;
         }
 
+        if (verbosity) {
+            odp_format_ufid(&f.ufid, &ds);
+            ds_put_cstr(&ds, " ");
+        }
         odp_flow_format(f.key, f.key_len, f.mask, f.mask_len,
                         &portno_names, &ds, verbosity);
         ds_put_cstr(&ds, ", ");
index b68e43bc52be1f6786a561cb5bfedfc1eee444c3..69903cb3329b24f57d2c327cae500b5ab08394a0 100644 (file)
@@ -3,6 +3,7 @@ AT_BANNER([dpif-netdev])
 # Strips out uninteresting parts of flow output, as well as parts
 # that vary from one run to another (e.g., timing and bond actions).
 m4_define([STRIP_XOUT], [[sed '
+    s/ufid:[0-9a-f]* //
     s/used:[0-9]*\.[0-9]*/used:0.0/
     s/actions:.*/actions: <del>/
     s/packets:[0-9]*/packets:0/
index 5116107a8a8f3d1a846276f79217337b2053c94d..c78b6aa7a5e6cd9f6ee40b89c8fdcd69aa9defdf 100644 (file)
@@ -5061,21 +5061,21 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:
 AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 ovs-appctl revalidator/wait
-AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_UFID | STRIP_USED | sort], [0], [dnl
 recirc_id(0),in_port(1),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop
 recirc_id(0),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop
 ])
 
-AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | STRIP_UFID | STRIP_USED | sort], [0], [dnl
 recirc_id(0),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop
 ])
 
-AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | sort | STRIP_USED], [0], [dnl
+AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | STRIP_UFID | STRIP_USED | sort], [0], [dnl
 skb_priority(0/0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
 skb_priority(0/0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
 ])
 
-AT_CHECK([ovs-appctl dpif/dump-flows -m br1 | sort | STRIP_USED], [0], [dnl
+AT_CHECK([ovs-appctl dpif/dump-flows -m br1 | STRIP_UFID | STRIP_USED | sort], [0], [dnl
 skb_priority(0/0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
 ])
 
@@ -5109,7 +5109,7 @@ for dl_src in 00 01; do
     AT_CHECK([ovs-appctl netdev-dummy/receive p1 "505400000007 6066666666$dl_src 8847 00014020 00014120 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45"])
 done
 sleep 1  # wait for the datapath flow installed
-AT_CHECK_UNQUOTED([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
+AT_CHECK_UNQUOTED([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
 recirc_id=0,mpls,in_port=1,dl_src=60:66:66:66:66:00,mpls_label=20,mpls_tc=0,mpls_ttl=32,mpls_bos=0,mpls_lse1=82208, actions:userspace(pid=0,slow_path(controller))
 recirc_id=0,mpls,in_port=1,dl_src=60:66:66:66:66:01,mpls_bos=0,mpls_lse1=82208, actions:userspace(pid=0,slow_path(controller))
 ])
@@ -5148,7 +5148,7 @@ for dl_src in 00 01; do
     AT_CHECK([ovs-appctl netdev-dummy/receive p1 "505400000007 6066666666$dl_src 8847 00014020 00014120 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45"])
 done
 sleep 1  # wait for the datapath flow installed
-AT_CHECK_UNQUOTED([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
+AT_CHECK_UNQUOTED([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
 recirc_id=0,mpls,in_port=1,dl_src=60:66:66:66:66:00,mpls_label=20,mpls_tc=0,mpls_ttl=32,mpls_bos=0,mpls_lse1=82208, actions:userspace(pid=0,slow_path(controller))
 recirc_id=0,mpls,in_port=1,dl_src=60:66:66:66:66:01,mpls_bos=0,mpls_lse1=82208, actions:userspace(pid=0,slow_path(controller))
 ])
@@ -5202,15 +5202,15 @@ dummy@ovs-dummy: hit:13 missed:2
                pbr1 1/none: (patch: peer=pbr0)
 ])
 
-AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
+AT_CHECK([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
 recirc_id=0,ip,in_port=100,nw_frag=no, actions:101,3,2
 recirc_id=0,ip,in_port=101,nw_frag=no, actions:100,2,3
 ])
 
-AT_CHECK([cat ovs-vswitchd.log | grep -e 'in_port(100).*packets:9' | FILTER_FLOW_DUMP], [0], [dnl
+AT_CHECK([cat ovs-vswitchd.log | grep -e 'in_port(100).*packets:9' | STRIP_UFID | FILTER_FLOW_DUMP], [0], [dnl
 skb_priority(0/0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(100),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:9, bytes:540, used:0.0s, actions:101,3,2
 ])
-AT_CHECK([cat ovs-vswitchd.log | grep -e 'in_port(101).*packets:4' | FILTER_FLOW_DUMP], [0], [dnl
+AT_CHECK([cat ovs-vswitchd.log | grep -e 'in_port(101).*packets:4' | STRIP_UFID | FILTER_FLOW_DUMP], [0], [dnl
 skb_priority(0/0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(101),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:4, bytes:240, used:0.0s, actions:100,2,3
 ])
 
@@ -5723,7 +5723,7 @@ sleep 1
 dnl The first packet is essentially a no-op, as the new destination MAC is the
 dnl same as the original.  The second entry actually updates the destination
 dnl MAC.
-AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
+AT_CHECK([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
 recirc_id=0,ip,in_port=1,dl_dst=50:54:00:00:00:0a,nw_frag=no, actions:2
 recirc_id=0,ip,in_port=1,dl_dst=50:54:00:00:00:0c,nw_frag=no, actions:set(eth(dst=50:54:00:00:00:0a)),2
 ])
@@ -5750,11 +5750,11 @@ for i in 1 2 3 4; do
     fi
 done
 sleep 1
-AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
+AT_CHECK([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
 pkt_mark=0,recirc_id=0,skb_priority=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0, actions:2
 pkt_mark=0,recirc_id=0,skb_priority=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0, actions:drop
 ])
-AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_DUMP | grep 'packets:3'], [0], [dnl
+AT_CHECK([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_DUMP | grep 'packets:3'], [0], [dnl
 skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:180, used:0.0s, actions:2
 skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:180, used:0.0s, actions:drop
 ])
index f10304c8d824cddc686eb2c185295040aa75705a..cbf0ad679c40d2cdc4d4c556601274980dad82d5 100644 (file)
@@ -36,6 +36,7 @@ m4_divert_pop([PREPARE_TESTS])
 m4_define([STRIP_XIDS], [[sed 's/ (xid=0x[0-9a-fA-F]*)//']])
 m4_define([STRIP_DURATION], [[sed 's/\bduration=[0-9.]*s/duration=?s/']])
 m4_define([STRIP_USED], [[sed 's/used:[0-9]\.[0-9]*/used:0.0/']])
+m4_define([STRIP_UFID], [[sed 's/ufid:[0-9a-f]* //']])
 m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m'])
 
 # OVS_VSWITCHD_START([vsctl-args], [vsctl-output], [=override])