Upcall tracing is implemented using 3 objects.

- upcall_tracing: Stores the current configuration provided by the user
  (e.g: filter), and a fixed-size list of "upcall_trace_block"s.
- upcall_trace_block: Groups several "upcall_trace" objects together
  based on a "trace_id".
- upcall_trace: Wraps a list of oftrace_node (the same current
  "ofproto/trace" uses)

Upcall trace_id and trace grouping:
Traces are grouped together based on their trace_id. When an upcall
comes in with recirc_id = 0 (and the filter is matched) a new trace_id is
allocated for it. This trace_id is then preserved in the frozen state so
when if it's recirculated, the next upcall will inherit the trace_id
from the original.

Ownership model:
upcall_trace objects are reference-counted. The block it's linked to
holds a reference and the upcall that adds oftrace_nodes to it holds
another. If the block gets evicted while the trace is being used by an
upcall, all the information will be dropped when the upcall ends.

New unixctl_conn commands allow listing, printing and flushing trace
groups.

Signed-off-by: Adrian Moreno <[email protected]>
---
 ofproto/ofproto-dpif-rid.h          |   1 +
 ofproto/ofproto-dpif-trace.c        |   4 +-
 ofproto/ofproto-dpif-trace.h        |   4 +-
 ofproto/ofproto-dpif-upcall-trace.c | 352 ++++++++++++++++++++++++++++
 ofproto/ofproto-dpif-upcall-trace.h |  23 ++
 ofproto/ofproto-dpif-upcall.c       | 122 ++++++++++
 ofproto/ofproto-dpif-xlate.c        |   5 +-
 ofproto/ofproto-dpif-xlate.h        |   4 +
 8 files changed, 511 insertions(+), 4 deletions(-)

diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h
index 21f32b935..220d59dac 100644
--- a/ofproto/ofproto-dpif-rid.h
+++ b/ofproto/ofproto-dpif-rid.h
@@ -155,6 +155,7 @@ struct frozen_state {
     bool conntracked;             /* Conntrack occurred prior to freeze. */
     bool was_mpls;                /* MPLS packet */
     struct uuid xport_uuid;       /* UUID of 1st port packet received on. */
+    uint64_t trace_id;            /* Non-zero if selected for tracing. */
 
     /* Actions to be translated when thawing. */
     struct ofpact *ofpacts;
diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
index e43d9f88c..a5710b267 100644
--- a/ofproto/ofproto-dpif-trace.c
+++ b/ofproto/ofproto-dpif-trace.c
@@ -61,7 +61,7 @@ oftrace_node_type_is_terminal(enum oftrace_node_type type)
     OVS_NOT_REACHED();
 }
 
