2 * Copyright (c) 2017 Ericsson AB.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include "dpif-netdev-perf.h"
21 #include "openvswitch/dynamic-string.h"
22 #include "openvswitch/vlog.h"
23 #include "ovs-thread.h"
26 VLOG_DEFINE_THIS_MODULE(pmd_perf
);
28 #define ITER_US_THRESHOLD 250 /* Warning threshold for iteration duration
30 #define VHOST_QUEUE_FULL 128 /* Size of the virtio TX queue. */
31 #define LOG_IT_BEFORE 5 /* Number of iterations to log before
32 suspicious iteration. */
33 #define LOG_IT_AFTER 5 /* Number of iterations to log after
34 suspicious iteration. */
36 bool log_enabled
= false;
37 bool log_extend
= false;
38 static uint32_t log_it_before
= LOG_IT_BEFORE
;
39 static uint32_t log_it_after
= LOG_IT_AFTER
;
40 static uint32_t log_us_thr
= ITER_US_THRESHOLD
;
41 uint32_t log_q_thr
= VHOST_QUEUE_FULL
;
42 uint64_t iter_cycle_threshold
;
44 static struct vlog_rate_limit latency_rl
= VLOG_RATE_LIMIT_INIT(600, 600);
50 return rte_get_tsc_hz();
53 /* This function is only invoked from PMD threads which depend on DPDK.
54 * A dummy function is sufficient when building without DPDK_NETDEV. */
62 /* Histogram functions. */
65 histogram_walls_set_lin(struct histogram
*hist
, uint32_t min
, uint32_t max
)
69 ovs_assert(min
< max
);
70 for (i
= 0; i
< NUM_BINS
-1; i
++) {
71 hist
->wall
[i
] = min
+ (i
* (max
- min
)) / (NUM_BINS
- 2);
73 hist
->wall
[NUM_BINS
-1] = UINT32_MAX
;
77 histogram_walls_set_log(struct histogram
*hist
, uint32_t min
, uint32_t max
)
79 int i
, start
, bins
, wall
;
80 double log_min
, log_max
;
82 ovs_assert(min
< max
);
96 for (i
= 0; i
< bins
; i
++) {
97 /* Make sure each wall is monotonically increasing. */
98 wall
= MAX(wall
, exp(log_min
+ (i
* (log_max
- log_min
)) / (bins
-1)));
99 hist
->wall
[start
+ i
] = wall
++;
101 if (hist
->wall
[NUM_BINS
-2] < max
) {
102 hist
->wall
[NUM_BINS
-2] = max
;
104 hist
->wall
[NUM_BINS
-1] = UINT32_MAX
;
108 histogram_samples(const struct histogram
*hist
)
110 uint64_t samples
= 0;
112 for (int i
= 0; i
< NUM_BINS
; i
++) {
113 samples
+= hist
->bin
[i
];
119 histogram_clear(struct histogram
*hist
)
123 for (i
= 0; i
< NUM_BINS
; i
++) {
129 history_init(struct history
*h
)
131 memset(h
, 0, sizeof(*h
));
135 pmd_perf_stats_init(struct pmd_perf_stats
*s
)
137 memset(s
, 0, sizeof(*s
));
138 ovs_mutex_init(&s
->stats_mutex
);
139 ovs_mutex_init(&s
->clear_mutex
);
140 /* Logarithmic histogram for cycles/it ranging from 500 to 24M
141 * (corresponding to 200 ns to 9.6 ms at 2.5 GHz TSC clock). */
142 histogram_walls_set_log(&s
->cycles
, 500, 24000000);
143 /* Logarithmic histogram for pkts/it ranging from 0 to 1000. */
144 histogram_walls_set_log(&s
->pkts
, 0, 1000);
145 /* Linear histogram for cycles/pkt ranging from 100 to 30K. */
146 histogram_walls_set_lin(&s
->cycles_per_pkt
, 100, 30000);
147 /* Linear histogram for pkts/rx batch ranging from 0 to 32,
148 * the maximum rx batch size in OVS. */
149 histogram_walls_set_lin(&s
->pkts_per_batch
, 0, 32);
150 /* Linear histogram for upcalls/it ranging from 0 to 30. */
151 histogram_walls_set_lin(&s
->upcalls
, 0, 30);
152 /* Logarithmic histogram for cycles/upcall ranging from 1000 to 1M
153 * (corresponding to 400 ns to 400 us at 2.5 GHz TSC clock). */
154 histogram_walls_set_log(&s
->cycles_per_upcall
, 1000, 1000000);
155 /* Log. histogram for max vhostuser queue fill level from 0 to 512.
156 * 512 is the maximum fill level for a virtio queue with 1024
157 * descriptors (maximum configurable length in Qemu), with the
158 * DPDK 17.11 virtio PMD in the guest. */
159 histogram_walls_set_log(&s
->max_vhost_qfill
, 0, 512);
160 s
->iteration_cnt
= 0;
161 s
->start_ms
= time_msec();
162 s
->log_susp_it
= UINT32_MAX
;
163 s
->log_begin_it
= UINT32_MAX
;
164 s
->log_end_it
= UINT32_MAX
;
165 s
->log_reason
= NULL
;
169 pmd_perf_format_overall_stats(struct ds
*str
, struct pmd_perf_stats
*s
,
172 uint64_t stats
[PMD_N_STATS
];
173 double us_per_cycle
= 1000000.0 / get_tsc_hz();
179 pmd_perf_read_counters(s
, stats
);
180 uint64_t tot_cycles
= stats
[PMD_CYCLES_ITER_IDLE
] +
181 stats
[PMD_CYCLES_ITER_BUSY
];
182 uint64_t rx_packets
= stats
[PMD_STAT_RECV
];
183 uint64_t tx_packets
= stats
[PMD_STAT_SENT_PKTS
];
184 uint64_t tx_batches
= stats
[PMD_STAT_SENT_BATCHES
];
185 uint64_t passes
= stats
[PMD_STAT_RECV
] +
186 stats
[PMD_STAT_RECIRC
];
187 uint64_t upcalls
= stats
[PMD_STAT_MISS
];
188 uint64_t upcall_cycles
= stats
[PMD_CYCLES_UPCALL
];
189 uint64_t tot_iter
= histogram_samples(&s
->pkts
);
190 uint64_t idle_iter
= s
->pkts
.bin
[0];
191 uint64_t busy_iter
= tot_iter
>= idle_iter
? tot_iter
- idle_iter
: 0;
194 " Cycles: %12"PRIu64
" (%.2f GHz)\n"
195 " Iterations: %12"PRIu64
" (%.2f us/it)\n"
196 " - idle: %12"PRIu64
" (%4.1f %% cycles)\n"
197 " - busy: %12"PRIu64
" (%4.1f %% cycles)\n",
198 tot_cycles
, (tot_cycles
/ duration
) / 1E9
,
199 tot_iter
, tot_cycles
* us_per_cycle
/ tot_iter
,
201 100.0 * stats
[PMD_CYCLES_ITER_IDLE
] / tot_cycles
,
203 100.0 * stats
[PMD_CYCLES_ITER_BUSY
] / tot_cycles
);
204 if (rx_packets
> 0) {
206 " Rx packets: %12"PRIu64
" (%.0f Kpps, %.0f cycles/pkt)\n"
207 " Datapath passes: %12"PRIu64
" (%.2f passes/pkt)\n"
208 " - EMC hits: %12"PRIu64
" (%4.1f %%)\n"
209 " - Megaflow hits: %12"PRIu64
" (%4.1f %%, %.2f subtbl lookups/"
211 " - Upcalls: %12"PRIu64
" (%4.1f %%, %.1f us/upcall)\n"
212 " - Lost upcalls: %12"PRIu64
" (%4.1f %%)\n",
213 rx_packets
, (rx_packets
/ duration
) / 1000,
214 1.0 * stats
[PMD_CYCLES_ITER_BUSY
] / rx_packets
,
215 passes
, rx_packets
? 1.0 * passes
/ rx_packets
: 0,
216 stats
[PMD_STAT_EXACT_HIT
],
217 100.0 * stats
[PMD_STAT_EXACT_HIT
] / passes
,
218 stats
[PMD_STAT_MASKED_HIT
],
219 100.0 * stats
[PMD_STAT_MASKED_HIT
] / passes
,
220 stats
[PMD_STAT_MASKED_HIT
]
221 ? 1.0 * stats
[PMD_STAT_MASKED_LOOKUP
] / stats
[PMD_STAT_MASKED_HIT
]
223 upcalls
, 100.0 * upcalls
/ passes
,
224 upcalls
? (upcall_cycles
* us_per_cycle
) / upcalls
: 0,
225 stats
[PMD_STAT_LOST
],
226 100.0 * stats
[PMD_STAT_LOST
] / passes
);
228 ds_put_format(str
, " Rx packets: %12d\n", 0);
230 if (tx_packets
> 0) {
232 " Tx packets: %12"PRIu64
" (%.0f Kpps)\n"
233 " Tx batches: %12"PRIu64
" (%.2f pkts/batch)"
235 tx_packets
, (tx_packets
/ duration
) / 1000,
236 tx_batches
, 1.0 * tx_packets
/ tx_batches
);
238 ds_put_format(str
, " Tx packets: %12d\n\n", 0);
243 pmd_perf_format_histograms(struct ds
*str
, struct pmd_perf_stats
*s
)
247 ds_put_cstr(str
, "Histograms\n");
249 " %-21s %-21s %-21s %-21s %-21s %-21s %-21s\n",
250 "cycles/it", "packets/it", "cycles/pkt", "pkts/batch",
251 "max vhost qlen", "upcalls/it", "cycles/upcall");
252 for (i
= 0; i
< NUM_BINS
-1; i
++) {
254 " %-9d %-11"PRIu64
" %-9d %-11"PRIu64
" %-9d %-11"PRIu64
255 " %-9d %-11"PRIu64
" %-9d %-11"PRIu64
" %-9d %-11"PRIu64
256 " %-9d %-11"PRIu64
"\n",
257 s
->cycles
.wall
[i
], s
->cycles
.bin
[i
],
258 s
->pkts
.wall
[i
],s
->pkts
.bin
[i
],
259 s
->cycles_per_pkt
.wall
[i
], s
->cycles_per_pkt
.bin
[i
],
260 s
->pkts_per_batch
.wall
[i
], s
->pkts_per_batch
.bin
[i
],
261 s
->max_vhost_qfill
.wall
[i
], s
->max_vhost_qfill
.bin
[i
],
262 s
->upcalls
.wall
[i
], s
->upcalls
.bin
[i
],
263 s
->cycles_per_upcall
.wall
[i
], s
->cycles_per_upcall
.bin
[i
]);
266 " %-9s %-11"PRIu64
" %-9s %-11"PRIu64
" %-9s %-11"PRIu64
267 " %-9s %-11"PRIu64
" %-9s %-11"PRIu64
" %-9s %-11"PRIu64
268 " %-9s %-11"PRIu64
"\n",
269 ">", s
->cycles
.bin
[i
],
271 ">", s
->cycles_per_pkt
.bin
[i
],
272 ">", s
->pkts_per_batch
.bin
[i
],
273 ">", s
->max_vhost_qfill
.bin
[i
],
274 ">", s
->upcalls
.bin
[i
],
275 ">", s
->cycles_per_upcall
.bin
[i
]);
276 if (s
->totals
.iterations
> 0) {
278 "-----------------------------------------------------"
279 "-----------------------------------------------------"
280 "------------------------------------------------\n");
282 " %-21s %-21s %-21s %-21s %-21s %-21s %-21s\n",
283 "cycles/it", "packets/it", "cycles/pkt", "pkts/batch",
284 "vhost qlen", "upcalls/it", "cycles/upcall");
286 " %-21"PRIu64
" %-21.5f %-21"PRIu64
287 " %-21.5f %-21.5f %-21.5f %-21"PRIu32
"\n",
288 s
->totals
.cycles
/ s
->totals
.iterations
,
289 1.0 * s
->totals
.pkts
/ s
->totals
.iterations
,
291 ? s
->totals
.busy_cycles
/ s
->totals
.pkts
: 0,
293 ? 1.0 * s
->totals
.pkts
/ s
->totals
.batches
: 0,
294 1.0 * s
->totals
.max_vhost_qfill
/ s
->totals
.iterations
,
295 1.0 * s
->totals
.upcalls
/ s
->totals
.iterations
,
297 ? s
->totals
.upcall_cycles
/ s
->totals
.upcalls
: 0);
302 pmd_perf_format_iteration_history(struct ds
*str
, struct pmd_perf_stats
*s
,
305 struct iter_stats
*is
;
312 ds_put_format(str
, " %-17s %-10s %-10s %-10s %-10s "
313 "%-10s %-10s %-10s\n",
314 "iter", "cycles", "packets", "cycles/pkt", "pkts/batch",
315 "vhost qlen", "upcalls", "cycles/upcall");
316 for (i
= 1; i
<= n_iter
; i
++) {
317 index
= history_sub(s
->iterations
.idx
, i
);
318 is
= &s
->iterations
.sample
[index
];
320 " %-17"PRIu64
" %-11"PRIu64
" %-11"PRIu32
321 " %-11"PRIu64
" %-11"PRIu32
" %-11"PRIu32
322 " %-11"PRIu32
" %-11"PRIu32
"\n",
326 is
->pkts
? is
->cycles
/ is
->pkts
: 0,
327 is
->batches
? is
->pkts
/ is
->batches
: 0,
330 is
->upcalls
? is
->upcall_cycles
/ is
->upcalls
: 0);
335 pmd_perf_format_ms_history(struct ds
*str
, struct pmd_perf_stats
*s
, int n_ms
)
337 struct iter_stats
*is
;
345 " %-12s %-10s %-10s %-10s %-10s"
346 " %-10s %-10s %-10s %-10s\n",
347 "ms", "iterations", "cycles/it", "Kpps", "cycles/pkt",
348 "pkts/batch", "vhost qlen", "upcalls", "cycles/upcall");
349 for (i
= 1; i
<= n_ms
; i
++) {
350 index
= history_sub(s
->milliseconds
.idx
, i
);
351 is
= &s
->milliseconds
.sample
[index
];
353 " %-12"PRIu64
" %-11"PRIu32
" %-11"PRIu64
354 " %-11"PRIu32
" %-11"PRIu64
" %-11"PRIu32
355 " %-11"PRIu32
" %-11"PRIu32
" %-11"PRIu32
"\n",
358 is
->iterations
? is
->cycles
/ is
->iterations
: 0,
360 is
->pkts
? is
->busy_cycles
/ is
->pkts
: 0,
361 is
->batches
? is
->pkts
/ is
->batches
: 0,
363 ? is
->max_vhost_qfill
/ is
->iterations
: 0,
365 is
->upcalls
? is
->upcall_cycles
/ is
->upcalls
: 0);
370 pmd_perf_read_counters(struct pmd_perf_stats
*s
,
371 uint64_t stats
[PMD_N_STATS
])
375 /* These loops subtracts reference values (.zero[*]) from the counters.
376 * Since loads and stores are relaxed, it might be possible for a .zero[*]
377 * value to be more recent than the current value we're reading from the
378 * counter. This is not a big problem, since these numbers are not
379 * supposed to be 100% accurate, but we should at least make sure that
380 * the result is not negative. */
381 for (int i
= 0; i
< PMD_N_STATS
; i
++) {
382 atomic_read_relaxed(&s
->counters
.n
[i
], &val
);
383 if (val
> s
->counters
.zero
[i
]) {
384 stats
[i
] = val
- s
->counters
.zero
[i
];
391 /* This function clears the PMD performance counters from within the PMD
392 * thread or from another thread when the PMD thread is not executing its
395 pmd_perf_stats_clear_lock(struct pmd_perf_stats
*s
)
396 OVS_REQUIRES(s
->stats_mutex
)
398 ovs_mutex_lock(&s
->clear_mutex
);
399 for (int i
= 0; i
< PMD_N_STATS
; i
++) {
400 atomic_read_relaxed(&s
->counters
.n
[i
], &s
->counters
.zero
[i
]);
402 /* The following stats are only applicable in PMD thread and */
403 memset(&s
->current
, 0 , sizeof(struct iter_stats
));
404 memset(&s
->totals
, 0 , sizeof(struct iter_stats
));
405 histogram_clear(&s
->cycles
);
406 histogram_clear(&s
->pkts
);
407 histogram_clear(&s
->cycles_per_pkt
);
408 histogram_clear(&s
->upcalls
);
409 histogram_clear(&s
->cycles_per_upcall
);
410 histogram_clear(&s
->pkts_per_batch
);
411 histogram_clear(&s
->max_vhost_qfill
);
412 history_init(&s
->iterations
);
413 history_init(&s
->milliseconds
);
414 s
->start_ms
= time_msec();
415 s
->milliseconds
.sample
[0].timestamp
= s
->start_ms
;
416 s
->log_susp_it
= UINT32_MAX
;
417 s
->log_begin_it
= UINT32_MAX
;
418 s
->log_end_it
= UINT32_MAX
;
419 s
->log_reason
= NULL
;
420 /* Clearing finished. */
422 ovs_mutex_unlock(&s
->clear_mutex
);
425 /* This function can be called from the anywhere to clear the stats
426 * of PMD and non-PMD threads. */
428 pmd_perf_stats_clear(struct pmd_perf_stats
*s
)
430 if (ovs_mutex_trylock(&s
->stats_mutex
) == 0) {
431 /* Locking successful. PMD not polling. */
432 pmd_perf_stats_clear_lock(s
);
433 ovs_mutex_unlock(&s
->stats_mutex
);
435 /* Request the polling PMD to clear the stats. There is no need to
436 * block here as stats retrieval is prevented during clearing. */
441 /* Functions recording PMD metrics per iteration. */
444 pmd_perf_start_iteration(struct pmd_perf_stats
*s
)
445 OVS_REQUIRES(s
->stats_mutex
)
448 /* Clear the PMD stats before starting next iteration. */
449 pmd_perf_stats_clear_lock(s
);
452 /* Initialize the current interval stats. */
453 memset(&s
->current
, 0, sizeof(struct iter_stats
));
454 if (OVS_LIKELY(s
->last_tsc
)) {
455 /* We assume here that last_tsc was updated immediately prior at
456 * the end of the previous iteration, or just before the first
458 s
->start_tsc
= s
->last_tsc
;
460 /* In case last_tsc has never been set before. */
461 s
->start_tsc
= cycles_counter_update(s
);
466 pmd_perf_end_iteration(struct pmd_perf_stats
*s
, int rx_packets
,
467 int tx_packets
, bool full_metrics
)
469 uint64_t now_tsc
= cycles_counter_update(s
);
470 struct iter_stats
*cum_ms
;
471 uint64_t cycles
, cycles_per_pkt
= 0;
474 cycles
= now_tsc
- s
->start_tsc
;
475 s
->current
.timestamp
= s
->iteration_cnt
;
476 s
->current
.cycles
= cycles
;
477 s
->current
.pkts
= rx_packets
;
479 if (rx_packets
+ tx_packets
> 0) {
480 pmd_perf_update_counter(s
, PMD_CYCLES_ITER_BUSY
, cycles
);
482 pmd_perf_update_counter(s
, PMD_CYCLES_ITER_IDLE
, cycles
);
484 /* Add iteration samples to histograms. */
485 histogram_add_sample(&s
->cycles
, cycles
);
486 histogram_add_sample(&s
->pkts
, rx_packets
);
492 s
->counters
.n
[PMD_CYCLES_UPCALL
] += s
->current
.upcall_cycles
;
494 if (rx_packets
> 0) {
495 cycles_per_pkt
= cycles
/ rx_packets
;
496 histogram_add_sample(&s
->cycles_per_pkt
, cycles_per_pkt
);
498 if (s
->current
.batches
> 0) {
499 histogram_add_sample(&s
->pkts_per_batch
,
500 rx_packets
/ s
->current
.batches
);
502 histogram_add_sample(&s
->upcalls
, s
->current
.upcalls
);
503 if (s
->current
.upcalls
> 0) {
504 histogram_add_sample(&s
->cycles_per_upcall
,
505 s
->current
.upcall_cycles
/ s
->current
.upcalls
);
507 histogram_add_sample(&s
->max_vhost_qfill
, s
->current
.max_vhost_qfill
);
509 /* Add iteration samples to millisecond stats. */
510 cum_ms
= history_current(&s
->milliseconds
);
511 cum_ms
->iterations
++;
512 cum_ms
->cycles
+= cycles
;
513 if (rx_packets
> 0) {
514 cum_ms
->busy_cycles
+= cycles
;
516 cum_ms
->pkts
+= s
->current
.pkts
;
517 cum_ms
->upcalls
+= s
->current
.upcalls
;
518 cum_ms
->upcall_cycles
+= s
->current
.upcall_cycles
;
519 cum_ms
->batches
+= s
->current
.batches
;
520 cum_ms
->max_vhost_qfill
+= s
->current
.max_vhost_qfill
;
523 /* Log suspicious iterations. */
524 if (cycles
> iter_cycle_threshold
) {
525 reason
= "Excessive total cycles";
526 } else if (s
->current
.max_vhost_qfill
>= log_q_thr
) {
527 reason
= "Vhost RX queue full";
529 if (OVS_UNLIKELY(reason
)) {
530 pmd_perf_set_log_susp_iteration(s
, reason
);
531 cycles_counter_update(s
);
534 /* Log iteration interval around suspicious iteration when reaching
535 * the end of the range to be logged. */
536 if (OVS_UNLIKELY(s
->log_end_it
== s
->iterations
.idx
)) {
537 pmd_perf_log_susp_iteration_neighborhood(s
);
538 cycles_counter_update(s
);
542 /* Store in iteration history. This advances the iteration idx and
543 * clears the next slot in the iteration history. */
544 history_store(&s
->iterations
, &s
->current
);
546 if (now_tsc
> s
->next_check_tsc
) {
547 /* Check if ms is completed and store in milliseconds history. */
548 uint64_t now
= time_msec();
549 if (now
!= cum_ms
->timestamp
) {
550 /* Add ms stats to totals. */
551 s
->totals
.iterations
+= cum_ms
->iterations
;
552 s
->totals
.cycles
+= cum_ms
->cycles
;
553 s
->totals
.busy_cycles
+= cum_ms
->busy_cycles
;
554 s
->totals
.pkts
+= cum_ms
->pkts
;
555 s
->totals
.upcalls
+= cum_ms
->upcalls
;
556 s
->totals
.upcall_cycles
+= cum_ms
->upcall_cycles
;
557 s
->totals
.batches
+= cum_ms
->batches
;
558 s
->totals
.max_vhost_qfill
+= cum_ms
->max_vhost_qfill
;
559 cum_ms
= history_next(&s
->milliseconds
);
560 cum_ms
->timestamp
= now
;
562 /* Do the next check after 10K cycles (4 us at 2.5 GHz TSC clock). */
563 s
->next_check_tsc
= cycles_counter_update(s
) + 10000;
567 /* Delay logging of the suspicious iteration and the range of iterations
568 * around it until after the last iteration in the range to be logged.
569 * This avoids any distortion of the measurements through the cost of
573 pmd_perf_set_log_susp_iteration(struct pmd_perf_stats
*s
,
576 if (s
->log_susp_it
== UINT32_MAX
) {
577 /* No logging scheduled yet. */
578 s
->log_susp_it
= s
->iterations
.idx
;
579 s
->log_reason
= reason
;
580 s
->log_begin_it
= history_sub(s
->iterations
.idx
, log_it_before
);
581 s
->log_end_it
= history_add(s
->iterations
.idx
, log_it_after
+ 1);
582 } else if (log_extend
) {
583 /* Logging was initiated earlier, we log the previous suspicious
584 * iteration now and extend the logging interval, if possible. */
585 struct iter_stats
*susp
= &s
->iterations
.sample
[s
->log_susp_it
];
586 uint32_t new_end_it
, old_range
, new_range
;
588 VLOG_WARN_RL(&latency_rl
,
589 "Suspicious iteration (%s): iter=%"PRIu64
590 " duration=%"PRIu64
" us\n",
593 (1000000L * susp
->cycles
) / get_tsc_hz());
595 new_end_it
= history_add(s
->iterations
.idx
, log_it_after
+ 1);
596 new_range
= history_sub(new_end_it
, s
->log_begin_it
);
597 old_range
= history_sub(s
->log_end_it
, s
->log_begin_it
);
598 if (new_range
< old_range
) {
599 /* Extended range exceeds history length. */
600 new_end_it
= s
->log_begin_it
;
602 s
->log_susp_it
= s
->iterations
.idx
;
603 s
->log_reason
= reason
;
604 s
->log_end_it
= new_end_it
;
609 pmd_perf_log_susp_iteration_neighborhood(struct pmd_perf_stats
*s
)
611 ovs_assert(s
->log_reason
!= NULL
);
612 ovs_assert(s
->log_susp_it
!= UINT32_MAX
);
614 struct ds log
= DS_EMPTY_INITIALIZER
;
615 struct iter_stats
*susp
= &s
->iterations
.sample
[s
->log_susp_it
];
616 uint32_t range
= history_sub(s
->log_end_it
, s
->log_begin_it
);
618 VLOG_WARN_RL(&latency_rl
,
619 "Suspicious iteration (%s): iter=%"PRIu64
620 " duration=%"PRIu64
" us\n",
623 (1000000L * susp
->cycles
) / get_tsc_hz());
625 pmd_perf_format_iteration_history(&log
, s
, range
);
626 VLOG_WARN_RL(&latency_rl
,
627 "Neighborhood of suspicious iteration:\n"
628 "%s", ds_cstr(&log
));
630 s
->log_susp_it
= s
->log_end_it
= s
->log_begin_it
= UINT32_MAX
;
631 s
->log_reason
= NULL
;
634 /* Reset to safe default values to avoid disturbance. */
635 log_it_before
= LOG_IT_BEFORE
;
636 log_it_after
= LOG_IT_AFTER
;
642 pmd_perf_log_set_cmd(struct unixctl_conn
*conn
,
643 int argc
, const char *argv
[],
644 void *aux OVS_UNUSED
)
646 unsigned int it_before
, it_after
, us_thr
, q_thr
;
652 it_before
= log_it_before
;
653 it_after
= log_it_after
;
658 if (!strcmp(argv
[1], "on")) {
662 } else if (!strcmp(argv
[1], "off")) {
666 } else if (!strcmp(argv
[1], "-e")) {
670 } else if (!strcmp(argv
[1], "-ne")) {
674 } else if (!strcmp(argv
[1], "-a") && argc
> 2) {
675 if (str_to_uint(argv
[2], 10, &it_after
)) {
676 if (it_after
> HISTORY_LEN
- 2) {
677 it_after
= HISTORY_LEN
- 2;
685 } else if (!strcmp(argv
[1], "-b") && argc
> 2) {
686 if (str_to_uint(argv
[2], 10, &it_before
)) {
687 if (it_before
> HISTORY_LEN
- 2) {
688 it_before
= HISTORY_LEN
- 2;
696 } else if (!strcmp(argv
[1], "-q") && argc
> 2) {
697 if (!str_to_uint(argv
[2], 10, &q_thr
)) {
703 } else if (!strcmp(argv
[1], "-us") && argc
> 2) {
704 if (!str_to_uint(argv
[2], 10, &us_thr
)) {
715 if (it_before
+ it_after
> HISTORY_LEN
- 2) {
716 it_after
= HISTORY_LEN
- 2 - it_before
;
720 unixctl_command_reply_error(conn
,
721 "Usage: ovs-appctl dpif-netdev/pmd-perf-log-set "
722 "[on|off] [-b before] [-a after] [-e|-ne] "
723 "[-us usec] [-q qlen]");
727 VLOG_INFO("pmd-perf-log-set: %s, before=%d, after=%d, extend=%s, "
728 "us_thr=%d, q_thr=%d\n",
729 on
? "on" : "off", it_before
, it_after
,
730 extend
? "true" : "false", us_thr
, q_thr
);
733 log_it_before
= it_before
;
734 log_it_after
= it_after
;
737 iter_cycle_threshold
= (log_us_thr
* get_tsc_hz()) / 1000000L;
739 unixctl_command_reply(conn
, "");