> > > @@ -3429,16 +3381,17 @@ dp_netdev_process_rxq_port(struct 
> > > dp_netdev_pmd_thread *pmd,
> > >      struct pmd_perf_stats *s = &pmd->perf_stats;
> > >      struct dp_packet_batch batch;
> > >      int error;
> > > -    int batch_cnt = 0, output_cnt = 0;
> > > +    int batch_cnt = 0;
> > >
> > >      dp_packet_batch_init(&batch);
> > > -
> > > -    cycles_count_intermediate(pmd, NULL, 0);
> > >      pmd->ctx.last_rxq = rxq;
> > > -    pmd->ctx.current_pmd_cycles_type = PMD_CYCLES_POLL_BUSY;
> > > +
> > > +    /* Measure duration for polling and processing rx burst. */
> > > +    uint64_t cycles = cycles_counter(pmd);
> >
> > hi Jan - not a full review. Consider this function is called from
> > dp_netdev_input()...dp_execute_cb(), if the code below is hit, the above
> > measurement will make for double counting.
> >
> > <snip>
> > #ifdef DPDK_NETDEV
> >             if (OVS_UNLIKELY(!dp_packet_batch_is_empty(&p->output_pkts)
> >                              && packets_->packets[0]->source
> >                                 != p->output_pkts.packets[0]->source)) {
> >                 /* XXX: netdev-dpdk assumes that all packets in a single
> >                  *      output batch has the same source. Flush here to
> >                  *      avoid memory access issues. */
> >                 dp_netdev_pmd_flush_output_on_port(pmd, p);
> >             }
> > #endif
> >             if (dp_packet_batch_size(&p->output_pkts)
> >                 + dp_packet_batch_size(packets_) > NETDEV_MAX_BURST) {
> >                 /* Flush here to avoid overflow. */
> >                 dp_netdev_pmd_flush_output_on_port(pmd, p);
> >             }
> > </snip>
> >
> > I wouldn't be too concerned about the first one because it's unlikely. I
> > would be more concerned about the second one, as it is quite likely that
> > multiple rxqs are sending to a single port and the cycles for tx could
> > be significant. Take 2 rxq's sending packets to a vhost port, unless we
> > got batches of 32 on each rxq there would be double counting for one of
> > the queues everytime. There could be more cases like this also, as there
> > is flush from a lot of places. I didn't compare, but from memory I don't
> > think this would be an issues in Ilya's patches.
> >
> > start/intermediate/stop type functions might be better than storing
> > cycles locally in functions, because you could stop and start the count
> > from any function you need. In this case, you could avoid the double
> > counting by something like stop/call
> > dp_netdev_pmd_flush_output_on_port()/start. That might start to make the
> > code more like Ilya's, so it's probably good to get his review.
> 
> You are right, I overlooked the possibility for a tx burst triggered 
> immediately by
> executing an output action. This needs fixing, and I agree that a scheme which
> is able to "suspend" an ongoing measurement at any time will be needed for
> that. Let me have another look at the previous scheme to see if I can simplify
> that. If not we can stick to the original solution.
> 

I have prepared a slightly more advanced version that allows arbitrary nesting 
of cycles measurements where the nested measurements are excluded from the 
outer measurement: Here's the outline. I am including this in the next version 
of my umbrella series. This version isn't tested yet:

diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 4761d3b..e487828 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -516,6 +516,8 @@ struct tx_port {
     struct dp_netdev_rxq *output_pkts_rxqs[NETDEV_MAX_BURST];
 };
 
+struct cycle_timer;
+
 /* A set of properties for the current processing loop that is not directly
  * associated with the pmd thread itself, but with the packets being
  * processed or the short-term system configuration (for example, time).
@@ -527,6 +529,7 @@ struct dp_netdev_pmd_thread_ctx {
     uint64_t last_cycles;
     /* RX queue from which last packet was received. */
     struct dp_netdev_rxq *last_rxq;
+    struct cycle_timer *cur_timer;
 };
 
 /* PMD: Poll modes drivers.  PMD accesses devices via polling to eliminate
@@ -632,6 +635,71 @@ struct dpif_netdev {
     uint64_t last_port_seq;
 };
 
+/* Support for accurate timing of PMD execution on TSC clock cycle level.
+ * These functions are intended to be invoked in the context of pmd threads. */
+
+/* Read the TSC cycle register and cache it in the pmd context. Any function
+ * not requiring clock cycle accuracy should read the cached value from the
+ * context. */
+static inline uint64_t
+cycles_counter(struct dp_netdev_pmd_thread *pmd)
+{
+#ifdef DPDK_NETDEV
+    return pmd->ctx.last_cycles = rte_get_tsc_cycles();
+#else
+    return pmd->ctx.last_cycles = 0;
+#endif
+}
+
+/* A nestable timer for measuring execution time in TSC cycles.
+ *
+ * Usage:
+ * struct cycle_timer timer;
+ *
+ * cycle_timer_start(pmd, &timer);
+ * <Timed execution>
+ * uint64_t cycles = cycle_timer_stop(pmd, &timer);
+ *
+ * The caller must guarantee that a call to cycle_timer_start() is always
+ * paired with a call to cycle_stimer_stop().
+ *
+ * Is is possible to have nested cycles timers within the timed code. The
+ * execution time measured by the nested timers is excluded from the time
+ * measured by the embracing timer.
+ */
+
+struct cycle_timer {
+    uint64_t *offset;
+    struct cycle_timer *interrupted;
+};
+
+static void
+cycle_timer_start(struct dp_netdev_pmd_thread *pmd,
+                  struct cycle_timer *timer)
+{
+    /* Suspend current timer, if any. */
+    timer->interrupted = pmd->ctx.cur_timer;
+    timer->offset = cycles_counter(pmd);
+    pmd->ctx.cur_timer = timer;
+}
+
+static uint64_t
+cycle_timer_stop(struct dp_netdev_pmd_thread *pmd,
+                 struct cycle_timer *timer)
+{
+    /* Assert that this is the current cycle timer. */
+    ovs_assert(pmd->ctx.cur_timer == timer);
+    uint64_t cycles = cycles_counter(pmd) - timer->offset;
+    if (timer->interrupted) {
+        /* Adjust the start offset by the used cycles. */
+        timer->interrupted->offset += cycles;
+    }
+    /* Restore suspended timer, if any. */
+    pmd->ctx.cur_timer = timer->interrupted;
+    return cycles;
+}
+
 static int get_port_by_number(struct dp_netdev *dp, odp_port_t port_no,
                               struct dp_netdev_port **portp)
     OVS_REQUIRES(dp->port_mutex);
@@ -3267,16 +3335,6 @@ dp_netdev_actions_free(struct dp_netdev_actions *actions)
     free(actions);
 }
 


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

Reply via email to