-static void
+void
 oftrace_node_list_destroy(struct ovs_list *nodes)
 {
     if (nodes) {
@@ -140,7 +140,7 @@ oftrace_pop_ct_state(struct ovs_list *next_ct_states)
     OVS_NOT_REACHED();
 }
 
-static void
+void
 oftrace_node_print_details(struct ds *output,
                            const struct ovs_list *nodes, int level)
 {
diff --git a/ofproto/ofproto-dpif-trace.h b/ofproto/ofproto-dpif-trace.h
index f023b10cd..c11236e13 100644
--- a/ofproto/ofproto-dpif-trace.h
+++ b/ofproto/ofproto-dpif-trace.h
@@ -98,5 +98,7 @@ bool oftrace_add_recirc_node(struct ovs_list *recirc_queue,
                              const uint16_t zone);
 
 void ofproto_append_ports_to_map(struct ofputil_port_map *, struct hmap ports);
-
+void oftrace_node_list_destroy(struct ovs_list *nodes);
+void oftrace_node_print_details(struct ds *, const struct ovs_list *nodes,
+                                int level);
 #endif /* ofproto-dpif-trace.h */
diff --git a/ofproto/ofproto-dpif-upcall-trace.c 
b/ofproto/ofproto-dpif-upcall-trace.c
index 94f158cf9..e1b7382a6 100644
--- a/ofproto/ofproto-dpif-upcall-trace.c
+++ b/ofproto/ofproto-dpif-upcall-trace.c
@@ -2,14 +2,182 @@
 
 #include "ofproto-dpif-upcall-trace.h"
 
+#include "ofproto/ofproto-dpif-xlate.h"
+#include "ofproto/ofproto-dpif-trace.h"
 #include <openvswitch/dynamic-string.h>
 #include <openvswitch/ofp-flow.h>
 #include <openvswitch/ofp-port.h>
 #include <openvswitch/vlog.h>
 #include "ofproto-dpif.h"
 
+
 VLOG_DEFINE_THIS_MODULE(upcall_trace);
 
+struct upcall_trace_block;
+struct upcall_trace;
+
+static bool upcall_tracing_matches(const struct upcall_tracing *,
+                                   const struct flow *,
+                                   const ofp_port_t *);
+
+/* upcall_trace - A single xlate result (one recirculation pass).
+ *
+ * An upcall_trace can be listed inside an "upcall_trace_block" and/or
+ * referenced by an in-flight upcall. "refcount" is used to track these two
+ * references.
+ *
+ * "list_node" should only be accessed through the associated
+ * "upcall_trace_block" as it will get invalidated if removed from the block
+ * list. "upcall"s can take pointer to the internal "xtrace_node" and add nodes
+ * to the list.
+ */
+struct upcall_trace {
+    struct ovs_list list_node;      /* In upcall_upcall_trace_block->traces. */
+    struct ovs_refcount refcount;
+
+    long long int timestamp_msec;
+    uint32_t recirc_id;
+    uint64_t trace_id;
+    struct flow initial_flow;       /* Flow at start of this xlate. */
+
+    /* The oftrace_node tree - moved from upcall_trace. */
+    struct ovs_list xtrace_nodes;   /* List of struct xtrace_node. */
+};
+
+static struct upcall_trace *
+upcall_trace_create(uint32_t recirc_id,
+                    uint64_t trace_id,
+                   const struct flow *flow)
+{
+    struct upcall_trace *trace = xzalloc(sizeof *trace);
+    trace->recirc_id = recirc_id;
+    trace->trace_id = trace_id;
+    trace->timestamp_msec = time_wall_msec();
+    trace->initial_flow = *flow;
+    ovs_list_init(&trace->xtrace_nodes);
+    ovs_refcount_init(&trace->refcount);
+    return trace;
+}
+
+static void
+upcall_trace_destroy(struct upcall_trace *trace)
+{
+    if (trace) {
+        oftrace_node_list_destroy(&trace->xtrace_nodes);
+        free(trace);
+    }
+}
+
+static struct upcall_trace *
+upcall_trace_ref(const struct upcall_trace *trace_)
+{
+    struct upcall_trace *trace = CONST_CAST(struct upcall_trace *, trace_);
+    if (trace) {
+        ovs_refcount_ref(&trace->refcount);
+    }
+    return trace;
+}
+
+void
+upcall_trace_unref(struct upcall_trace *trace)
+{
+    if (trace && ovs_refcount_unref(&trace->refcount) == 1) {
+        upcall_trace_destroy(trace);
+    }
+}
+
+uint64_t
+upcall_trace_get_trace_id(const struct upcall_trace *utrace)
+{
+    return utrace ? utrace->trace_id : 0;
+}
+
+struct ovs_list *
+upcall_trace_xlate_start(struct upcall_trace *utrace,
+                         struct xlate_in *xin)
+{
+    if (OVS_UNLIKELY(utrace)) {
+        /* Copy initial flow - it may change during xlate. */
+        utrace->initial_flow = xin->flow;
+        utrace->recirc_id = xin->flow.recirc_id;
+        if (!ovs_list_is_empty(&utrace->xtrace_nodes)) {
+            VLOG_ERR("Started new xlate tracing without consuming previous");
+            oftrace_node_list_destroy(&utrace->xtrace_nodes);
+            ovs_list_init(&utrace->xtrace_nodes);
+        }
+        return &utrace->xtrace_nodes;
+    }
+    return NULL;
+}
+
+/* upcall_trace_block - A group of upcall_traces with the same trace_id. */
+struct upcall_trace_block {
+    struct ovs_list list_node;      /* In upcall_tracing->blocks. */
+    uint64_t trace_id;
+    struct ovs_list traces;          /* List of struct upcall_trace. */
+};
+
+static struct upcall_trace_block *
+upcall_trace_block_create(uint64_t trace_id)
+{
+    struct upcall_trace_block *block = xzalloc(sizeof *block);
+    block->trace_id = trace_id;
+    ovs_list_init(&block->traces);
+    return block;
+}
+
+static void
+upcall_trace_block_destroy(struct upcall_trace_block *block)
+{
+    if (block) {
+        struct upcall_trace *trace;
+        LIST_FOR_EACH_POP (trace, list_node, &block->traces) {
+            upcall_trace_unref(trace);
+        }
+        free(block);
+    }
+}
+
+static void
+upcall_trace_block_format_short(const struct upcall_trace_block *block,
+                                struct ds *output)
+{
+    struct upcall_trace *trace;
+    ds_put_format(output, "trace_id=0x%"PRIx64, block->trace_id);
+    if (!ovs_list_is_empty(&block->traces)) {
+        trace = CONTAINER_OF(ovs_list_front(&block->traces),
+                             struct upcall_trace, list_node);
+
+        ds_put_strftime_msec(output, " ts=%H:%M:%S.###",
+                             trace->timestamp_msec, false);
+
+        ds_put_format(output, " nodes=%"PRIuSIZE" flow=",
+                      ovs_list_size(&block->traces));
+        flow_format(output, &trace->initial_flow, NULL);
+    }
+}
+
+static void
+upcall_trace_block_format(const struct upcall_trace_block *block,
+                          struct ds *output)
+{
+    struct upcall_trace *trace;
+    ds_put_format(output, "=== Trace 0x%"PRIx64" ===\n", block->trace_id);
+    LIST_FOR_EACH (trace, list_node, &block->traces) {
+        ds_put_format(output, "--- recirc_id=0x%"PRIx32" [",
+                      trace->recirc_id);
+        ds_put_strftime_msec(output, "%H:%M:%S.###",
+                             trace->timestamp_msec, false);
+        ds_put_cstr(output, "] ---\n");
+        ds_put_cstr(output, "\nInitial flow: ");
+        flow_format(output, &trace->initial_flow, NULL);
+        ds_put_cstr(output, "\n");
+
+        /* Format the xtrace_traces. */
+        oftrace_node_print_details(output, &trace->xtrace_nodes, 0);
+        ds_put_char(output, '\n');
+    }
+}
 char * OVS_WARN_UNUSED_RESULT
 upcall_tracing_create(int argc, const char *argv[],
                       struct ofproto_dpif **ofprotop,
@@ -69,6 +237,7 @@ void
 upcall_tracing_destroy(struct upcall_tracing *tracing)
 {
     if (tracing) {
+        upcall_tracing_flush_all(tracing);
         ovs_mutex_destroy(&tracing->mutex);
         free(tracing);
     }
@@ -88,3 +257,186 @@ upcall_tracing_format(const struct upcall_tracing 
*tracing, struct ds *output)
         ds_put_cstr(output, "disabled");
     }
 }
+
+static bool
+upcall_tracing_matches(const struct upcall_tracing *tracing,
+                     const struct flow *flowp,
+                     const ofp_port_t *ofp_in_port)
+{
+    bool matches = false;
+    struct minimatch minimatch;
+    struct flow flow;
+    if (ofp_in_port) {
+        flow = *flowp;
+        flow.in_port.ofp_port = *ofp_in_port;
+        flowp = &flow;
+    }
+    minimatch_init(&minimatch, &tracing->filter);
+    if (minimatch_matches_flow(&minimatch, flowp)) {
+        matches = true;
+    }
+    minimatch_destroy(&minimatch);
+    return matches;
+}
+
+static struct upcall_trace_block *
+upcall_tracing_find_block__(struct upcall_tracing *tracing,
+                                 uint64_t trace_id)
+OVS_REQUIRES(tracing->mutex)
+{
+    struct upcall_trace_block *block;
+    LIST_FOR_EACH (block, list_node, &tracing->blocks) {
+        if (block->trace_id == trace_id) {
+            return block;
+        }
+    }
+    return NULL;
+}
+
+static void
+upcall_tracing_evict_if_needed(struct upcall_tracing *tracing)
+OVS_REQUIRES(tracing->mutex)
+{
+    struct upcall_trace_block *block;
+    while (tracing->n_blocks >= tracing->max_blocks
+           && !ovs_list_is_empty(&tracing->blocks)) {
+        block = CONTAINER_OF(ovs_list_pop_front(&tracing->blocks),
+                             struct upcall_trace_block, list_node);
+        upcall_trace_block_destroy(block);
+        VLOG_DBG("Deleted oldest trace block");
+        tracing->n_blocks--;
+    }
+}
+
+static uint64_t
+upcall_tracing_id_generate(struct upcall_tracing *tracing)
+OVS_REQUIRES(tracing->mutex)
+{
+    uint64_t id;
+    if (tracing->last_trace_id == UINT64_MAX) {
+        tracing->last_trace_id = 0;
+    }
+    id = ++tracing->last_trace_id;
+    return id;
+}
+
+/* Create a new trace allocating a new block and trace_id for it.*/
+struct upcall_trace *
+upcall_tracing_trace_from_flow(struct upcall_tracing *tracing,
+                               const struct flow *flow,
+                               const ofp_port_t *ofp_in_port)
+{
+    struct upcall_trace *trace = NULL;
+    struct upcall_trace_block *block;
+    uint64_t trace_id;
+
+    ovs_mutex_lock(&tracing->mutex);
+
+    if (!upcall_tracing_matches(tracing, flow, ofp_in_port)) {
+        goto out;
+    }
+
+    trace_id = upcall_tracing_id_generate(tracing);
+    trace = upcall_trace_create(0, trace_id, flow);
+
+    upcall_tracing_evict_if_needed(tracing);
+    block = upcall_trace_block_create(trace_id);
+    ovs_list_push_back(&tracing->blocks, &block->list_node);
+    ovs_list_push_back(&block->traces, &trace->list_node);
+    tracing->n_blocks++;
+
+out:
+    ovs_mutex_unlock(&tracing->mutex);
+    return upcall_trace_ref(trace);
+}
+
+/* Create a new trace if a block with 'trace_id' is already present.*/
+struct upcall_trace *
+upcall_tracing_append_to_id(struct upcall_tracing *tracing,
+                            uint64_t trace_id,
+                            uint32_t recirc_id,
+                            const struct flow *flow)
+{
+    struct upcall_trace *trace = NULL;
+    struct upcall_trace_block *block;
+
+    ovs_mutex_lock(&tracing->mutex);
+    block = upcall_tracing_find_block__(tracing, trace_id);
+    if (!block) {
+        goto out;
+    }
+
+    trace = upcall_trace_create(recirc_id, trace_id, flow);
+    ovs_list_push_back(&block->traces, &trace->list_node);
+out:
+    ovs_mutex_unlock(&tracing->mutex);
+    return upcall_trace_ref(trace);
+}
+
+void
+upcall_tracing_format_list(struct upcall_tracing *tracing, struct ds *output)
+{
+    ovs_mutex_lock(&tracing->mutex);
+
+    if (ovs_list_is_empty(&tracing->blocks)) {
+        ds_put_cstr(output, "No traces captured.\n");
+    } else {
+        struct upcall_trace_block *block;
+        LIST_FOR_EACH (block, list_node, &tracing->blocks) {
+            upcall_trace_block_format_short(block, output);
+            if (&block->list_node != ovs_list_back(&tracing->blocks)) {
+                ds_put_char(output, '\n');
+            }
+        }
+    }
+
+    ovs_mutex_unlock(&tracing->mutex);
+}
+
+/* Thread-safe version that finds and formats a block by trace_id.
+ * Returns true if block was found and formatted. */
+void
+upcall_tracing_format_id(struct upcall_tracing *tracing,
+                         uint64_t trace_id, struct ds *output)
+{
+    struct upcall_trace_block *block;
+
+    ovs_mutex_lock(&tracing->mutex);
+
+    block = upcall_tracing_find_block__(tracing, trace_id);
+    if (block) {
+        upcall_trace_block_format(block, output);
+    }
+
+    ovs_mutex_unlock(&tracing->mutex);
+}
+
+void
+upcall_tracing_flush(struct upcall_tracing *tracing, uint64_t trace_id)
+{
+    struct upcall_trace_block *block;
+
+    ovs_mutex_lock(&tracing->mutex);
+    block = upcall_tracing_find_block__(tracing, trace_id);
+    if (block) {
+        ovs_list_remove(&block->list_node);
+        upcall_trace_block_destroy(block);
+        tracing->n_blocks--;
+    }
+
+    ovs_mutex_unlock(&tracing->mutex);
+}
+
+void
+upcall_tracing_flush_all(struct upcall_tracing *tracing)
+{
+    struct upcall_trace_block *block;
+
+    ovs_mutex_lock(&tracing->mutex);
+    LIST_FOR_EACH_POP (block, list_node, &tracing->blocks) {
+        upcall_trace_block_destroy(block);
+    }
+    tracing->n_blocks = 0;
+    ovs_mutex_unlock(&tracing->mutex);
+}
+
diff --git a/ofproto/ofproto-dpif-upcall-trace.h 
b/ofproto/ofproto-dpif-upcall-trace.h
index 150d48d29..7e4d35399 100644
--- a/ofproto/ofproto-dpif-upcall-trace.h
+++ b/ofproto/ofproto-dpif-upcall-trace.h
@@ -25,9 +25,32 @@ struct upcall_tracing {
     size_t max_blocks;
     uint64_t last_trace_id;
 };
+struct upcall_trace;
+
 char * upcall_tracing_create(int argc, const char *argv[],
                              struct ofproto_dpif **ofprotop,
                              struct upcall_tracing **tracingp);
 void upcall_tracing_destroy(struct upcall_tracing *);
 void upcall_tracing_format(const struct upcall_tracing *, struct ds *);
+void upcall_tracing_format_list(struct upcall_tracing *, struct ds *);
+void upcall_tracing_format_id(struct upcall_tracing *, uint64_t trace_id,
+                              struct ds *);
+
+void upcall_tracing_flush(struct upcall_tracing *, uint64_t trace_id);
+void upcall_tracing_flush_all(struct upcall_tracing *);
+
+struct upcall_trace *
+upcall_tracing_trace_from_flow(struct upcall_tracing *tracing,
+                               const struct flow *flow,
+                               const ofp_port_t *ofp_in_port);
+struct upcall_trace *
+upcall_tracing_append_to_id(struct upcall_tracing *tracing,
+                            uint64_t trace_id,
+                            uint32_t recirc_id,
+                            const struct flow *flow);
+struct ovs_list *upcall_trace_xlate_start(struct upcall_trace *,
+                                         struct xlate_in *);
+uint64_t upcall_trace_get_trace_id(const struct upcall_trace *);
+void upcall_trace_unref(struct upcall_trace *);
+
 #endif /*OFPROTO_DPIF_UPCALL_TRACE_H */
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 20fa302a5..6fc3d2c1f 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -263,6 +263,8 @@ struct upcall {
     struct user_action_cookie cookie;
 
     uint64_t odp_actions_stub[1024 / 8]; /* Stub for odp_actions. */
+
+    struct upcall_trace *trace;    /* Upcall trace. */
 };
 
 /* Ukeys must transition through these states using transition_ukey(). */
@@ -377,6 +379,11 @@ static void udpif_pause_revalidators(struct udpif *);
 static void udpif_resume_revalidators(struct udpif *);
 static void *udpif_upcall_handler(void *);
 static void *udpif_revalidator(void *);
+static struct upcall_trace *udpif_trace_from_upcall(const struct upcall *);
+
+static struct upcall_trace
+*udpif_trace_from_frozen_upcall(const struct upcall *,
+                                const struct frozen_state *);
 static unsigned long udpif_get_n_flows(struct udpif *);
 static void revalidate(struct revalidator *);
 static void revalidator_pause(struct revalidator *);
@@ -411,6 +418,10 @@ static void upcall_unixctl_trace_create(struct 
unixctl_conn *, int argc,
                                         const char *argv[], void *aux);
 static void upcall_unixctl_trace_show(struct unixctl_conn *, int argc,
                                       const char *argv[], void *aux);
+static void upcall_unixctl_trace_list(struct unixctl_conn *, int argc,
+                                      const char *argv[], void *aux);
+static void upcall_unixctl_trace_get(struct unixctl_conn *, int argc,
+                                      const char *argv[], void *aux);
 static void upcall_unixctl_trace_delete(struct unixctl_conn *, int argc,
                                         const char *argv[], void *aux);
 
@@ -496,6 +507,10 @@ udpif_init(void)
                                  upcall_unixctl_trace_create, NULL);
         unixctl_command_register("upcall/trace/show", "", 0, 0,
                                  upcall_unixctl_trace_show, NULL);
+        unixctl_command_register("upcall/trace/list", "", 0, 1,
+                                 upcall_unixctl_trace_list, NULL);
+        unixctl_command_register("upcall/trace/get", "bridge trace_id", 1, 1,
+                                 upcall_unixctl_trace_get, NULL);
         unixctl_command_register("upcall/trace/delete", "", 0, 1,
                                  upcall_unixctl_trace_delete, NULL);
         ovsthread_once_done(&once);
@@ -1287,6 +1302,7 @@ upcall_receive(struct upcall *upcall, const struct 
dpif_backer *backer,
 
     upcall->out_tun_key = NULL;
     upcall->actions = NULL;
+    upcall->trace = udpif_trace_from_upcall(upcall);
 
     return 0;
 }
@@ -1310,6 +1326,14 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
                   upcall->flow, upcall->ofp_in_port, NULL,
                   stats.tcp_flags, upcall->packet, wc, odp_actions);
 
