* [RFC 0/6] Packet capture fixes
@ 2025-08-11 21:34 Stephen Hemminger
2025-08-11 21:34 ` [RFC 1/6] dumpcap: handle primary process exit Stephen Hemminger
` (6 more replies)
0 siblings, 7 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-11 21:34 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
This patch series addresses the problem of packet capture when
the secondary process is sending/receiving by using the same
mechanism as hot plug. It also fixes some bugs on shutdown
of dumpcap (and pdump).
In draft form at this point, works but needs more testing.
Stephen Hemminger (6):
dumpcap: handle primary process exit
pdump: handle primary process exit
pdump: fix races in callbacks
dumpcap: handle pdump requests from primary
pdump: handle pdump requests from primary
pdump: forward callback enable to secondary
app/dumpcap/main.c | 14 +++
app/pdump/main.c | 16 ++-
lib/pdump/rte_pdump.c | 261 +++++++++++++++++++++++++++++++++++++-----
3 files changed, 256 insertions(+), 35 deletions(-)
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC 1/6] dumpcap: handle primary process exit
2025-08-11 21:34 [RFC 0/6] Packet capture fixes Stephen Hemminger
@ 2025-08-11 21:34 ` Stephen Hemminger
2025-08-11 21:35 ` [RFC 2/6] pdump: " Stephen Hemminger
` (5 subsequent siblings)
6 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-11 21:34 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, stable
If primary process exits, then it is not possible (or needed)
to cleanup resources. Instead just exit after closing the
capture file.
Bugzilla ID: 1760
Fixes: cbb44143be74 ("app/dumpcap: add new packet capture application")
Cc: stable@dpdk.org
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/dumpcap/main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 3d3c0dbc66..7b19c830b5 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -1058,6 +1058,10 @@ int main(int argc, char **argv)
else
pcap_dump_close(out.dumper);
+ /* If primary has exited, do not try and communicate with it */
+ if (!rte_eal_primary_proc_alive(NULL))
+ return 0;
+
cleanup_pdump_resources();
rte_ring_free(r);
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC 2/6] pdump: handle primary process exit
2025-08-11 21:34 [RFC 0/6] Packet capture fixes Stephen Hemminger
2025-08-11 21:34 ` [RFC 1/6] dumpcap: handle primary process exit Stephen Hemminger
@ 2025-08-11 21:35 ` Stephen Hemminger
2025-08-11 21:35 ` [RFC 3/6] pdump: fix races in callbacks Stephen Hemminger
` (4 subsequent siblings)
6 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-11 21:35 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, stable
If primary process exits, then it is not possible (or needed)
to cleanup resources. Instead just exit after closing the
capture file.
Bugzilla ID: 1760
Fixes: a99a311ba101 ("app/pdump: exit with primary process")
Cc: stable@dpdk.org
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/pdump/main.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/app/pdump/main.c b/app/pdump/main.c
index fa85859703..1741d7e709 100644
--- a/app/pdump/main.c
+++ b/app/pdump/main.c
@@ -1028,13 +1028,15 @@ main(int argc, char **argv)
dump_packets();
disable_primary_monitor();
- cleanup_pdump_resources();
+
/* dump debug stats */
print_pdump_stats();
- ret = rte_eal_cleanup();
- if (ret)
- printf("Error from rte_eal_cleanup(), %d\n", ret);
+ /* If primary has exited, do not try and communicate with it */
+ if (!rte_eal_primary_proc_alive(NULL))
+ return 0;
- return 0;
+ cleanup_pdump_resources();
+
+ return rte_eal_cleanup() ? EXIT_FAILURE : 0;
}
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC 3/6] pdump: fix races in callbacks
2025-08-11 21:34 [RFC 0/6] Packet capture fixes Stephen Hemminger
2025-08-11 21:34 ` [RFC 1/6] dumpcap: handle primary process exit Stephen Hemminger
2025-08-11 21:35 ` [RFC 2/6] pdump: " Stephen Hemminger
@ 2025-08-11 21:35 ` Stephen Hemminger
2025-08-11 21:35 ` [RFC 4/6] dumpcap: handle pdump requests from primary Stephen Hemminger
` (3 subsequent siblings)
6 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-11 21:35 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
The pdump callback can race with other cpu's in the datapath.
Handle this by using reference counts and LSB in manner
similar to seqcount and bpf code.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/pdump/rte_pdump.c | 48 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 46 insertions(+), 2 deletions(-)
diff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c
index ba75b828f2..bfd63fa8c2 100644
--- a/lib/pdump/rte_pdump.c
+++ b/lib/pdump/rte_pdump.c
@@ -12,6 +12,7 @@
#include <rte_memzone.h>
#include <rte_errno.h>
#include <rte_string_fns.h>
+#include <rte_pause.h>
#include <rte_pcapng.h>
#include "rte_pdump.h"
@@ -62,6 +63,7 @@ static struct pdump_rxtx_cbs {
const struct rte_bpf *filter;
enum pdump_version ver;
uint32_t snaplen;
+ RTE_ATOMIC(uint32_t) use_count;
} rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT],
tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
@@ -78,6 +80,36 @@ static struct {
const struct rte_memzone *mz;
} *pdump_stats;
+static void
+pdump_cb_wait(struct pdump_rxtx_cbs *cbs)
+{
+ /* make sure the data loads happens before the use count load */
+ rte_atomic_thread_fence(rte_memory_order_acquire);
+
+ /* wait until use_count is even (not in use) */
+ RTE_WAIT_UNTIL_MASKED(&cbs->use_count, 1, ==, 0, rte_memory_order_relaxed);
+}
+
+static __rte_always_inline void
+pdump_cb_hold(struct pdump_rxtx_cbs *cbs)
+{
+ uint32_t count = cbs->use_count + 1;
+
+ rte_atomic_store_explicit(&cbs->use_count, count, rte_memory_order_relaxed);
+
+ /* prevent stores after this from happening before the use_count update */
+ rte_atomic_thread_fence(rte_memory_order_release);
+}
+
+static __rte_always_inline void
+pdump_cb_release(struct pdump_rxtx_cbs *cbs)
+{
+ uint32_t count = cbs->use_count + 1;
+
+ /* Synchronizes-with the load acquire in pdump_cb_wait */
+ rte_atomic_store_explicit(&cbs->use_count, count, rte_memory_order_release);
+}
+
/* Create a clone of mbuf to be placed into ring. */
static void
pdump_copy(uint16_t port_id, uint16_t queue,
@@ -146,11 +178,14 @@ pdump_rx(uint16_t port, uint16_t queue,
struct rte_mbuf **pkts, uint16_t nb_pkts,
uint16_t max_pkts __rte_unused, void *user_params)
{
- const struct pdump_rxtx_cbs *cbs = user_params;
+ struct pdump_rxtx_cbs *cbs = user_params;
struct rte_pdump_stats *stats = &pdump_stats->rx[port][queue];
+ pdump_cb_hold(cbs);
pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_IN,
pkts, nb_pkts, cbs, stats);
+ pdump_cb_release(cbs);
+
return nb_pkts;
}
@@ -158,14 +193,18 @@ static uint16_t
pdump_tx(uint16_t port, uint16_t queue,
struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params)
{
- const struct pdump_rxtx_cbs *cbs = user_params;
+ struct pdump_rxtx_cbs *cbs = user_params;
struct rte_pdump_stats *stats = &pdump_stats->tx[port][queue];
+ pdump_cb_hold(cbs);
pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_OUT,
pkts, nb_pkts, cbs, stats);
+ pdump_cb_release(cbs);
+
return nb_pkts;
}
+
static int
pdump_register_rx_callbacks(enum pdump_version ver,
uint16_t end_q, uint16_t port, uint16_t queue,
@@ -186,6 +225,7 @@ pdump_register_rx_callbacks(enum pdump_version ver,
port, qid);
return -EEXIST;
}
+ cbs->use_count = 0;
cbs->ver = ver;
cbs->ring = ring;
cbs->mp = mp;
@@ -218,6 +258,7 @@ pdump_register_rx_callbacks(enum pdump_version ver,
-ret);
return ret;
}
+ pdump_cb_wait(cbs);
cbs->cb = NULL;
}
}
@@ -246,6 +287,7 @@ pdump_register_tx_callbacks(enum pdump_version ver,
port, qid);
return -EEXIST;
}
+ cbs->use_count = 0;
cbs->ver = ver;
cbs->ring = ring;
cbs->mp = mp;
@@ -277,6 +319,8 @@ pdump_register_tx_callbacks(enum pdump_version ver,
-ret);
return ret;
}
+
+ pdump_cb_wait(cbs);
cbs->cb = NULL;
}
}
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC 4/6] dumpcap: handle pdump requests from primary
2025-08-11 21:34 [RFC 0/6] Packet capture fixes Stephen Hemminger
` (2 preceding siblings ...)
2025-08-11 21:35 ` [RFC 3/6] pdump: fix races in callbacks Stephen Hemminger
@ 2025-08-11 21:35 ` Stephen Hemminger
2025-08-11 21:35 ` [RFC 5/6] pdump: " Stephen Hemminger
` (2 subsequent siblings)
6 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-11 21:35 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
The primary process will start to notify all secondary processes
about pdump changes. The dumpcap secondary process can just call
rte_pdump_init() and it take care of that.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/dumpcap/main.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 7b19c830b5..c734fc7b9d 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -528,6 +528,8 @@ cleanup_pdump_resources(void)
if (intf->opts.promisc_mode)
rte_eth_promiscuous_disable(intf->port);
}
+
+ rte_pdump_uninit();
}
/* Alarm signal handler, used to check that primary process */
@@ -659,6 +661,14 @@ static void dpdk_init(void)
if (rte_eal_init(eal_argc, eal_argv) < 0)
rte_exit(EXIT_FAILURE, "EAL init failed: is primary process running?\n");
+ /*
+ * Register pdump callback handler.
+ * Primary will notify all secondary processes of change.
+ * No impact for this application, but need to reply.
+ */
+ if (rte_pdump_init() < 0)
+ rte_exit(EXIT_FAILURE, "EAL pdump init failed\n");
+
/*
* If no lcore argument was specified, then run this program as a normal process
* which can be scheduled on any non-isolated CPU.
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC 5/6] pdump: handle pdump requests from primary
2025-08-11 21:34 [RFC 0/6] Packet capture fixes Stephen Hemminger
` (3 preceding siblings ...)
2025-08-11 21:35 ` [RFC 4/6] dumpcap: handle pdump requests from primary Stephen Hemminger
@ 2025-08-11 21:35 ` Stephen Hemminger
2025-08-11 21:35 ` [RFC 6/6] pdump: forward callback enable to secondary Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
6 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-11 21:35 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
The primary process will start to notify all secondary processes
about pdump changes. The pdump secondary process can just call
rte_pdump_init() and it take care of that.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/pdump/main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/pdump/main.c b/app/pdump/main.c
index 1741d7e709..626ba0ce93 100644
--- a/app/pdump/main.c
+++ b/app/pdump/main.c
@@ -552,6 +552,7 @@ cleanup_pdump_resources(void)
}
}
+ rte_pdump_uninit();
cleanup_rings();
}
@@ -822,6 +823,9 @@ enable_pdump(void)
struct pdump_tuples *pt;
int ret = 0, ret1 = 0;
+ if (rte_pdump_init() < 0)
+ rte_exit(EXIT_FAILURE, "pdump init failed\n");
+
for (i = 0; i < num_tuples; i++) {
pt = &pdump_t[i];
if (pt->dir == RTE_PDUMP_FLAG_RXTX) {
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC 6/6] pdump: forward callback enable to secondary
2025-08-11 21:34 [RFC 0/6] Packet capture fixes Stephen Hemminger
` (4 preceding siblings ...)
2025-08-11 21:35 ` [RFC 5/6] pdump: " Stephen Hemminger
@ 2025-08-11 21:35 ` Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
6 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-11 21:35 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
When packet capture is enabled, need to also notify
secondary processes to force them to do the callbacks.
Requires that all secondary processes also call rte_pdump_init()
or there will be warning about not responding secondary.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/pdump/rte_pdump.c | 213 ++++++++++++++++++++++++++++++++++++------
1 file changed, 185 insertions(+), 28 deletions(-)
diff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c
index bfd63fa8c2..b95f7f863d 100644
--- a/lib/pdump/rte_pdump.c
+++ b/lib/pdump/rte_pdump.c
@@ -5,6 +5,7 @@
#include <stdlib.h>
#include <eal_export.h>
+#include <rte_alarm.h>
#include <rte_mbuf.h>
#include <rte_ethdev.h>
#include <rte_lcore.h>
@@ -26,11 +27,28 @@ RTE_LOG_REGISTER_DEFAULT(pdump_logtype, NOTICE);
/* Used for the multi-process communication */
#define PDUMP_MP "mp_pdump"
+/* Overly generous timeout for secondary to respond */
+#define MP_TIMEOUT_S 5
+
enum pdump_operation {
DISABLE = 1,
ENABLE = 2
};
+static inline const char *
+pdump_opname(enum pdump_operation op)
+{
+ static char buf[32];
+
+ if (op == DISABLE)
+ return "disable";
+ if (op == ENABLE)
+ return "enable";
+
+ snprintf(buf, sizeof(buf), "op%u", op);
+ return buf;
+}
+
/* Internal version number in request */
enum pdump_version {
V1 = 1, /* no filtering or snap */
@@ -56,6 +74,11 @@ struct pdump_response {
int32_t err_value;
};
+struct pdump_bundle {
+ struct rte_mp_msg msg;
+ char peer[];
+};
+
static struct pdump_rxtx_cbs {
struct rte_ring *ring;
struct rte_mempool *mp;
@@ -432,34 +455,150 @@ set_pdump_rxtx_cbs(const struct pdump_request *p)
return ret;
}
+static void
+pdump_request_to_secondary(const struct pdump_request *req)
+{
+ struct rte_mp_msg mp_req = { };
+ struct rte_mp_reply mp_reply;
+ struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
+
+ PDUMP_LOG_LINE(DEBUG, "forward req %s to secondary", pdump_opname(req->op));
+
+ memcpy(mp_req.param, req, sizeof(*req));
+ strlcpy(mp_req.name, PDUMP_MP, sizeof(mp_req.name));
+ mp_req.len_param = sizeof(*req);
+
+ if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) != 0)
+ PDUMP_LOG_LINE(ERR, "rte_mp_request_sync failed");
+
+ else if (mp_reply.nb_sent != mp_reply.nb_received)
+ PDUMP_LOG_LINE(ERR, "not all secondary's replied (sent %u recv %u)",
+ mp_reply.nb_sent, mp_reply.nb_received);
+
+ free(mp_reply.msgs);
+}
+
+/* Allocate temporary storage for passing state to the alarm thread for deferred handling */
+static struct pdump_bundle *
+pdump_bundle_alloc(const struct rte_mp_msg *mp_msg, const char *peer)
+{
+ struct pdump_bundle *bundle;
+ size_t peer_len = strlen(peer) + 1;
+
+ /* peer is the unix domain socket path */
+ bundle = malloc(sizeof(*bundle) + peer_len);
+ if (bundle == NULL)
+ return NULL;
+
+ bundle->msg = *mp_msg;
+ memcpy(bundle->peer, peer, peer_len);
+ return bundle;
+}
+
+/* Send response to peer */
static int
-pdump_server(const struct rte_mp_msg *mp_msg, const void *peer)
+pdump_send_response(const struct pdump_request *req, int result, const void *peer)
{
- struct rte_mp_msg mp_resp;
- const struct pdump_request *cli_req;
- struct pdump_response *resp = (struct pdump_response *)&mp_resp.param;
+ struct rte_mp_msg mp_resp = { };
+ struct pdump_response *resp = (struct pdump_response *)mp_resp.param;
+ int ret;
- /* recv client requests */
- if (mp_msg->len_param != sizeof(*cli_req)) {
- PDUMP_LOG_LINE(ERR, "failed to recv from client");
- resp->err_value = -EINVAL;
- } else {
- cli_req = (const struct pdump_request *)mp_msg->param;
- resp->ver = cli_req->ver;
- resp->res_op = cli_req->op;
- resp->err_value = set_pdump_rxtx_cbs(cli_req);
+ if (req) {
+ resp->ver = req->ver;
+ resp->res_op = req->op;
}
+ resp->err_value = result;
rte_strscpy(mp_resp.name, PDUMP_MP, RTE_MP_MAX_NAME_LEN);
mp_resp.len_param = sizeof(*resp);
- mp_resp.num_fds = 0;
- if (rte_mp_reply(&mp_resp, peer) < 0) {
- PDUMP_LOG_LINE(ERR, "failed to send to client:%s",
+
+ ret = rte_mp_reply(&mp_resp, peer);
+ if (ret != 0)
+ PDUMP_LOG_LINE(ERR, "failed to send response: %s",
strerror(rte_errno));
- return -1;
+ return ret;
+}
+
+/* Callback from MP request handler in secondary process */
+static int
+pdump_handle_primary_request(const struct rte_mp_msg *mp_msg, const void *peer)
+{
+ const struct pdump_request *req = NULL;
+ int ret;
+
+ if (mp_msg->len_param != sizeof(*req)) {
+ PDUMP_LOG_LINE(ERR, "invalid request from primary");
+ ret = -EINVAL;
+ } else {
+ req = (const struct pdump_request *)mp_msg->param;
+ PDUMP_LOG_LINE(DEBUG, "secondary pdump %s", pdump_opname(req->op));
+
+ /* Can just do it now, no need for interrupt thread */
+ ret = set_pdump_rxtx_cbs(req);
}
+ return pdump_send_response(req, ret, peer);
+
+}
+
+/* Callback from the alarm handler (in interrupt thread) which does actual change */
+static void
+__pdump_request(void *param)
+{
+ struct pdump_bundle *bundle = param;
+ struct rte_mp_msg *msg = &bundle->msg;
+ const struct pdump_request *req =
+ (const struct pdump_request *)msg->param;
+ int ret;
+
+ PDUMP_LOG_LINE(DEBUG, "primary pdump %s", pdump_opname(req->op));
+
+ ret = set_pdump_rxtx_cbs(req);
+ ret = pdump_send_response(req, ret, bundle->peer);
+
+ /* Primary process is responsible for broadcasting request to all secondaries */
+ if (ret == 0)
+ pdump_request_to_secondary(req);
+
+ free(bundle);
+}
+
+/* Callback from MP request handler in primary process */
+static int
+pdump_handle_secondary_request(const struct rte_mp_msg *mp_msg, const void *peer)
+{
+ struct pdump_bundle *bundle = NULL;
+ const struct pdump_request *req = NULL;
+ int ret;
+
+ if (mp_msg->len_param != sizeof(*req)) {
+ PDUMP_LOG_LINE(ERR, "invalid request from secondary");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ req = (const struct pdump_request *)mp_msg->param;
+
+ bundle = pdump_bundle_alloc(mp_msg, peer);
+ if (bundle == NULL) {
+ PDUMP_LOG_LINE(ERR, "not enough memory");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /*
+ * We are in IPC callback thread, sync IPC is not possible
+ * since sending to secondary would cause livelock.
+ * Delegate the task to interrupt thread.
+ */
+ ret = rte_eal_alarm_set(1, __pdump_request, bundle);
+ if (ret != 0)
+ goto error;
return 0;
+
+error:
+ free(bundle);
+ return pdump_send_response(req, ret, peer);
}
RTE_EXPORT_SYMBOL(rte_pdump_init)
@@ -469,19 +608,36 @@ rte_pdump_init(void)
const struct rte_memzone *mz;
int ret;
- mz = rte_memzone_reserve(MZ_RTE_PDUMP_STATS, sizeof(*pdump_stats),
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- PDUMP_LOG_LINE(ERR, "cannot allocate pdump statistics");
- rte_errno = ENOMEM;
- return -1;
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+ ret = rte_mp_action_register(PDUMP_MP, pdump_handle_secondary_request);
+ if (ret && rte_errno != ENOTSUP)
+ return -1;
+
+ mz = rte_memzone_reserve(MZ_RTE_PDUMP_STATS, sizeof(*pdump_stats),
+ SOCKET_ID_ANY, 0);
+ if (mz == NULL) {
+ PDUMP_LOG_LINE(ERR, "cannot allocate pdump statistics");
+ rte_mp_action_unregister(PDUMP_MP);
+ rte_errno = ENOMEM;
+ return -1;
+ }
+ } else {
+ ret = rte_mp_action_register(PDUMP_MP, pdump_handle_primary_request);
+ if (ret && rte_errno != ENOTSUP)
+ return -1;
+
+ mz = rte_memzone_lookup(MZ_RTE_PDUMP_STATS);
+ if (mz == NULL) {
+ PDUMP_LOG_LINE(ERR, "cannot find pdump statistics");
+ rte_mp_action_unregister(PDUMP_MP);
+ rte_errno = ENOENT;
+ return -1;
+ }
}
+
pdump_stats = mz->addr;
pdump_stats->mz = mz;
- ret = rte_mp_action_register(PDUMP_MP, pdump_server);
- if (ret && rte_errno != ENOTSUP)
- return -1;
return 0;
}
@@ -491,7 +647,7 @@ rte_pdump_uninit(void)
{
rte_mp_action_unregister(PDUMP_MP);
- if (pdump_stats != NULL) {
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY && pdump_stats != NULL) {
rte_memzone_free(pdump_stats->mz);
pdump_stats = NULL;
}
@@ -580,11 +736,12 @@ pdump_prepare_client_request(const char *device, uint16_t queue,
int ret = -1;
struct rte_mp_msg mp_req, *mp_rep;
struct rte_mp_reply mp_reply;
- struct timespec ts = {.tv_sec = 5, .tv_nsec = 0};
+ struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
struct pdump_request *req = (struct pdump_request *)mp_req.param;
struct pdump_response *resp;
if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+ /* FIXME */
PDUMP_LOG_LINE(ERR,
"pdump enable/disable not allowed in primary process");
return -EINVAL;
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 0/8] Packet capture cleanup
2025-08-11 21:34 [RFC 0/6] Packet capture fixes Stephen Hemminger
` (5 preceding siblings ...)
2025-08-11 21:35 ` [RFC 6/6] pdump: forward callback enable to secondary Stephen Hemminger
@ 2025-08-14 16:52 ` Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 1/8] dumpcap: handle primary process exit Stephen Hemminger
` (7 more replies)
6 siblings, 8 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:52 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
This patch series addresses the problem of packet capture when
the secondary process is sending/receiving by using the same
mechanism as hot plug. It also fixes some bugs on shutdown
of dumpcap (and pdump).
The documentation has been updated and improved as well.
v2 - add documentation
Stephen Hemminger (8):
dumpcap: handle primary process exit
pdump: handle primary process exit
pdump: fix races in callbacks
dumpcap: handle pdump requests from primary
pdump: handle pdump requests from primary
pdump: forward callback enable to secondary
pdump: remove use of VLA
doc: update documentation on pdump library
app/dumpcap/main.c | 14 +
app/pdump/main.c | 16 +-
doc/guides/prog_guide/img/pdump_overview.svg | 135 +++++++++
doc/guides/prog_guide/pdump_lib.rst | 183 ++++++++-----
lib/pdump/meson.build | 2 -
lib/pdump/rte_pdump.c | 273 ++++++++++++++++---
6 files changed, 514 insertions(+), 109 deletions(-)
create mode 100644 doc/guides/prog_guide/img/pdump_overview.svg
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 1/8] dumpcap: handle primary process exit
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
@ 2025-08-14 16:52 ` Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 2/8] pdump: " Stephen Hemminger
` (6 subsequent siblings)
7 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:52 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, stable, Reshma Pattan
If primary process exits, then it is not possible (or needed)
to cleanup resources. Instead just exit after closing the
capture file.
Bugzilla ID: 1760
Fixes: cbb44143be74 ("app/dumpcap: add new packet capture application")
Cc: stable@dpdk.org
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/dumpcap/main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 3d3c0dbc66..7b19c830b5 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -1058,6 +1058,10 @@ int main(int argc, char **argv)
else
pcap_dump_close(out.dumper);
+ /* If primary has exited, do not try and communicate with it */
+ if (!rte_eal_primary_proc_alive(NULL))
+ return 0;
+
cleanup_pdump_resources();
rte_ring_free(r);
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 2/8] pdump: handle primary process exit
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 1/8] dumpcap: handle primary process exit Stephen Hemminger
@ 2025-08-14 16:52 ` Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 3/8] pdump: fix races in callbacks Stephen Hemminger
` (5 subsequent siblings)
7 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:52 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, stable, Reshma Pattan, Suanming Mou, Anatoly Burakov
If primary process exits, then it is not possible (or needed)
to cleanup resources. Instead just exit after closing the
capture file.
Bugzilla ID: 1760
Fixes: a99a311ba101 ("app/pdump: exit with primary process")
Cc: stable@dpdk.org
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/pdump/main.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/app/pdump/main.c b/app/pdump/main.c
index fa85859703..1741d7e709 100644
--- a/app/pdump/main.c
+++ b/app/pdump/main.c
@@ -1028,13 +1028,15 @@ main(int argc, char **argv)
dump_packets();
disable_primary_monitor();
- cleanup_pdump_resources();
+
/* dump debug stats */
print_pdump_stats();
- ret = rte_eal_cleanup();
- if (ret)
- printf("Error from rte_eal_cleanup(), %d\n", ret);
+ /* If primary has exited, do not try and communicate with it */
+ if (!rte_eal_primary_proc_alive(NULL))
+ return 0;
- return 0;
+ cleanup_pdump_resources();
+
+ return rte_eal_cleanup() ? EXIT_FAILURE : 0;
}
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 3/8] pdump: fix races in callbacks
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 1/8] dumpcap: handle primary process exit Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 2/8] pdump: " Stephen Hemminger
@ 2025-08-14 16:52 ` Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 4/8] dumpcap: handle pdump requests from primary Stephen Hemminger
` (4 subsequent siblings)
7 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:52 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Reshma Pattan
The pdump callback can race with other cpu's in the datapath.
Handle this by using reference counts and LSB in manner
similar to seqcount and bpf code.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/pdump/rte_pdump.c | 48 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 46 insertions(+), 2 deletions(-)
diff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c
index ba75b828f2..bfd63fa8c2 100644
--- a/lib/pdump/rte_pdump.c
+++ b/lib/pdump/rte_pdump.c
@@ -12,6 +12,7 @@
#include <rte_memzone.h>
#include <rte_errno.h>
#include <rte_string_fns.h>
+#include <rte_pause.h>
#include <rte_pcapng.h>
#include "rte_pdump.h"
@@ -62,6 +63,7 @@ static struct pdump_rxtx_cbs {
const struct rte_bpf *filter;
enum pdump_version ver;
uint32_t snaplen;
+ RTE_ATOMIC(uint32_t) use_count;
} rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT],
tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
@@ -78,6 +80,36 @@ static struct {
const struct rte_memzone *mz;
} *pdump_stats;
+static void
+pdump_cb_wait(struct pdump_rxtx_cbs *cbs)
+{
+ /* make sure the data loads happens before the use count load */
+ rte_atomic_thread_fence(rte_memory_order_acquire);
+
+ /* wait until use_count is even (not in use) */
+ RTE_WAIT_UNTIL_MASKED(&cbs->use_count, 1, ==, 0, rte_memory_order_relaxed);
+}
+
+static __rte_always_inline void
+pdump_cb_hold(struct pdump_rxtx_cbs *cbs)
+{
+ uint32_t count = cbs->use_count + 1;
+
+ rte_atomic_store_explicit(&cbs->use_count, count, rte_memory_order_relaxed);
+
+ /* prevent stores after this from happening before the use_count update */
+ rte_atomic_thread_fence(rte_memory_order_release);
+}
+
+static __rte_always_inline void
+pdump_cb_release(struct pdump_rxtx_cbs *cbs)
+{
+ uint32_t count = cbs->use_count + 1;
+
+ /* Synchronizes-with the load acquire in pdump_cb_wait */
+ rte_atomic_store_explicit(&cbs->use_count, count, rte_memory_order_release);
+}
+
/* Create a clone of mbuf to be placed into ring. */
static void
pdump_copy(uint16_t port_id, uint16_t queue,
@@ -146,11 +178,14 @@ pdump_rx(uint16_t port, uint16_t queue,
struct rte_mbuf **pkts, uint16_t nb_pkts,
uint16_t max_pkts __rte_unused, void *user_params)
{
- const struct pdump_rxtx_cbs *cbs = user_params;
+ struct pdump_rxtx_cbs *cbs = user_params;
struct rte_pdump_stats *stats = &pdump_stats->rx[port][queue];
+ pdump_cb_hold(cbs);
pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_IN,
pkts, nb_pkts, cbs, stats);
+ pdump_cb_release(cbs);
+
return nb_pkts;
}
@@ -158,14 +193,18 @@ static uint16_t
pdump_tx(uint16_t port, uint16_t queue,
struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params)
{
- const struct pdump_rxtx_cbs *cbs = user_params;
+ struct pdump_rxtx_cbs *cbs = user_params;
struct rte_pdump_stats *stats = &pdump_stats->tx[port][queue];
+ pdump_cb_hold(cbs);
pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_OUT,
pkts, nb_pkts, cbs, stats);
+ pdump_cb_release(cbs);
+
return nb_pkts;
}
+
static int
pdump_register_rx_callbacks(enum pdump_version ver,
uint16_t end_q, uint16_t port, uint16_t queue,
@@ -186,6 +225,7 @@ pdump_register_rx_callbacks(enum pdump_version ver,
port, qid);
return -EEXIST;
}
+ cbs->use_count = 0;
cbs->ver = ver;
cbs->ring = ring;
cbs->mp = mp;
@@ -218,6 +258,7 @@ pdump_register_rx_callbacks(enum pdump_version ver,
-ret);
return ret;
}
+ pdump_cb_wait(cbs);
cbs->cb = NULL;
}
}
@@ -246,6 +287,7 @@ pdump_register_tx_callbacks(enum pdump_version ver,
port, qid);
return -EEXIST;
}
+ cbs->use_count = 0;
cbs->ver = ver;
cbs->ring = ring;
cbs->mp = mp;
@@ -277,6 +319,8 @@ pdump_register_tx_callbacks(enum pdump_version ver,
-ret);
return ret;
}
+
+ pdump_cb_wait(cbs);
cbs->cb = NULL;
}
}
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 4/8] dumpcap: handle pdump requests from primary
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
` (2 preceding siblings ...)
2025-08-14 16:52 ` [PATCH v2 3/8] pdump: fix races in callbacks Stephen Hemminger
@ 2025-08-14 16:53 ` Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 5/8] pdump: " Stephen Hemminger
` (3 subsequent siblings)
7 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:53 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Reshma Pattan
The primary process will start to notify all secondary processes
about pdump changes. The dumpcap secondary process can just call
rte_pdump_init() and it take care of that.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/dumpcap/main.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c
index 7b19c830b5..c734fc7b9d 100644
--- a/app/dumpcap/main.c
+++ b/app/dumpcap/main.c
@@ -528,6 +528,8 @@ cleanup_pdump_resources(void)
if (intf->opts.promisc_mode)
rte_eth_promiscuous_disable(intf->port);
}
+
+ rte_pdump_uninit();
}
/* Alarm signal handler, used to check that primary process */
@@ -659,6 +661,14 @@ static void dpdk_init(void)
if (rte_eal_init(eal_argc, eal_argv) < 0)
rte_exit(EXIT_FAILURE, "EAL init failed: is primary process running?\n");
+ /*
+ * Register pdump callback handler.
+ * Primary will notify all secondary processes of change.
+ * No impact for this application, but need to reply.
+ */
+ if (rte_pdump_init() < 0)
+ rte_exit(EXIT_FAILURE, "EAL pdump init failed\n");
+
/*
* If no lcore argument was specified, then run this program as a normal process
* which can be scheduled on any non-isolated CPU.
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 5/8] pdump: handle pdump requests from primary
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
` (3 preceding siblings ...)
2025-08-14 16:53 ` [PATCH v2 4/8] dumpcap: handle pdump requests from primary Stephen Hemminger
@ 2025-08-14 16:53 ` Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 6/8] pdump: forward callback enable to secondary Stephen Hemminger
` (2 subsequent siblings)
7 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:53 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Reshma Pattan
The primary process will start to notify all secondary processes
about pdump changes. The pdump secondary process can just call
rte_pdump_init() and it take care of that.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/pdump/main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/pdump/main.c b/app/pdump/main.c
index 1741d7e709..626ba0ce93 100644
--- a/app/pdump/main.c
+++ b/app/pdump/main.c
@@ -552,6 +552,7 @@ cleanup_pdump_resources(void)
}
}
+ rte_pdump_uninit();
cleanup_rings();
}
@@ -822,6 +823,9 @@ enable_pdump(void)
struct pdump_tuples *pt;
int ret = 0, ret1 = 0;
+ if (rte_pdump_init() < 0)
+ rte_exit(EXIT_FAILURE, "pdump init failed\n");
+
for (i = 0; i < num_tuples; i++) {
pt = &pdump_t[i];
if (pt->dir == RTE_PDUMP_FLAG_RXTX) {
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 6/8] pdump: forward callback enable to secondary
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
` (4 preceding siblings ...)
2025-08-14 16:53 ` [PATCH v2 5/8] pdump: " Stephen Hemminger
@ 2025-08-14 16:53 ` Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 7/8] pdump: remove use of VLA Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 8/8] doc: update documentation on pdump library Stephen Hemminger
7 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:53 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Reshma Pattan, Anatoly Burakov
When packet capture is enabled, need to also notify
secondary processes to force them to do the callbacks.
Requires that all secondary processes also call rte_pdump_init()
or there will be warning about not responding secondary.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/pdump/rte_pdump.c | 213 ++++++++++++++++++++++++++++++++++++------
1 file changed, 185 insertions(+), 28 deletions(-)
diff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c
index bfd63fa8c2..b95f7f863d 100644
--- a/lib/pdump/rte_pdump.c
+++ b/lib/pdump/rte_pdump.c
@@ -5,6 +5,7 @@
#include <stdlib.h>
#include <eal_export.h>
+#include <rte_alarm.h>
#include <rte_mbuf.h>
#include <rte_ethdev.h>
#include <rte_lcore.h>
@@ -26,11 +27,28 @@ RTE_LOG_REGISTER_DEFAULT(pdump_logtype, NOTICE);
/* Used for the multi-process communication */
#define PDUMP_MP "mp_pdump"
+/* Overly generous timeout for secondary to respond */
+#define MP_TIMEOUT_S 5
+
enum pdump_operation {
DISABLE = 1,
ENABLE = 2
};
+static inline const char *
+pdump_opname(enum pdump_operation op)
+{
+ static char buf[32];
+
+ if (op == DISABLE)
+ return "disable";
+ if (op == ENABLE)
+ return "enable";
+
+ snprintf(buf, sizeof(buf), "op%u", op);
+ return buf;
+}
+
/* Internal version number in request */
enum pdump_version {
V1 = 1, /* no filtering or snap */
@@ -56,6 +74,11 @@ struct pdump_response {
int32_t err_value;
};
+struct pdump_bundle {
+ struct rte_mp_msg msg;
+ char peer[];
+};
+
static struct pdump_rxtx_cbs {
struct rte_ring *ring;
struct rte_mempool *mp;
@@ -432,34 +455,150 @@ set_pdump_rxtx_cbs(const struct pdump_request *p)
return ret;
}
+static void
+pdump_request_to_secondary(const struct pdump_request *req)
+{
+ struct rte_mp_msg mp_req = { };
+ struct rte_mp_reply mp_reply;
+ struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
+
+ PDUMP_LOG_LINE(DEBUG, "forward req %s to secondary", pdump_opname(req->op));
+
+ memcpy(mp_req.param, req, sizeof(*req));
+ strlcpy(mp_req.name, PDUMP_MP, sizeof(mp_req.name));
+ mp_req.len_param = sizeof(*req);
+
+ if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) != 0)
+ PDUMP_LOG_LINE(ERR, "rte_mp_request_sync failed");
+
+ else if (mp_reply.nb_sent != mp_reply.nb_received)
+ PDUMP_LOG_LINE(ERR, "not all secondary's replied (sent %u recv %u)",
+ mp_reply.nb_sent, mp_reply.nb_received);
+
+ free(mp_reply.msgs);
+}
+
+/* Allocate temporary storage for passing state to the alarm thread for deferred handling */
+static struct pdump_bundle *
+pdump_bundle_alloc(const struct rte_mp_msg *mp_msg, const char *peer)
+{
+ struct pdump_bundle *bundle;
+ size_t peer_len = strlen(peer) + 1;
+
+ /* peer is the unix domain socket path */
+ bundle = malloc(sizeof(*bundle) + peer_len);
+ if (bundle == NULL)
+ return NULL;
+
+ bundle->msg = *mp_msg;
+ memcpy(bundle->peer, peer, peer_len);
+ return bundle;
+}
+
+/* Send response to peer */
static int
-pdump_server(const struct rte_mp_msg *mp_msg, const void *peer)
+pdump_send_response(const struct pdump_request *req, int result, const void *peer)
{
- struct rte_mp_msg mp_resp;
- const struct pdump_request *cli_req;
- struct pdump_response *resp = (struct pdump_response *)&mp_resp.param;
+ struct rte_mp_msg mp_resp = { };
+ struct pdump_response *resp = (struct pdump_response *)mp_resp.param;
+ int ret;
- /* recv client requests */
- if (mp_msg->len_param != sizeof(*cli_req)) {
- PDUMP_LOG_LINE(ERR, "failed to recv from client");
- resp->err_value = -EINVAL;
- } else {
- cli_req = (const struct pdump_request *)mp_msg->param;
- resp->ver = cli_req->ver;
- resp->res_op = cli_req->op;
- resp->err_value = set_pdump_rxtx_cbs(cli_req);
+ if (req) {
+ resp->ver = req->ver;
+ resp->res_op = req->op;
}
+ resp->err_value = result;
rte_strscpy(mp_resp.name, PDUMP_MP, RTE_MP_MAX_NAME_LEN);
mp_resp.len_param = sizeof(*resp);
- mp_resp.num_fds = 0;
- if (rte_mp_reply(&mp_resp, peer) < 0) {
- PDUMP_LOG_LINE(ERR, "failed to send to client:%s",
+
+ ret = rte_mp_reply(&mp_resp, peer);
+ if (ret != 0)
+ PDUMP_LOG_LINE(ERR, "failed to send response: %s",
strerror(rte_errno));
- return -1;
+ return ret;
+}
+
+/* Callback from MP request handler in secondary process */
+static int
+pdump_handle_primary_request(const struct rte_mp_msg *mp_msg, const void *peer)
+{
+ const struct pdump_request *req = NULL;
+ int ret;
+
+ if (mp_msg->len_param != sizeof(*req)) {
+ PDUMP_LOG_LINE(ERR, "invalid request from primary");
+ ret = -EINVAL;
+ } else {
+ req = (const struct pdump_request *)mp_msg->param;
+ PDUMP_LOG_LINE(DEBUG, "secondary pdump %s", pdump_opname(req->op));
+
+ /* Can just do it now, no need for interrupt thread */
+ ret = set_pdump_rxtx_cbs(req);
}
+ return pdump_send_response(req, ret, peer);
+
+}
+
+/* Callback from the alarm handler (in interrupt thread) which does actual change */
+static void
+__pdump_request(void *param)
+{
+ struct pdump_bundle *bundle = param;
+ struct rte_mp_msg *msg = &bundle->msg;
+ const struct pdump_request *req =
+ (const struct pdump_request *)msg->param;
+ int ret;
+
+ PDUMP_LOG_LINE(DEBUG, "primary pdump %s", pdump_opname(req->op));
+
+ ret = set_pdump_rxtx_cbs(req);
+ ret = pdump_send_response(req, ret, bundle->peer);
+
+ /* Primary process is responsible for broadcasting request to all secondaries */
+ if (ret == 0)
+ pdump_request_to_secondary(req);
+
+ free(bundle);
+}
+
+/* Callback from MP request handler in primary process */
+static int
+pdump_handle_secondary_request(const struct rte_mp_msg *mp_msg, const void *peer)
+{
+ struct pdump_bundle *bundle = NULL;
+ const struct pdump_request *req = NULL;
+ int ret;
+
+ if (mp_msg->len_param != sizeof(*req)) {
+ PDUMP_LOG_LINE(ERR, "invalid request from secondary");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ req = (const struct pdump_request *)mp_msg->param;
+
+ bundle = pdump_bundle_alloc(mp_msg, peer);
+ if (bundle == NULL) {
+ PDUMP_LOG_LINE(ERR, "not enough memory");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /*
+ * We are in IPC callback thread, sync IPC is not possible
+ * since sending to secondary would cause livelock.
+ * Delegate the task to interrupt thread.
+ */
+ ret = rte_eal_alarm_set(1, __pdump_request, bundle);
+ if (ret != 0)
+ goto error;
return 0;
+
+error:
+ free(bundle);
+ return pdump_send_response(req, ret, peer);
}
RTE_EXPORT_SYMBOL(rte_pdump_init)
@@ -469,19 +608,36 @@ rte_pdump_init(void)
const struct rte_memzone *mz;
int ret;
- mz = rte_memzone_reserve(MZ_RTE_PDUMP_STATS, sizeof(*pdump_stats),
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- PDUMP_LOG_LINE(ERR, "cannot allocate pdump statistics");
- rte_errno = ENOMEM;
- return -1;
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+ ret = rte_mp_action_register(PDUMP_MP, pdump_handle_secondary_request);
+ if (ret && rte_errno != ENOTSUP)
+ return -1;
+
+ mz = rte_memzone_reserve(MZ_RTE_PDUMP_STATS, sizeof(*pdump_stats),
+ SOCKET_ID_ANY, 0);
+ if (mz == NULL) {
+ PDUMP_LOG_LINE(ERR, "cannot allocate pdump statistics");
+ rte_mp_action_unregister(PDUMP_MP);
+ rte_errno = ENOMEM;
+ return -1;
+ }
+ } else {
+ ret = rte_mp_action_register(PDUMP_MP, pdump_handle_primary_request);
+ if (ret && rte_errno != ENOTSUP)
+ return -1;
+
+ mz = rte_memzone_lookup(MZ_RTE_PDUMP_STATS);
+ if (mz == NULL) {
+ PDUMP_LOG_LINE(ERR, "cannot find pdump statistics");
+ rte_mp_action_unregister(PDUMP_MP);
+ rte_errno = ENOENT;
+ return -1;
+ }
}
+
pdump_stats = mz->addr;
pdump_stats->mz = mz;
- ret = rte_mp_action_register(PDUMP_MP, pdump_server);
- if (ret && rte_errno != ENOTSUP)
- return -1;
return 0;
}
@@ -491,7 +647,7 @@ rte_pdump_uninit(void)
{
rte_mp_action_unregister(PDUMP_MP);
- if (pdump_stats != NULL) {
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY && pdump_stats != NULL) {
rte_memzone_free(pdump_stats->mz);
pdump_stats = NULL;
}
@@ -580,11 +736,12 @@ pdump_prepare_client_request(const char *device, uint16_t queue,
int ret = -1;
struct rte_mp_msg mp_req, *mp_rep;
struct rte_mp_reply mp_reply;
- struct timespec ts = {.tv_sec = 5, .tv_nsec = 0};
+ struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
struct pdump_request *req = (struct pdump_request *)mp_req.param;
struct pdump_response *resp;
if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+ /* FIXME */
PDUMP_LOG_LINE(ERR,
"pdump enable/disable not allowed in primary process");
return -EINVAL;
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 7/8] pdump: remove use of VLA
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
` (5 preceding siblings ...)
2025-08-14 16:53 ` [PATCH v2 6/8] pdump: forward callback enable to secondary Stephen Hemminger
@ 2025-08-14 16:53 ` Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 8/8] doc: update documentation on pdump library Stephen Hemminger
7 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:53 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Reshma Pattan
Replace variable length array with alloca.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/pdump/meson.build | 2 --
lib/pdump/rte_pdump.c | 12 ++++++++----
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/lib/pdump/meson.build b/lib/pdump/meson.build
index 553dfdd5e6..da8d51b616 100644
--- a/lib/pdump/meson.build
+++ b/lib/pdump/meson.build
@@ -7,8 +7,6 @@ if is_windows
subdir_done()
endif
-cflags += no_wvla_cflag
-
sources = files('rte_pdump.c')
headers = files('rte_pdump.h')
deps += ['ethdev', 'bpf', 'pcapng']
diff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c
index b95f7f863d..d201997b75 100644
--- a/lib/pdump/rte_pdump.c
+++ b/lib/pdump/rte_pdump.c
@@ -144,17 +144,21 @@ pdump_copy(uint16_t port_id, uint16_t queue,
unsigned int i;
int ring_enq;
uint16_t d_pkts = 0;
- struct rte_mbuf *dup_bufs[nb_pkts];
+ struct rte_mbuf **dup_bufs;
struct rte_ring *ring;
struct rte_mempool *mp;
struct rte_mbuf *p;
- uint64_t rcs[nb_pkts];
+ uint64_t *rcs = NULL;
- if (cbs->filter)
+ if (cbs->filter) {
+ rcs = alloca(sizeof(uint64_t) * nb_pkts);
rte_bpf_exec_burst(cbs->filter, (void **)pkts, rcs, nb_pkts);
+ }
ring = cbs->ring;
mp = cbs->mp;
+ dup_bufs = alloca(sizeof(struct rte_mbuf *) * nb_pkts);
+
for (i = 0; i < nb_pkts; i++) {
/*
* This uses same BPF return value convention as socket filter
@@ -162,7 +166,7 @@ pdump_copy(uint16_t port_id, uint16_t queue,
* if program returns zero
* then packet doesn't match the filter (will be ignored).
*/
- if (cbs->filter && rcs[i] == 0) {
+ if (rcs != NULL && rcs[i] == 0) {
rte_atomic_fetch_add_explicit(&stats->filtered,
1, rte_memory_order_relaxed);
continue;
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 8/8] doc: update documentation on pdump library
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
` (6 preceding siblings ...)
2025-08-14 16:53 ` [PATCH v2 7/8] pdump: remove use of VLA Stephen Hemminger
@ 2025-08-14 16:53 ` Stephen Hemminger
7 siblings, 0 replies; 16+ messages in thread
From: Stephen Hemminger @ 2025-08-14 16:53 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Reshma Pattan
The documentation was missing some of the API, and had some
awkward wording. With the help of ChatGPT, update it and make
it more concise.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
doc/guides/prog_guide/img/pdump_overview.svg | 135 ++++++++++++++
doc/guides/prog_guide/pdump_lib.rst | 183 ++++++++++++-------
2 files changed, 250 insertions(+), 68 deletions(-)
create mode 100644 doc/guides/prog_guide/img/pdump_overview.svg
diff --git a/doc/guides/prog_guide/img/pdump_overview.svg b/doc/guides/prog_guide/img/pdump_overview.svg
new file mode 100644
index 0000000000..537de49669
--- /dev/null
+++ b/doc/guides/prog_guide/img/pdump_overview.svg
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright (c) 2025 Stephen Hemminger -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Created with ChatGPT -->
+
+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1080" viewBox="0 0 1200 1080">
+ <defs>
+ <marker id="arrowBlue" markerWidth="12" markerHeight="8" refX="10" refY="4" orient="auto">
+ <polygon points="0,0 12,4 0,8" fill="#0066cc"/>
+ </marker>
+ <marker id="arrowThinBlue" markerWidth="10" markerHeight="6" refX="9" refY="3" orient="auto">
+ <polygon points="0,0 10,3 0,6" fill="#3399ff"/>
+ </marker>
+ <marker id="arrowRed" markerWidth="12" markerHeight="8" refX="10" refY="4" orient="auto">
+ <polygon points="0,0 12,4 0,8" fill="#cc0000"/>
+ </marker>
+ <marker id="arrowThinRed" markerWidth="10" markerHeight="6" refX="9" refY="3" orient="auto">
+ <polygon points="0,0 10,3 0,6" fill="#ff3333"/>
+ </marker>
+ <style>
+ .lane-title { font: 700 20px system-ui, sans-serif; fill: #234; }
+ .lifeline { stroke: #9db3cc; stroke-dasharray: 6 6; stroke-width: 2; }
+ .step { fill: #fff; stroke: #517fa4; stroke-width: 1.5; rx: 8; ry: 8; }
+ .step text, .note text { font: 13px ui-monospace, monospace; fill: #102030; }
+ .note { fill: #fff9e6; stroke: #d9b54a; stroke-width: 1.2; rx: 8; ry: 8; }
+ .msg-blue { stroke: #0066cc; stroke-width: 2.2; marker-end: url(#arrowBlue); fill: none; }
+ .msg-thin-blue { stroke: #3399ff; stroke-width: 2; marker-end: url(#arrowThinBlue); fill: none; }
+ .msg-red { stroke: #cc0000; stroke-width: 2.2; marker-end: url(#arrowRed); fill: none; }
+ .msg-thin-red { stroke: #ff3333; stroke-width: 2; marker-end: url(#arrowThinRed); fill: none; }
+ .label { font: 14px ui-monospace, monospace; fill: #0d2238; }
+ .small { font-size: 12px; fill: #334b63; }
+ </style>
+ </defs>
+
+ <!-- Lanes -->
+ <rect x="30" y="20" width="340" height="1040" fill="#e6f0ff" stroke="#8aa4c2" stroke-width="2" rx="20" ry="20"/>
+ <text class="lane-title" x="200" y="52" text-anchor="middle">Capture Process</text>
+ <rect x="430" y="20" width="340" height="1040" fill="#e8f8e8" stroke="#8aa4c2" stroke-width="2" rx="20" ry="20"/>
+ <text class="lane-title" x="600" y="52" text-anchor="middle">Primary Process</text>
+ <rect x="830" y="20" width="340" height="1040" fill="#f9f9f9" stroke="#8aa4c2" stroke-width="2" rx="20" ry="20"/>
+ <text class="lane-title" x="1000" y="52" text-anchor="middle">Secondary Processes</text>
+
+ <!-- Lifelines -->
+ <line class="lifeline" x1="200" y1="70" x2="200" y2="1040"/>
+ <line class="lifeline" x1="600" y1="70" x2="600" y2="1040"/>
+ <line class="lifeline" x1="1000" y1="70" x2="1000" y2="1040"/>
+
+ <!-- Startup -->
+ <g transform="translate(80,90)">
+ <rect class="step" x="0" y="0" width="240" height="40"/>
+ <text x="12" y="25" class="label">rte_eal_init()</text>
+ <rect class="step" x="0" y="52" width="240" height="40"/>
+ <text x="12" y="77" class="label">rte_pdump_init()</text>
+ </g>
+ <g transform="translate(480,90)">
+ <rect class="step" x="0" y="0" width="240" height="40"/>
+ <text x="12" y="25" class="label">rte_eal_init()</text>
+ <rect class="step" x="0" y="52" width="240" height="40"/>
+ <text x="12" y="77" class="label">rte_pdump_init()</text>
+ </g>
+ <g transform="translate(880,90)">
+ <rect class="step" x="0" y="0" width="240" height="40"/>
+ <text x="12" y="25" class="label">rte_eal_init()</text>
+ <rect class="step" x="0" y="52" width="240" height="40"/>
+ <text x="12" y="77" class="label">rte_pdump_init()</text>
+ </g>
+
+ <!-- Enable sequence (Blue) -->
+ <line class="msg-blue" x1="200" y1="220" x2="600" y2="220"/>
+ <text class="label" x="400" y="212" text-anchor="middle">rte_pdump_enable()</text>
+ <rect class="note" x="260" y="230" width="280" height="36"/>
+ <text x="274" y="253" class="small">uses rte_mp_request() to message primary</text>
+
+ <g transform="translate(480,280)">
+ <rect class="step" x="0" y="0" width="240" height="96"/>
+ <text x="12" y="24" class="label">pdump_server()</text>
+ <text x="20" y="46" class="small">• enable RX/TX callbacks</text>
+ <text x="20" y="64" class="small">• send ACK to capture</text>
+ <text x="20" y="82" class="small">• forward request to all secondaries</text>
+ </g>
+ <line class="msg-thin-blue" x1="600" y1="330" x2="200" y2="330"/>
+ <text class="label" x="400" y="322" text-anchor="middle">ACK</text>
+ <line class="msg-blue" x1="600" y1="380" x2="1000" y2="380"/>
+ <text class="label" x="800" y="372" text-anchor="middle">forward enable request</text>
+
+ <g transform="translate(880,420)">
+ <rect class="step" x="0" y="0" width="240" height="78"/>
+ <text x="12" y="22" class="label">pdump_server()</text>
+ <text x="20" y="42" class="small">• register RX/TX callbacks</text>
+ <text x="20" y="60" class="small">• send response</text>
+ </g>
+ <line class="msg-thin-blue" x1="1000" y1="520" x2="600" y2="520"/>
+ <text class="label" x="800" y="512" text-anchor="middle">response</text>
+
+ <g transform="translate(480,560)">
+ <rect class="step" x="0" y="0" width="240" height="56"/>
+ <text x="12" y="24" class="label">collect responses</text>
+ <text x="12" y="44" class="small">from secondary processes</text>
+ </g>
+
+ <!-- Packet capture running -->
+ <g transform="translate(480,640)">
+ <rect class="step" x="-300" y="0" width="840" height="50"/>
+ <text x="120" y="30" class="label" text-anchor="middle">Packet capture in progress...</text>
+ </g>
+
+ <!-- Shutdown sequence (Red) -->
+ <line class="msg-red" x1="200" y1="720" x2="600" y2="720"/>
+ <text class="label" x="400" y="712" text-anchor="middle">rte_pdump_disable()</text>
+
+ <g transform="translate(480,760)">
+ <rect class="step" x="0" y="0" width="240" height="80"/>
+ <text x="12" y="24" class="label">pdump_server()</text>
+ <text x="20" y="46" class="small">• remove RX/TX callbacks</text>
+ <text x="20" y="64" class="small">• forward disable to secondaries</text>
+ </g>
+
+ <line class="msg-red" x1="600" y1="820" x2="1000" y2="820"/>
+ <text class="label" x="800" y="812" text-anchor="middle">forward disable request</text>
+
+ <g transform="translate(880,860)">
+ <rect class="step" x="0" y="0" width="240" height="60"/>
+ <text x="12" y="24" class="label">pdump_server()</text>
+ <text x="20" y="46" class="small">• remove RX/TX callbacks</text>
+ </g>
+
+ <line class="msg-thin-red" x1="1000" y1="940" x2="600" y2="940"/>
+ <text class="label" x="800" y="932" text-anchor="middle">response</text>
+
+ <g transform="translate(480,980)">
+ <rect class="step" x="0" y="0" width="240" height="40"/>
+ <text x="12" y="25" class="label">collect disable responses</text>
+ </g>
+</svg>
diff --git a/doc/guides/prog_guide/pdump_lib.rst b/doc/guides/prog_guide/pdump_lib.rst
index 07b9f39d09..5183756d9e 100644
--- a/doc/guides/prog_guide/pdump_lib.rst
+++ b/doc/guides/prog_guide/pdump_lib.rst
@@ -4,90 +4,137 @@
Packet Capture Library
======================
-The DPDK ``pdump`` library provides a framework for packet capturing in DPDK.
-The library does the complete copy of the Rx and Tx mbufs to a new mempool and
-hence it slows down the performance of the applications, so it is recommended
-to use this library for debugging purposes.
+The DPDK ``pdump`` library provides a framework for capturing packets within DPDK applications.
+It enables a **secondary process** to monitor packets being processed by both
+**primary** or **secondary** processes.
-The library uses a generic multi process channel to facilitate communication
-between primary and secondary process for enabling/disabling packet capture on
-ports.
+Overview
+--------
-The library provides the following APIs to initialize the packet capture framework, to enable
-or disable the packet capture, and to uninitialize it.
+The library uses a multi-process channel to facilitate communication
+between the primary and secondary processes. This mechanism allows enabling
+or disabling packet capture on specific ports or queues.
-* ``rte_pdump_init()``:
- This API initializes the packet capture framework.
+.. _figure_pdump_overview:
-* ``rte_pdump_enable()``:
- This API enables the packet capture on a given port and queue.
+.. figure:: img/pdump_overview.*
-* ``rte_pdump_enable_bpf()``
- This API enables the packet capture on a given port and queue.
- It also allows setting an optional filter using DPDK BPF interpreter
- and setting the captured packet length.
+ Packet Capture enable and disable sequence
-* ``rte_pdump_enable_by_deviceid()``:
- This API enables the packet capture on a given device id (``vdev name or pci address``) and queue.
+API Reference
+-------------
-* ``rte_pdump_enable_bpf_by_deviceid()``
- This API enables the packet capture on a given device id (``vdev name or pci address``) and queue.
- It also allows setting an optional filter using DPDK BPF interpreter
- and setting the captured packet length.
+The library exposes APIs for:
-* ``rte_pdump_disable()``:
- This API disables the packet capture on a given port and queue.
+* Initializing and uninitializing the packet capture framework.
+* Enabling and disabling packet capture.
+* Applying optional filters and limiting captured packet length.
-* ``rte_pdump_disable_by_deviceid()``:
- This API disables the packet capture on a given device id (``vdev name or pci address``) and queue.
-* ``rte_pdump_uninit()``:
- This API uninitializes the packet capture framework.
+.. function:: int rte_pdump_init(void)
+ Initialize the packet capture framework.
+
+.. function:: int rte_pdump_enable(uint16_t port_id, uint16_t queue, uint32_t flags)
+
+ Enable packet capture on the specified port and queue.
+
+.. function:: int rte_pdump_enable_bpf(uint16_t port_id, uint16_t queue, const struct rte_bpf_program *bpf, uint32_t snaplen)
+
+ Enable packet capture on the specified port and queue with an optional
+ BPF packet filter and a limit on the captured packet length.
+
+.. function:: int rte_pdump_enable_by_deviceid(const char *device_id, uint16_t queue, uint32_t flags)
+
+ Enable packet capture on the specified device ID (``vdev`` name or PCI address)
+ and queue.
+
+.. function:: int rte_pdump_enable_bpf_by_deviceid(const char *device_id, uint16_t queue, const struct rte_bpf_program *bpf, uint32_t snaplen)
+
+ Enable packet capture on the specified device ID (``vdev`` name or PCI address)
+ and queue, with optional filtering and captured packet length limit.
+
+.. function:: int rte_pdump_disable(uint16_t port_id, uint16_t queue)
+
+ Disable packet capture on the specified port and queue.
+ This applies to the current process and all other processes.
+
+.. function:: int rte_pdump_disable_by_deviceid(const char *device_id, uint16_t queue)
+
+ Disable packet capture on the specified device ID (``vdev`` name or PCI address)
+ and queue.
+
+.. function:: int rte_pdump_uninit(void)
+
+ Uninitialize the packet capture framework for this process.
+
+.. function:: int rte_pdump_stats(uint16_t port_id, struct rte_dump_stats *stats)
+
+ Reports the number of packets captured, filtered, and missed.
+ Packets maybe missed due to mbuf pool being exhausted or the ring being full.
Operation
---------
-The primary process using ``librte_pdump`` is responsible for initializing the packet
-capture framework. The packet capture framework, as part of its initialization, creates the
-multi process channel to facilitate communication with secondary process, so the
-secondary process ``app/pdump`` tool is responsible for enabling and disabling the packet capture on ports.
+All processes using ``librte_pdump`` must initialize the packet capture framework
+before use. This initialization is required in both the primary and secondary processes.
+
+DPDK provides the following utilities that use this library:
+
+* ``app/dpdk-dumpcap``
+* ``app/pdump``
Implementation Details
----------------------
-The library API ``rte_pdump_init()``, initializes the packet capture framework by creating the multi process
-channel using ``rte_mp_action_register()`` API. The primary process will listen to secondary process requests
-to enable or disable the packet capture over the multi process channel.
-
-The library APIs ``rte_pdump_enable()`` and ``rte_pdump_enable_by_deviceid()`` enables the packet capture.
-For the calls to these APIs from secondary process, the library creates the "pdump enable" request and sends
-the request to the primary process over the multi process channel. The primary process takes this request
-and enables the packet capture by registering the Ethernet RX and TX callbacks for the given port or device_id
-and queue combinations. Then the primary process will mirror the packets to the new mempool and enqueue them to
-the rte_ring that secondary process have passed to these APIs.
-
-The packet ring supports one of two formats.
-The default format enqueues copies of the original packets into the rte_ring.
-If the ``RTE_PDUMP_FLAG_PCAPNG`` is set, the mbuf data is extended
-with header and trailer to match the format of Pcapng enhanced packet block.
-The enhanced packet block has meta-data such as the timestamp, port and queue
-the packet was captured on.
-It is up to the application consuming the packets from the ring
-to select the format desired.
-
-The library APIs ``rte_pdump_disable()`` and ``rte_pdump_disable_by_deviceid()`` disables the packet capture.
-For the calls to these APIs from secondary process, the library creates the "pdump disable" request and sends
-the request to the primary process over the multi process channel. The primary process takes this request and
-disables the packet capture by removing the Ethernet RX and TX callbacks for the given port or device_id and
-queue combinations.
-
-The library API ``rte_pdump_uninit()``, uninitializes the packet capture framework by calling ``rte_mp_action_unregister()``
-function.
-
-
-Use Case: Packet Capturing
---------------------------
-
-The DPDK ``app/dpdk-dumpcap`` utility uses this library
-to capture packets in DPDK.
+``rte_pdump_init()`` creates the multi-process channel by calling
+``rte_mp_action_register()``.
+
+The primary process listens for requests from secondary processes to
+enable or disable packet capture over the multi-process channel.
+
+When a secondary process calls ``rte_pdump_enable()`` or
+``rte_pdump_enable_by_deviceid()``, the library sends a "pdump enable" request
+to the primary process. The primary process then:
+
+1. Receives the request over the multi-process channel.
+2. Registers Ethernet Rx and Tx callbacks for the specified port.
+3. Forwards the request to other secondary processes (if any)
+
+
+FAQ
+---
+
+* What is the performance impact of pdump?
+
+Setting up pdump with ``rte_pdump_init`` has no impact,
+there are no changes in the fast path.
+When pdump is enabled, the Tx and Rx fast path functions
+callbacks make a copy of the mbufs and enqueue them. This will impact
+performance. The effect can be reduced by filtering to only
+see the packets of interest and using the snaplen parameter
+to only copy the needed headers.
+
+* What happens if process does not call pdump init?
+
+If application does not call ``rte_pdump_init`` then the request
+to enable (in the capture command) will timeout and an error is returned.
+
+* Where do packets go?
+
+Packets captured are placed in the ring passed in ``rte_pdump_enable``.
+The capture application must dequeue these mbuf's and free them.
+
+* Why is copy required?
+
+A copy is used instead of incrementing the reference count because
+on transmit the device maybe using fast free which does not use refcounts;
+and on receive the application may modify the incoming packet.
+
+* What about offloads?
+
+The offload flags of the original mbuf are copied to the ring.
+It is up to the capture application to handle flags like VLAN stripping
+as necessary. Packets are captured before passing to driver and hardware
+so the actual packet on the wire maybe segmented or encapsulated based
+on the offload flags.
--
2.47.2
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2025-08-14 16:54 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-11 21:34 [RFC 0/6] Packet capture fixes Stephen Hemminger
2025-08-11 21:34 ` [RFC 1/6] dumpcap: handle primary process exit Stephen Hemminger
2025-08-11 21:35 ` [RFC 2/6] pdump: " Stephen Hemminger
2025-08-11 21:35 ` [RFC 3/6] pdump: fix races in callbacks Stephen Hemminger
2025-08-11 21:35 ` [RFC 4/6] dumpcap: handle pdump requests from primary Stephen Hemminger
2025-08-11 21:35 ` [RFC 5/6] pdump: " Stephen Hemminger
2025-08-11 21:35 ` [RFC 6/6] pdump: forward callback enable to secondary Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 0/8] Packet capture cleanup Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 1/8] dumpcap: handle primary process exit Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 2/8] pdump: " Stephen Hemminger
2025-08-14 16:52 ` [PATCH v2 3/8] pdump: fix races in callbacks Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 4/8] dumpcap: handle pdump requests from primary Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 5/8] pdump: " Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 6/8] pdump: forward callback enable to secondary Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 7/8] pdump: remove use of VLA Stephen Hemminger
2025-08-14 16:53 ` [PATCH v2 8/8] doc: update documentation on pdump library Stephen Hemminger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).