Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2016-2018 Intel Corporation
3 : : */
4 : :
5 : : #include <stdlib.h>
6 : :
7 : : #include <eal_export.h>
8 : : #include <rte_alarm.h>
9 : : #include <rte_mbuf.h>
10 : : #include <rte_ethdev.h>
11 : : #include <rte_lcore.h>
12 : : #include <rte_log.h>
13 : : #include <rte_memzone.h>
14 : : #include <rte_errno.h>
15 : : #include <rte_string_fns.h>
16 : : #include <rte_pause.h>
17 : : #include <rte_pcapng.h>
18 : :
19 : : #include "rte_pdump.h"
20 : :
21 [ - + ]: 254 : RTE_LOG_REGISTER_DEFAULT(pdump_logtype, NOTICE);
22 : : #define RTE_LOGTYPE_PDUMP pdump_logtype
23 : :
24 : : #define PDUMP_LOG_LINE(level, ...) \
25 : : RTE_LOG_LINE_PREFIX(level, PDUMP, "%s(): ", __func__, __VA_ARGS__)
26 : :
27 : : /* Used for the multi-process communication */
28 : : #define PDUMP_MP "mp_pdump"
29 : :
30 : : #define PDUMP_BURST_SIZE 32u
31 : :
32 : : /* Overly generous timeout for secondary to respond */
33 : : #define MP_TIMEOUT_S 5
34 : :
35 : : enum pdump_operation {
36 : : DISABLE = 1,
37 : : ENABLE = 2
38 : : };
39 : :
40 : : static const char *
41 : 0 : pdump_opname(enum pdump_operation op)
42 : : {
43 : : static char buf[32];
44 : :
45 [ # # ]: 0 : if (op == DISABLE)
46 : : return "disable";
47 [ # # ]: 0 : if (op == ENABLE)
48 : : return "enable";
49 : :
50 : : snprintf(buf, sizeof(buf), "op%u", op);
51 : 0 : return buf;
52 : : }
53 : :
54 : : /* Internal version number in request */
55 : : enum pdump_version {
56 : : V1 = 1, /* no filtering or snap */
57 : : V2 = 2,
58 : : };
59 : :
60 : : struct pdump_request {
61 : : uint16_t ver;
62 : : uint16_t op;
63 : : uint32_t flags;
64 : : char device[RTE_DEV_NAME_MAX_LEN];
65 : : uint16_t queue;
66 : : struct rte_ring *ring;
67 : : struct rte_mempool *mp;
68 : :
69 : : const struct rte_bpf_prm *prm;
70 : : uint32_t snaplen;
71 : : };
72 : :
73 : : struct pdump_response {
74 : : uint16_t ver;
75 : : uint16_t res_op;
76 : : int32_t err_value;
77 : : };
78 : :
79 : : struct pdump_bundle {
80 : : struct rte_mp_msg msg;
81 : : char peer[];
82 : : };
83 : :
84 : : static struct pdump_rxtx_cbs {
85 : : struct rte_ring *ring;
86 : : struct rte_mempool *mp;
87 : : const struct rte_eth_rxtx_callback *cb;
88 : : const struct rte_bpf *filter;
89 : : enum pdump_version ver;
90 : : uint32_t snaplen;
91 : : RTE_ATOMIC(uint32_t) use_count;
92 : : } rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT],
93 : : tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
94 : :
95 : :
96 : : /*
97 : : * The packet capture statistics keep track of packets
98 : : * accepted, filtered and dropped. These are per-queue
99 : : * and in memory between primary and secondary processes.
100 : : */
101 : : static const char MZ_RTE_PDUMP_STATS[] = "rte_pdump_stats";
102 : : static struct {
103 : : struct rte_pdump_stats rx[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
104 : : struct rte_pdump_stats tx[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
105 : : const struct rte_memzone *mz;
106 : : } *pdump_stats;
107 : :
108 : : static void
109 : 0 : pdump_cb_wait(struct pdump_rxtx_cbs *cbs)
110 : : {
111 : : /* make sure the data loads happens before the use count load */
112 : : rte_atomic_thread_fence(rte_memory_order_acquire);
113 : :
114 : : /* wait until use_count is even (not in use) */
115 [ # # ]: 0 : RTE_WAIT_UNTIL_MASKED(&cbs->use_count, 1, ==, 0, rte_memory_order_relaxed);
116 : 0 : }
117 : :
118 : : static __rte_always_inline void
119 : : pdump_cb_hold(struct pdump_rxtx_cbs *cbs)
120 : : {
121 : 0 : uint32_t count = cbs->use_count + 1;
122 : :
123 : 0 : rte_atomic_store_explicit(&cbs->use_count, count, rte_memory_order_relaxed);
124 : :
125 : : /* prevent stores after this from happening before the use_count update */
126 : : rte_atomic_thread_fence(rte_memory_order_release);
127 : : }
128 : :
129 : : static __rte_always_inline void
130 : : pdump_cb_release(struct pdump_rxtx_cbs *cbs)
131 : : {
132 : 0 : uint32_t count = cbs->use_count + 1;
133 : :
134 : : /* Synchronizes-with the load acquire in pdump_cb_wait */
135 : 0 : rte_atomic_store_explicit(&cbs->use_count, count, rte_memory_order_release);
136 : : }
137 : :
138 : : /* Create a clone of mbuf to be placed into ring. */
139 : : static void
140 : 0 : pdump_copy_burst(uint16_t port_id, uint16_t queue_id,
141 : : enum rte_pcapng_direction direction,
142 : : struct rte_mbuf **pkts, unsigned int nb_pkts,
143 : : const struct pdump_rxtx_cbs *cbs,
144 : : struct rte_pdump_stats *stats)
145 : : {
146 : : unsigned int i, ring_enq, d_pkts = 0;
147 : : struct rte_mbuf *dup_bufs[PDUMP_BURST_SIZE]; /* duplicated packets */
148 : : struct rte_ring *ring;
149 : : struct rte_mempool *mp;
150 : : struct rte_mbuf *p;
151 : : uint64_t rcs[PDUMP_BURST_SIZE]; /* filter result */
152 : :
153 : : RTE_ASSERT(nb_pkts <= PDUMP_BURST_SIZE);
154 : :
155 [ # # ]: 0 : if (cbs->filter)
156 : 0 : rte_bpf_exec_burst(cbs->filter, (void **)pkts, rcs, nb_pkts);
157 : :
158 : 0 : ring = cbs->ring;
159 : 0 : mp = cbs->mp;
160 [ # # ]: 0 : for (i = 0; i < nb_pkts; i++) {
161 : : /*
162 : : * This uses same BPF return value convention as socket filter
163 : : * and pcap_offline_filter.
164 : : * if program returns zero
165 : : * then packet doesn't match the filter (will be ignored).
166 : : */
167 [ # # # # ]: 0 : if (cbs->filter && rcs[i] == 0) {
168 : 0 : rte_atomic_fetch_add_explicit(&stats->filtered,
169 : : 1, rte_memory_order_relaxed);
170 : 0 : continue;
171 : : }
172 : :
173 : : /*
174 : : * If using pcapng then want to wrap packets
175 : : * otherwise a simple copy.
176 : : */
177 [ # # ]: 0 : if (cbs->ver == V2)
178 : 0 : p = rte_pcapng_copy(port_id, queue_id, pkts[i], mp, cbs->snaplen,
179 : : direction, NULL);
180 : : else
181 : 0 : p = rte_pktmbuf_copy(pkts[i], mp, 0, cbs->snaplen);
182 : :
183 [ # # ]: 0 : if (unlikely(p == NULL))
184 : 0 : rte_atomic_fetch_add_explicit(&stats->nombuf, 1, rte_memory_order_relaxed);
185 : : else
186 : 0 : dup_bufs[d_pkts++] = p;
187 : : }
188 : :
189 [ # # ]: 0 : if (d_pkts == 0)
190 : 0 : return;
191 : :
192 [ # # # # : 0 : rte_atomic_fetch_add_explicit(&stats->accepted, d_pkts, rte_memory_order_relaxed);
# ]
193 : :
194 : : ring_enq = rte_ring_enqueue_burst(ring, (void *)&dup_bufs[0], d_pkts, NULL);
195 [ # # ]: 0 : if (unlikely(ring_enq < d_pkts)) {
196 : 0 : unsigned int drops = d_pkts - ring_enq;
197 : :
198 : 0 : rte_atomic_fetch_add_explicit(&stats->ringfull, drops, rte_memory_order_relaxed);
199 : 0 : rte_pktmbuf_free_bulk(&dup_bufs[ring_enq], drops);
200 : : }
201 : : }
202 : :
203 : : /* Create a clone of mbuf to be placed into ring. */
204 : : static void
205 : : pdump_copy(uint16_t port_id, uint16_t queue_id,
206 : : enum rte_pcapng_direction direction,
207 : : struct rte_mbuf **pkts, uint16_t nb_pkts,
208 : : const struct pdump_rxtx_cbs *cbs,
209 : : struct rte_pdump_stats *stats)
210 : : {
211 : : unsigned int offs = 0;
212 : :
213 : : do {
214 : 0 : unsigned int n = RTE_MIN(nb_pkts - offs, PDUMP_BURST_SIZE);
215 : :
216 : 0 : pdump_copy_burst(port_id, queue_id, direction, &pkts[offs], n, cbs, stats);
217 : 0 : offs += n;
218 [ # # # # ]: 0 : } while (offs < nb_pkts);
219 : : }
220 : :
221 : : static uint16_t
222 : 0 : pdump_rx(uint16_t port, uint16_t queue,
223 : : struct rte_mbuf **pkts, uint16_t nb_pkts,
224 : : uint16_t max_pkts __rte_unused, void *user_params)
225 : : {
226 : : struct pdump_rxtx_cbs *cbs = user_params;
227 : 0 : struct rte_pdump_stats *stats = &pdump_stats->rx[port][queue];
228 : :
229 : : pdump_cb_hold(cbs);
230 : : pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_IN,
231 : : pkts, nb_pkts, cbs, stats);
232 : : pdump_cb_release(cbs);
233 : :
234 : 0 : return nb_pkts;
235 : : }
236 : :
237 : : static uint16_t
238 : 0 : pdump_tx(uint16_t port, uint16_t queue,
239 : : struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params)
240 : : {
241 : : struct pdump_rxtx_cbs *cbs = user_params;
242 : 0 : struct rte_pdump_stats *stats = &pdump_stats->tx[port][queue];
243 : :
244 : : pdump_cb_hold(cbs);
245 : : pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_OUT,
246 : : pkts, nb_pkts, cbs, stats);
247 : : pdump_cb_release(cbs);
248 : :
249 : 0 : return nb_pkts;
250 : : }
251 : :
252 : :
253 : : static int
254 : 0 : pdump_register_rx_callbacks(enum pdump_version ver,
255 : : uint16_t end_q, uint16_t port, uint16_t queue,
256 : : struct rte_ring *ring, struct rte_mempool *mp,
257 : : struct rte_bpf *filter,
258 : : uint16_t operation, uint32_t snaplen)
259 : : {
260 : : uint16_t qid;
261 : :
262 [ # # ]: 0 : qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue;
263 [ # # ]: 0 : for (; qid < end_q; qid++) {
264 : 0 : struct pdump_rxtx_cbs *cbs = &rx_cbs[port][qid];
265 : :
266 [ # # ]: 0 : if (operation == ENABLE) {
267 [ # # ]: 0 : if (cbs->cb) {
268 : 0 : PDUMP_LOG_LINE(ERR,
269 : : "rx callback for port=%d queue=%d, already exists",
270 : : port, qid);
271 : 0 : return -EEXIST;
272 : : }
273 : 0 : cbs->use_count = 0;
274 : 0 : cbs->ver = ver;
275 : 0 : cbs->ring = ring;
276 : 0 : cbs->mp = mp;
277 : 0 : cbs->snaplen = snaplen;
278 : 0 : cbs->filter = filter;
279 : :
280 : 0 : cbs->cb = rte_eth_add_first_rx_callback(port, qid,
281 : : pdump_rx, cbs);
282 [ # # ]: 0 : if (cbs->cb == NULL) {
283 : 0 : PDUMP_LOG_LINE(ERR,
284 : : "failed to add rx callback, errno=%d",
285 : : rte_errno);
286 : 0 : return rte_errno;
287 : : }
288 : :
289 : 0 : memset(&pdump_stats->rx[port][qid], 0, sizeof(struct rte_pdump_stats));
290 [ # # ]: 0 : } else if (operation == DISABLE) {
291 : : int ret;
292 : :
293 [ # # ]: 0 : if (cbs->cb == NULL) {
294 : 0 : PDUMP_LOG_LINE(ERR,
295 : : "no existing rx callback for port=%d queue=%d",
296 : : port, qid);
297 : 0 : return -EINVAL;
298 : : }
299 : 0 : ret = rte_eth_remove_rx_callback(port, qid, cbs->cb);
300 [ # # ]: 0 : if (ret < 0) {
301 : 0 : PDUMP_LOG_LINE(ERR,
302 : : "failed to remove rx callback, errno=%d",
303 : : -ret);
304 : 0 : return ret;
305 : : }
306 : 0 : pdump_cb_wait(cbs);
307 : 0 : cbs->cb = NULL;
308 : : }
309 : : }
310 : :
311 : : return 0;
312 : : }
313 : :
314 : : static int
315 : 0 : pdump_register_tx_callbacks(enum pdump_version ver,
316 : : uint16_t end_q, uint16_t port, uint16_t queue,
317 : : struct rte_ring *ring, struct rte_mempool *mp,
318 : : struct rte_bpf *filter,
319 : : uint16_t operation, uint32_t snaplen)
320 : : {
321 : :
322 : : uint16_t qid;
323 : :
324 [ # # ]: 0 : qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue;
325 [ # # ]: 0 : for (; qid < end_q; qid++) {
326 : 0 : struct pdump_rxtx_cbs *cbs = &tx_cbs[port][qid];
327 : :
328 [ # # ]: 0 : if (operation == ENABLE) {
329 [ # # ]: 0 : if (cbs->cb) {
330 : 0 : PDUMP_LOG_LINE(ERR,
331 : : "tx callback for port=%d queue=%d, already exists",
332 : : port, qid);
333 : 0 : return -EEXIST;
334 : : }
335 : 0 : cbs->use_count = 0;
336 : 0 : cbs->ver = ver;
337 : 0 : cbs->ring = ring;
338 : 0 : cbs->mp = mp;
339 : 0 : cbs->snaplen = snaplen;
340 : 0 : cbs->filter = filter;
341 : :
342 : 0 : cbs->cb = rte_eth_add_tx_callback(port, qid, pdump_tx,
343 : : cbs);
344 [ # # ]: 0 : if (cbs->cb == NULL) {
345 : 0 : PDUMP_LOG_LINE(ERR,
346 : : "failed to add tx callback, errno=%d",
347 : : rte_errno);
348 : 0 : return rte_errno;
349 : : }
350 : 0 : memset(&pdump_stats->tx[port][qid], 0, sizeof(struct rte_pdump_stats));
351 [ # # ]: 0 : } else if (operation == DISABLE) {
352 : : int ret;
353 : :
354 [ # # ]: 0 : if (cbs->cb == NULL) {
355 : 0 : PDUMP_LOG_LINE(ERR,
356 : : "no existing tx callback for port=%d queue=%d",
357 : : port, qid);
358 : 0 : return -EINVAL;
359 : : }
360 : 0 : ret = rte_eth_remove_tx_callback(port, qid, cbs->cb);
361 [ # # ]: 0 : if (ret < 0) {
362 : 0 : PDUMP_LOG_LINE(ERR,
363 : : "failed to remove tx callback, errno=%d",
364 : : -ret);
365 : 0 : return ret;
366 : : }
367 : :
368 : 0 : pdump_cb_wait(cbs);
369 : 0 : cbs->cb = NULL;
370 : : }
371 : : }
372 : :
373 : : return 0;
374 : : }
375 : :
376 : : static int
377 : 0 : set_pdump_rxtx_cbs(const struct pdump_request *p)
378 : : {
379 : : uint16_t nb_rx_q = 0, nb_tx_q = 0, end_q, queue;
380 : : uint16_t port;
381 : : int ret = 0;
382 : : struct rte_bpf *filter = NULL;
383 : : uint32_t flags;
384 : : uint16_t operation;
385 : : struct rte_ring *ring;
386 : : struct rte_mempool *mp;
387 : :
388 : : /* Check for possible DPDK version mismatch */
389 [ # # ]: 0 : if (!(p->ver == V1 || p->ver == V2)) {
390 : 0 : PDUMP_LOG_LINE(ERR,
391 : : "incorrect client version %u", p->ver);
392 : 0 : return -EINVAL;
393 : : }
394 : :
395 [ # # ]: 0 : if (p->prm) {
396 [ # # ]: 0 : if (p->prm->prog_arg.type != RTE_BPF_ARG_PTR_MBUF) {
397 : 0 : PDUMP_LOG_LINE(ERR,
398 : : "invalid BPF program type: %u",
399 : : p->prm->prog_arg.type);
400 : 0 : return -EINVAL;
401 : : }
402 : :
403 : 0 : filter = rte_bpf_load(p->prm);
404 [ # # ]: 0 : if (filter == NULL) {
405 : 0 : PDUMP_LOG_LINE(ERR, "cannot load BPF filter: %s",
406 : : rte_strerror(rte_errno));
407 : 0 : return -rte_errno;
408 : : }
409 : : }
410 : :
411 : 0 : flags = p->flags;
412 : 0 : operation = p->op;
413 : 0 : queue = p->queue;
414 : 0 : ring = p->ring;
415 : 0 : mp = p->mp;
416 : :
417 : 0 : ret = rte_eth_dev_get_port_by_name(p->device, &port);
418 [ # # ]: 0 : if (ret < 0) {
419 : 0 : PDUMP_LOG_LINE(ERR,
420 : : "failed to get port id for device id=%s",
421 : : p->device);
422 : 0 : return -EINVAL;
423 : : }
424 : :
425 : : /* validation if packet capture is for all queues */
426 [ # # ]: 0 : if (queue == RTE_PDUMP_ALL_QUEUES) {
427 : : struct rte_eth_dev_info dev_info;
428 : :
429 : 0 : ret = rte_eth_dev_info_get(port, &dev_info);
430 [ # # ]: 0 : if (ret != 0) {
431 : 0 : PDUMP_LOG_LINE(ERR,
432 : : "Error during getting device (port %u) info: %s",
433 : : port, strerror(-ret));
434 : 0 : return ret;
435 : : }
436 : :
437 : 0 : nb_rx_q = dev_info.nb_rx_queues;
438 : 0 : nb_tx_q = dev_info.nb_tx_queues;
439 [ # # # # ]: 0 : if (nb_rx_q == 0 && flags & RTE_PDUMP_FLAG_RX) {
440 : 0 : PDUMP_LOG_LINE(ERR,
441 : : "number of rx queues cannot be 0");
442 : 0 : return -EINVAL;
443 : : }
444 [ # # # # ]: 0 : if (nb_tx_q == 0 && flags & RTE_PDUMP_FLAG_TX) {
445 : 0 : PDUMP_LOG_LINE(ERR,
446 : : "number of tx queues cannot be 0");
447 : 0 : return -EINVAL;
448 : : }
449 [ # # # # ]: 0 : if ((nb_tx_q == 0 || nb_rx_q == 0) &&
450 : : flags == RTE_PDUMP_FLAG_RXTX) {
451 : 0 : PDUMP_LOG_LINE(ERR,
452 : : "both tx&rx queues must be non zero");
453 : 0 : return -EINVAL;
454 : : }
455 : : }
456 : :
457 : : /* register RX callback */
458 [ # # ]: 0 : if (flags & RTE_PDUMP_FLAG_RX) {
459 [ # # ]: 0 : end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_rx_q : queue + 1;
460 : 0 : ret = pdump_register_rx_callbacks(p->ver, end_q, port, queue,
461 : : ring, mp, filter,
462 : 0 : operation, p->snaplen);
463 [ # # ]: 0 : if (ret < 0)
464 : : return ret;
465 : : }
466 : :
467 : : /* register TX callback */
468 [ # # ]: 0 : if (flags & RTE_PDUMP_FLAG_TX) {
469 [ # # ]: 0 : end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_tx_q : queue + 1;
470 : 0 : ret = pdump_register_tx_callbacks(p->ver, end_q, port, queue,
471 : : ring, mp, filter,
472 : 0 : operation, p->snaplen);
473 : : if (ret < 0)
474 : : return ret;
475 : : }
476 : :
477 : : return ret;
478 : : }
479 : :
480 : : static void
481 : 0 : pdump_request_to_secondary(const struct pdump_request *req)
482 : : {
483 : 0 : struct rte_mp_msg mp_req = { };
484 : : struct rte_mp_reply mp_reply;
485 : 0 : struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
486 : :
487 : 0 : PDUMP_LOG_LINE(DEBUG, "forward req %s to secondary", pdump_opname(req->op));
488 : :
489 : : memcpy(mp_req.param, req, sizeof(*req));
490 : : strlcpy(mp_req.name, PDUMP_MP, sizeof(mp_req.name));
491 : 0 : mp_req.len_param = sizeof(*req);
492 : :
493 [ # # ]: 0 : if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) != 0)
494 : 0 : PDUMP_LOG_LINE(ERR, "rte_mp_request_sync failed");
495 : :
496 [ # # ]: 0 : else if (mp_reply.nb_sent != mp_reply.nb_received)
497 : 0 : PDUMP_LOG_LINE(ERR, "not all secondary's replied (sent %u recv %u)",
498 : : mp_reply.nb_sent, mp_reply.nb_received);
499 : :
500 : 0 : free(mp_reply.msgs);
501 : 0 : }
502 : :
503 : : /* Allocate temporary storage for passing state to the alarm thread for deferred handling */
504 : : static struct pdump_bundle *
505 : 0 : pdump_bundle_alloc(const struct rte_mp_msg *mp_msg, const char *peer)
506 : : {
507 : : struct pdump_bundle *bundle;
508 : 0 : size_t peer_len = strlen(peer) + 1;
509 : :
510 : : /* peer is the unix domain socket path */
511 : 0 : bundle = malloc(sizeof(*bundle) + peer_len);
512 [ # # ]: 0 : if (bundle == NULL)
513 : : return NULL;
514 : :
515 : 0 : bundle->msg = *mp_msg;
516 : 0 : memcpy(bundle->peer, peer, peer_len);
517 : 0 : return bundle;
518 : : }
519 : :
520 : : /* Send response to peer */
521 : : static int
522 : 0 : pdump_send_response(const struct pdump_request *req, int result, const void *peer)
523 : : {
524 : 0 : struct rte_mp_msg mp_resp = { };
525 : : struct pdump_response *resp = (struct pdump_response *)mp_resp.param;
526 : : int ret;
527 : :
528 [ # # ]: 0 : if (req) {
529 : 0 : resp->ver = req->ver;
530 : 0 : resp->res_op = req->op;
531 : : }
532 : 0 : resp->err_value = result;
533 : :
534 : 0 : rte_strscpy(mp_resp.name, PDUMP_MP, RTE_MP_MAX_NAME_LEN);
535 : 0 : mp_resp.len_param = sizeof(*resp);
536 : :
537 : 0 : ret = rte_mp_reply(&mp_resp, peer);
538 [ # # ]: 0 : if (ret != 0)
539 : 0 : PDUMP_LOG_LINE(ERR, "failed to send response: %s",
540 : : strerror(rte_errno));
541 : 0 : return ret;
542 : : }
543 : :
544 : : /* Callback from MP request handler in secondary process */
545 : : static int
546 : 0 : pdump_handle_primary_request(const struct rte_mp_msg *mp_msg, const void *peer)
547 : : {
548 : : const struct pdump_request *req = NULL;
549 : : int ret;
550 : :
551 [ # # ]: 0 : if (mp_msg->len_param != sizeof(*req)) {
552 : 0 : PDUMP_LOG_LINE(ERR, "invalid request from primary");
553 : : ret = -EINVAL;
554 : : } else {
555 : 0 : req = (const struct pdump_request *)mp_msg->param;
556 : 0 : PDUMP_LOG_LINE(DEBUG, "secondary pdump %s", pdump_opname(req->op));
557 : :
558 : : /* Can just do it now, no need for interrupt thread */
559 : 0 : ret = set_pdump_rxtx_cbs(req);
560 : : }
561 : :
562 : 0 : return pdump_send_response(req, ret, peer);
563 : :
564 : : }
565 : :
566 : : /* Callback from the alarm handler (in interrupt thread) which does actual change */
567 : : static void
568 : 0 : __pdump_request(void *param)
569 : : {
570 : : struct pdump_bundle *bundle = param;
571 : : struct rte_mp_msg *msg = &bundle->msg;
572 : 0 : const struct pdump_request *req =
573 : : (const struct pdump_request *)msg->param;
574 : : int ret;
575 : :
576 : 0 : PDUMP_LOG_LINE(DEBUG, "primary pdump %s", pdump_opname(req->op));
577 : :
578 : 0 : ret = set_pdump_rxtx_cbs(req);
579 : :
580 : : /* Primary process is responsible for broadcasting request to all secondaries */
581 [ # # ]: 0 : if (ret == 0)
582 : 0 : pdump_request_to_secondary(req);
583 : :
584 : 0 : pdump_send_response(req, ret, bundle->peer);
585 : 0 : free(bundle);
586 : 0 : }
587 : :
588 : : /* Callback from MP request handler in primary process */
589 : : static int
590 : 0 : pdump_handle_secondary_request(const struct rte_mp_msg *mp_msg, const void *peer)
591 : : {
592 : : struct pdump_bundle *bundle = NULL;
593 : : const struct pdump_request *req = NULL;
594 : : int ret;
595 : :
596 [ # # ]: 0 : if (mp_msg->len_param != sizeof(*req)) {
597 : 0 : PDUMP_LOG_LINE(ERR, "invalid request from secondary");
598 : : ret = -EINVAL;
599 : 0 : goto error;
600 : : }
601 : :
602 : 0 : req = (const struct pdump_request *)mp_msg->param;
603 : :
604 : 0 : bundle = pdump_bundle_alloc(mp_msg, peer);
605 [ # # ]: 0 : if (bundle == NULL) {
606 : 0 : PDUMP_LOG_LINE(ERR, "not enough memory");
607 : : ret = -ENOMEM;
608 : 0 : goto error;
609 : : }
610 : :
611 : : /*
612 : : * We are in IPC callback thread, sync IPC is not possible
613 : : * since sending to secondary would cause livelock.
614 : : * Delegate the task to interrupt thread.
615 : : */
616 : 0 : ret = rte_eal_alarm_set(1, __pdump_request, bundle);
617 [ # # ]: 0 : if (ret != 0)
618 : 0 : goto error;
619 : : return 0;
620 : :
621 : 0 : error:
622 : 0 : free(bundle);
623 : 0 : return pdump_send_response(req, ret, peer);
624 : : }
625 : :
626 : : RTE_EXPORT_SYMBOL(rte_pdump_init)
627 : : int
628 : 1 : rte_pdump_init(void)
629 : : {
630 : : const struct rte_memzone *mz;
631 : : int ret;
632 : :
633 [ + - ]: 1 : if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
634 : 1 : ret = rte_mp_action_register(PDUMP_MP, pdump_handle_secondary_request);
635 [ - + - - ]: 1 : if (ret && rte_errno != ENOTSUP)
636 : : return -1;
637 : :
638 : 1 : mz = rte_memzone_reserve(MZ_RTE_PDUMP_STATS, sizeof(*pdump_stats),
639 : : SOCKET_ID_ANY, 0);
640 [ - + ]: 1 : if (mz == NULL) {
641 : 0 : PDUMP_LOG_LINE(ERR, "cannot allocate pdump statistics");
642 : 0 : rte_mp_action_unregister(PDUMP_MP);
643 : 0 : rte_errno = ENOMEM;
644 : 0 : return -1;
645 : : }
646 : : } else {
647 : 0 : ret = rte_mp_action_register(PDUMP_MP, pdump_handle_primary_request);
648 [ # # # # ]: 0 : if (ret && rte_errno != ENOTSUP)
649 : : return -1;
650 : :
651 : 0 : mz = rte_memzone_lookup(MZ_RTE_PDUMP_STATS);
652 [ # # ]: 0 : if (mz == NULL) {
653 : 0 : PDUMP_LOG_LINE(ERR, "cannot find pdump statistics");
654 : 0 : rte_mp_action_unregister(PDUMP_MP);
655 : 0 : rte_errno = ENOENT;
656 : 0 : return -1;
657 : : }
658 : : }
659 : :
660 : 1 : pdump_stats = mz->addr;
661 : 1 : pdump_stats->mz = mz;
662 : :
663 : 1 : return 0;
664 : : }
665 : :
666 : : RTE_EXPORT_SYMBOL(rte_pdump_uninit)
667 : : int
668 : 1 : rte_pdump_uninit(void)
669 : : {
670 : 1 : rte_mp_action_unregister(PDUMP_MP);
671 : :
672 [ + - + - ]: 1 : if (rte_eal_process_type() == RTE_PROC_PRIMARY && pdump_stats != NULL) {
673 : 1 : rte_memzone_free(pdump_stats->mz);
674 : 1 : pdump_stats = NULL;
675 : : }
676 : :
677 : 1 : return 0;
678 : : }
679 : :
680 : : static int
681 : 0 : pdump_validate_ring_mp(struct rte_ring *ring, struct rte_mempool *mp)
682 : : {
683 [ # # ]: 0 : if (ring == NULL || mp == NULL) {
684 : 0 : PDUMP_LOG_LINE(ERR, "NULL ring or mempool");
685 : 0 : rte_errno = EINVAL;
686 : 0 : return -1;
687 : : }
688 [ # # ]: 0 : if (mp->flags & RTE_MEMPOOL_F_SP_PUT ||
689 : : mp->flags & RTE_MEMPOOL_F_SC_GET) {
690 : 0 : PDUMP_LOG_LINE(ERR,
691 : : "mempool with SP or SC set not valid for pdump,"
692 : : "must have MP and MC set");
693 : 0 : rte_errno = EINVAL;
694 : 0 : return -1;
695 : : }
696 [ # # # # ]: 0 : if (rte_ring_is_prod_single(ring) || rte_ring_is_cons_single(ring)) {
697 : 0 : PDUMP_LOG_LINE(ERR,
698 : : "ring with SP or SC set is not valid for pdump,"
699 : : "must have MP and MC set");
700 : 0 : rte_errno = EINVAL;
701 : 0 : return -1;
702 : : }
703 : :
704 : : return 0;
705 : : }
706 : :
707 : : static int
708 : 0 : pdump_validate_flags(uint32_t flags)
709 : : {
710 [ # # ]: 0 : if ((flags & RTE_PDUMP_FLAG_RXTX) == 0) {
711 : 0 : PDUMP_LOG_LINE(ERR,
712 : : "invalid flags, should be either rx/tx/rxtx");
713 : 0 : rte_errno = EINVAL;
714 : 0 : return -1;
715 : : }
716 : :
717 : : /* mask off the flags we know about */
718 [ # # ]: 0 : if (flags & ~(RTE_PDUMP_FLAG_RXTX | RTE_PDUMP_FLAG_PCAPNG)) {
719 : 0 : PDUMP_LOG_LINE(ERR,
720 : : "unknown flags: %#x", flags);
721 : 0 : rte_errno = ENOTSUP;
722 : 0 : return -1;
723 : : }
724 : :
725 : : return 0;
726 : : }
727 : :
728 : : static int
729 : 0 : pdump_validate_port(uint16_t port, char *name)
730 : : {
731 : : int ret = 0;
732 : :
733 [ # # ]: 0 : if (port >= RTE_MAX_ETHPORTS) {
734 : 0 : PDUMP_LOG_LINE(ERR, "Invalid port id %u", port);
735 : 0 : rte_errno = EINVAL;
736 : 0 : return -1;
737 : : }
738 : :
739 : 0 : ret = rte_eth_dev_get_name_by_port(port, name);
740 [ # # ]: 0 : if (ret < 0) {
741 : 0 : PDUMP_LOG_LINE(ERR, "port %u to name mapping failed",
742 : : port);
743 : 0 : rte_errno = EINVAL;
744 : 0 : return -1;
745 : : }
746 : :
747 : : return 0;
748 : : }
749 : :
750 : : static int
751 : 0 : pdump_prepare_client_request(const char *device, uint16_t queue,
752 : : uint32_t flags, uint32_t snaplen,
753 : : uint16_t operation,
754 : : struct rte_ring *ring,
755 : : struct rte_mempool *mp,
756 : : const struct rte_bpf_prm *prm)
757 : : {
758 : : int ret = -1;
759 : : struct rte_mp_msg mp_req, *mp_rep;
760 : : struct rte_mp_reply mp_reply;
761 : 0 : struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
762 : : struct pdump_request *req = (struct pdump_request *)mp_req.param;
763 : : struct pdump_response *resp;
764 : :
765 [ # # ]: 0 : if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
766 : : /* FIXME */
767 : 0 : PDUMP_LOG_LINE(ERR,
768 : : "pdump enable/disable not allowed in primary process");
769 : 0 : return -EINVAL;
770 : : }
771 : :
772 : : memset(req, 0, sizeof(*req));
773 : :
774 [ # # ]: 0 : req->ver = (flags & RTE_PDUMP_FLAG_PCAPNG) ? V2 : V1;
775 : 0 : req->flags = flags & RTE_PDUMP_FLAG_RXTX;
776 : 0 : req->op = operation;
777 : 0 : req->queue = queue;
778 : 0 : rte_strscpy(req->device, device, sizeof(req->device));
779 : :
780 [ # # ]: 0 : if ((operation & ENABLE) != 0) {
781 : 0 : req->ring = ring;
782 : 0 : req->mp = mp;
783 : 0 : req->prm = prm;
784 : 0 : req->snaplen = snaplen;
785 : : }
786 : :
787 : 0 : rte_strscpy(mp_req.name, PDUMP_MP, RTE_MP_MAX_NAME_LEN);
788 : 0 : mp_req.len_param = sizeof(*req);
789 : 0 : mp_req.num_fds = 0;
790 [ # # ]: 0 : if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) == 0) {
791 : 0 : mp_rep = &mp_reply.msgs[0];
792 : : resp = (struct pdump_response *)mp_rep->param;
793 [ # # ]: 0 : if (resp->err_value == 0)
794 : : ret = 0;
795 : : else
796 : 0 : rte_errno = -resp->err_value;
797 : 0 : free(mp_reply.msgs);
798 : : }
799 : :
800 [ # # ]: 0 : if (ret < 0)
801 : 0 : PDUMP_LOG_LINE(ERR,
802 : : "client request for pdump enable/disable failed");
803 : : return ret;
804 : : }
805 : :
806 : : /*
807 : : * There are two versions of this function, because although original API
808 : : * left place holder for future filter, it never checked the value.
809 : : * Therefore the API can't depend on application passing a non
810 : : * bogus value.
811 : : */
812 : : static int
813 : 0 : pdump_enable(uint16_t port, uint16_t queue,
814 : : uint32_t flags, uint32_t snaplen,
815 : : struct rte_ring *ring, struct rte_mempool *mp,
816 : : const struct rte_bpf_prm *prm)
817 : : {
818 : : int ret;
819 : : char name[RTE_DEV_NAME_MAX_LEN];
820 : :
821 : 0 : ret = pdump_validate_port(port, name);
822 [ # # ]: 0 : if (ret < 0)
823 : : return ret;
824 : 0 : ret = pdump_validate_ring_mp(ring, mp);
825 [ # # ]: 0 : if (ret < 0)
826 : : return ret;
827 : 0 : ret = pdump_validate_flags(flags);
828 [ # # ]: 0 : if (ret < 0)
829 : : return ret;
830 : :
831 [ # # ]: 0 : if (snaplen == 0)
832 : : snaplen = UINT32_MAX;
833 : :
834 : 0 : return pdump_prepare_client_request(name, queue, flags, snaplen,
835 : : ENABLE, ring, mp, prm);
836 : : }
837 : :
838 : : RTE_EXPORT_SYMBOL(rte_pdump_enable)
839 : : int
840 : 0 : rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags,
841 : : struct rte_ring *ring,
842 : : struct rte_mempool *mp,
843 : : void *filter __rte_unused)
844 : : {
845 : 0 : return pdump_enable(port, queue, flags, 0,
846 : : ring, mp, NULL);
847 : : }
848 : :
849 : : RTE_EXPORT_SYMBOL(rte_pdump_enable_bpf)
850 : : int
851 : 0 : rte_pdump_enable_bpf(uint16_t port, uint16_t queue,
852 : : uint32_t flags, uint32_t snaplen,
853 : : struct rte_ring *ring,
854 : : struct rte_mempool *mp,
855 : : const struct rte_bpf_prm *prm)
856 : : {
857 : 0 : return pdump_enable(port, queue, flags, snaplen,
858 : : ring, mp, prm);
859 : : }
860 : :
861 : : static int
862 : 0 : pdump_enable_by_deviceid(const char *device_id, uint16_t queue,
863 : : uint32_t flags, uint32_t snaplen,
864 : : struct rte_ring *ring,
865 : : struct rte_mempool *mp,
866 : : const struct rte_bpf_prm *prm)
867 : : {
868 : : int ret;
869 : :
870 : 0 : ret = pdump_validate_ring_mp(ring, mp);
871 [ # # ]: 0 : if (ret < 0)
872 : : return ret;
873 : 0 : ret = pdump_validate_flags(flags);
874 [ # # ]: 0 : if (ret < 0)
875 : : return ret;
876 : :
877 [ # # ]: 0 : if (snaplen == 0)
878 : : snaplen = UINT32_MAX;
879 : :
880 : 0 : return pdump_prepare_client_request(device_id, queue, flags, snaplen,
881 : : ENABLE, ring, mp, prm);
882 : : }
883 : :
884 : : RTE_EXPORT_SYMBOL(rte_pdump_enable_by_deviceid)
885 : : int
886 : 0 : rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue,
887 : : uint32_t flags,
888 : : struct rte_ring *ring,
889 : : struct rte_mempool *mp,
890 : : void *filter __rte_unused)
891 : : {
892 : 0 : return pdump_enable_by_deviceid(device_id, queue, flags, 0,
893 : : ring, mp, NULL);
894 : : }
895 : :
896 : : RTE_EXPORT_SYMBOL(rte_pdump_enable_bpf_by_deviceid)
897 : : int
898 : 0 : rte_pdump_enable_bpf_by_deviceid(const char *device_id, uint16_t queue,
899 : : uint32_t flags, uint32_t snaplen,
900 : : struct rte_ring *ring,
901 : : struct rte_mempool *mp,
902 : : const struct rte_bpf_prm *prm)
903 : : {
904 : 0 : return pdump_enable_by_deviceid(device_id, queue, flags, snaplen,
905 : : ring, mp, prm);
906 : : }
907 : :
908 : : RTE_EXPORT_SYMBOL(rte_pdump_disable)
909 : : int
910 : 0 : rte_pdump_disable(uint16_t port, uint16_t queue, uint32_t flags)
911 : : {
912 : : int ret = 0;
913 : : char name[RTE_DEV_NAME_MAX_LEN];
914 : :
915 : 0 : ret = pdump_validate_port(port, name);
916 [ # # ]: 0 : if (ret < 0)
917 : : return ret;
918 : 0 : ret = pdump_validate_flags(flags);
919 [ # # ]: 0 : if (ret < 0)
920 : : return ret;
921 : :
922 : 0 : ret = pdump_prepare_client_request(name, queue, flags, 0,
923 : : DISABLE, NULL, NULL, NULL);
924 : :
925 : 0 : return ret;
926 : : }
927 : :
928 : : RTE_EXPORT_SYMBOL(rte_pdump_disable_by_deviceid)
929 : : int
930 : 0 : rte_pdump_disable_by_deviceid(char *device_id, uint16_t queue,
931 : : uint32_t flags)
932 : : {
933 : : int ret = 0;
934 : :
935 : 0 : ret = pdump_validate_flags(flags);
936 [ # # ]: 0 : if (ret < 0)
937 : : return ret;
938 : :
939 : 0 : ret = pdump_prepare_client_request(device_id, queue, flags, 0,
940 : : DISABLE, NULL, NULL, NULL);
941 : :
942 : 0 : return ret;
943 : : }
944 : :
945 : : static void
946 : 0 : pdump_sum_stats(uint16_t port, uint16_t nq,
947 : : struct rte_pdump_stats stats[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT],
948 : : struct rte_pdump_stats *total)
949 : : {
950 : : uint64_t *sum = (uint64_t *)total;
951 : : unsigned int i;
952 : : uint64_t val;
953 : : uint16_t qid;
954 : :
955 [ # # ]: 0 : for (qid = 0; qid < nq; qid++) {
956 : 0 : const RTE_ATOMIC(uint64_t) *perq = (const uint64_t __rte_atomic *)&stats[port][qid];
957 : :
958 [ # # ]: 0 : for (i = 0; i < sizeof(*total) / sizeof(uint64_t); i++) {
959 : 0 : val = rte_atomic_load_explicit(&perq[i], rte_memory_order_relaxed);
960 : 0 : sum[i] += val;
961 : : }
962 : : }
963 : 0 : }
964 : :
965 : : RTE_EXPORT_SYMBOL(rte_pdump_stats)
966 : : int
967 : 0 : rte_pdump_stats(uint16_t port, struct rte_pdump_stats *stats)
968 : : {
969 : : struct rte_eth_dev_info dev_info;
970 : : const struct rte_memzone *mz;
971 : : int ret;
972 : :
973 : : memset(stats, 0, sizeof(*stats));
974 : 0 : ret = rte_eth_dev_info_get(port, &dev_info);
975 [ # # ]: 0 : if (ret != 0) {
976 : 0 : PDUMP_LOG_LINE(ERR,
977 : : "Error during getting device (port %u) info: %s",
978 : : port, strerror(-ret));
979 : 0 : return ret;
980 : : }
981 : :
982 [ # # ]: 0 : if (pdump_stats == NULL) {
983 [ # # ]: 0 : if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
984 : : /* rte_pdump_init was not called */
985 : 0 : PDUMP_LOG_LINE(ERR, "pdump stats not initialized");
986 : 0 : rte_errno = EINVAL;
987 : 0 : return -1;
988 : : }
989 : :
990 : : /* secondary process looks up the memzone */
991 : 0 : mz = rte_memzone_lookup(MZ_RTE_PDUMP_STATS);
992 [ # # ]: 0 : if (mz == NULL) {
993 : : /* rte_pdump_init was not called in primary process?? */
994 : 0 : PDUMP_LOG_LINE(ERR, "can not find pdump stats");
995 : 0 : rte_errno = EINVAL;
996 : 0 : return -1;
997 : : }
998 : 0 : pdump_stats = mz->addr;
999 : : }
1000 : :
1001 : 0 : pdump_sum_stats(port, dev_info.nb_rx_queues, pdump_stats->rx, stats);
1002 : 0 : pdump_sum_stats(port, dev_info.nb_tx_queues, pdump_stats->tx, stats);
1003 : 0 : return 0;
1004 : : }
|