+    if (!upcall->trace && xin.frozen_state) {
+        upcall->trace =
+            udpif_trace_from_frozen_upcall(upcall, xin.frozen_state);
+    }
+
+    /* Set trace_id in xlate_in so it propagates through freezes. */
+    xin.trace_id = upcall_trace_get_trace_id(upcall->trace);
+
     if (upcall->type == MISS_UPCALL) {
         xin.resubmit_stats = &stats;
 
@@ -1332,6 +1356,8 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
 
     upcall->reval_seq = seq_read(udpif->reval_seq);
 
+    xin.trace = upcall_trace_xlate_start(upcall->trace, &xin);
+
     xerr = xlate_actions(&xin, &upcall->xout);
 
     /* Translate again and log the ofproto trace for
@@ -1401,6 +1427,9 @@ upcall_uninit(struct upcall *upcall)
             /* The reference was transferred to the ukey if one was created. */
             recirc_id_node_unref(upcall->recirc);
         }
+        if (upcall->trace) {
+            upcall_trace_unref(upcall->trace);
+        }
     }
 }
 
@@ -3477,6 +3506,66 @@ static void upcall_unixctl_trace_show(struct 
unixctl_conn *conn,
     ds_destroy(&ds);
 }
 
+static void upcall_unixctl_trace_list(struct unixctl_conn *conn,
+                                      int argc OVS_UNUSED,
+                                      const char *argv[] OVS_UNUSED,
+                                      void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct shash_node **backers;
+    int i;
+
+    backers = shash_sort(&all_dpif_backers);
+    for (i = 0; i < shash_count(&all_dpif_backers); i++) {
+        struct dpif_backer *backer = backers[i]->data;
+        struct upcall_tracing *tracing =
+            ovsrcu_get(struct upcall_tracing *, &backer->udpif->tracing);
+        if (tracing) {
+            ds_put_format(&ds, "%s:\n", dpif_name(backer->dpif));
+            upcall_tracing_format_list(tracing, &ds);
+        }
+    }
+    free(backers);
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+static void upcall_unixctl_trace_get(struct unixctl_conn *conn,
+                                      int argc OVS_UNUSED,
+                                      const char *argv[] OVS_UNUSED,
+                                      void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct shash_node **backers;
+    uint64_t trace_id;
+    int i;
+
+    if (!strncmp(argv[1], "0x", 2) || !strncmp(argv[1], "0X", 2)) {
+        if (sscanf(argv[1], "%"SCNx64, &trace_id) != 1) {
+            unixctl_command_reply_error(conn, "invalid trace_id");
+            return;
+        }
+    } else {
+        if (sscanf(argv[1], "%"SCNu64, &trace_id) != 1) {
+            unixctl_command_reply_error(conn, "invalid trace_id");
+            return;
+        }
+    }
+
+    backers = shash_sort(&all_dpif_backers);
+    for (i = 0; i < shash_count(&all_dpif_backers); i++) {
+        struct dpif_backer *backer = backers[i]->data;
+        struct upcall_tracing *tracing =
+            ovsrcu_get(struct upcall_tracing *, &backer->udpif->tracing);
+        if (tracing) {
+            upcall_tracing_format_id(tracing, trace_id, &ds);
+        }
+    }
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
 static void upcall_unixctl_trace_delete(struct unixctl_conn *conn,
                                         int argc OVS_UNUSED,
                                         const char *argv[] OVS_UNUSED,
@@ -3839,3 +3928,36 @@ udpif_flow_unprogram(struct udpif *udpif, struct 
udpif_key *ukey,
 
     return opsp->error;
 }
+
+static struct upcall_trace *
+udpif_trace_from_upcall(const struct upcall *upcall)
+{
+    struct upcall_trace *trace = NULL;
+    struct upcall_tracing *tracing = ovsrcu_get(struct upcall_tracing *,
+                                    &upcall->ofproto->backer->udpif->tracing);
+
+    /* Only check the filter for non-recirculated flows.  For recirculated
+     * flows, the trace_id will be retrieved from frozen_state later in
+     * udpif_tracer_from_frozen_upcall(). */
+    if (OVS_UNLIKELY(tracing) && upcall->flow->recirc_id == 0) {
+        trace = upcall_tracing_trace_from_flow(tracing, upcall->flow,
+                                       &upcall->ofp_in_port);
+    }
+    return trace;
+}
+
+static struct upcall_trace *
+udpif_trace_from_frozen_upcall(const struct upcall *upcall,
+                                const struct frozen_state *state)
+{
+    struct upcall_trace *trace = NULL;
+    struct upcall_tracing *tracing = ovsrcu_get(struct upcall_tracing *,
+                                    &upcall->ofproto->backer->udpif->tracing);
+
+    if (OVS_UNLIKELY(tracing) && state && state->trace_id) {
+        trace = upcall_tracing_append_to_id(tracing, state->trace_id,
+                                           upcall->flow->recirc_id,
+                                           upcall->flow);
+    }
+    return trace;
+}
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index de82e2903..8c23d6035 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -5265,6 +5265,7 @@ xlate_controller_action(struct xlate_ctx *ctx, int len,
         .mirrors = ctx->mirrors,
         .conntracked = ctx->conntracked,
         .was_mpls = ctx->was_mpls,
+        .trace_id = ctx->xin->trace_id,
         .ofpacts = NULL,
         .ofpacts_len = 0,
         .action_set = NULL,
@@ -5310,6 +5311,7 @@ finish_freezing__(struct xlate_ctx *ctx, uint8_t table)
         .conntracked = ctx->conntracked,
         .was_mpls = ctx->was_mpls,
         .xport_uuid = ctx->xin->xport_uuid,
+        .trace_id = ctx->xin->trace_id,
         .ofpacts = ctx->frozen_actions.data,
         .ofpacts_len = ctx->frozen_actions.size,
         .action_set = ctx->action_set.data,
@@ -5382,7 +5384,7 @@ compose_recirculate_and_fork(struct xlate_ctx *ctx, 
uint8_t table,
     ctx->freezing = true;
     recirc_id = finish_freezing__(ctx, table);
 
-    if (OVS_UNLIKELY(ctx->xin->trace) && recirc_id) {
+    if (OVS_UNLIKELY(ctx->xin->trace) && recirc_id && ctx->xin->recirc_queue) {
         if (oftrace_add_recirc_node(ctx->xin->recirc_queue,
                                     OFT_RECIRC_CONNTRACK, &ctx->xin->flow,
                                     ctx->ct_nat_action, ctx->xin->packet,
@@ -7918,6 +7920,7 @@ xlate_in_init(struct xlate_in *xin, struct ofproto_dpif 
*ofproto,
     xin->in_packet_out = false;
     xin->recirc_queue = NULL;
     xin->xport_uuid = UUID_ZERO;
+    xin->trace_id = 0;
 
     /* Do recirc lookup. */
     xin->frozen_state = NULL;
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index d973a634a..90a66e76a 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -174,6 +174,10 @@ struct xlate_in {
     /* If true, port names are displayed instead of port numbers in
      * tracing translation. */
     bool names;
+
+    /* If non-zero, this xlate is being traced and the trace_id should be
+     * propagated through recirculations. */
+    uint64_t trace_id;
 };
 
 void xlate_ofproto_set(struct ofproto_dpif *, const char *name, struct dpif *,
-- 
2.53.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to