Packet capture fixes
cover Packet capture fixes
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(-)
Signature · 1 line
2.47.2
RFC 1/6 dumpcap: handle primary process exit
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
--- app/dumpcap/main.c | 4 ++++ 1 file changed, 4 insertions(+)
app/dumpcap/main.c +4 -0
@@ -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);
Signature · 1 line
2.47.2
RFC 2/6 pdump: handle primary process exit
In reply to Stephen HemmingerIf primary process exits, then it is not possible (or needed) to cleanup resources. Instead just exit after closing the capture file.
Bugzilla ID: 1760
--- app/pdump/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
app/pdump/main.c +7 -5
@@ -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 ; }
Signature · 1 line
2.47.2
RFC 3/6 pdump: fix races in callbacks
In reply to Stephen HemmingerThe 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.
--- lib/pdump/rte_pdump.c | 48 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-)
lib/pdump/rte_pdump.c +46 -2
@@ -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 ; } }
Signature · 1 line
2.47.2
RFC 4/6 dumpcap: handle pdump requests from primary
In reply to Stephen HemmingerThe 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.
--- app/dumpcap/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
app/dumpcap/main.c +10 -0
@@ -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.
Signature · 1 line
2.47.2
RFC 5/6 pdump: handle pdump requests from primary
In reply to Stephen HemmingerThe 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.
--- app/pdump/main.c | 4 ++++ 1 file changed, 4 insertions(+)
app/pdump/main.c +4 -0
@@ -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 ) {
Signature · 1 line
2.47.2
RFC 6/6 pdump: forward callback enable to secondary
In reply to Stephen HemmingerWhen 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.
--- lib/pdump/rte_pdump.c | 213 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 185 insertions(+), 28 deletions(-)
lib/pdump/rte_pdump.c +185 -28
@@ -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 ;
Signature · 1 line
2.47.2
v2 cover Packet capture cleanup
In reply to Stephen HemmingerThis 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
PATCH v2 1/8 dumpcap: handle primary process exit
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
--- app/dumpcap/main.c | 4 ++++ 1 file changed, 4 insertions(+)
app/dumpcap/main.c +4 -0
@@ -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);
Signature · 1 line
2.47.2
PATCH v2 2/8 pdump: handle primary process exit
In reply to Stephen HemmingerIf primary process exits, then it is not possible (or needed) to cleanup resources. Instead just exit after closing the capture file.
Bugzilla ID: 1760
--- app/pdump/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
app/pdump/main.c +7 -5
@@ -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 ; }
Signature · 1 line
2.47.2
PATCH v2 3/8 pdump: fix races in callbacks
In reply to Stephen HemmingerThe 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.
--- lib/pdump/rte_pdump.c | 48 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-)
lib/pdump/rte_pdump.c +46 -2
@@ -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 ; } }
Signature · 1 line
2.47.2
PATCH v2 4/8 dumpcap: handle pdump requests from primary
In reply to Stephen HemmingerThe 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.
--- app/dumpcap/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
app/dumpcap/main.c +10 -0
@@ -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.
Signature · 1 line
2.47.2
PATCH v2 5/8 pdump: handle pdump requests from primary
In reply to Stephen HemmingerThe 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.
--- app/pdump/main.c | 4 ++++ 1 file changed, 4 insertions(+)
app/pdump/main.c +4 -0
@@ -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 ) {
Signature · 1 line
2.47.2
PATCH v2 6/8 pdump: forward callback enable to secondary
In reply to Stephen HemmingerWhen 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.
--- lib/pdump/rte_pdump.c | 213 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 185 insertions(+), 28 deletions(-)
lib/pdump/rte_pdump.c +185 -28
@@ -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 ;
Signature · 1 line
2.47.2
PATCH v2 7/8 pdump: remove use of VLA
In reply to Stephen HemmingerReplace variable length array with alloca.
--- lib/pdump/meson.build | 2 -- lib/pdump/rte_pdump.c | 12 ++++++++---- 2 files changed, 8 insertions(+), 6 deletions(-)
lib/pdump/meson.build +0 -2
@@ -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']
lib/pdump/rte_pdump.c +8 -4
@@ -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 ;
Signature · 1 line
2.47.2
PATCH v2 8/8 doc: update documentation on pdump library
In reply to Stephen HemmingerThe documentation was missing some of the API, and had some awkward wording. With the help of ChatGPT, update it and make it more concise.
--- 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
doc/guides/prog_guide/img/pdump_overview.svg new file +135 -0
@@ -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>
doc/guides/prog_guide/pdump_lib.rst +115 -68
205 lines not on this page · raw message
Signature · 1 line
2.47.2
v6 cover packet capture bugfixes and secondary support
In reply to Stephen HemmingerThis 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.
v6 - reduce amount of changes when replacing VLA
v5 - fix signed/unsigned mismatch in RTE_MIN
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 ++++++----- doc/guides/rel_notes/release_25_11.rst | 5 + lib/pdump/meson.build | 2 - lib/pdump/rte_pdump.c | 303 ++++++++++++++++--- 7 files changed, 544 insertions(+), 114 deletions(-) create mode 100644 doc/guides/prog_guide/img/pdump_overview.svg
Signature · 1 line
2.51.0
PATCH v6 1/8 dumpcap: handle primary process exit
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
--- app/dumpcap/main.c | 4 ++++ 1 file changed, 4 insertions(+)
app/dumpcap/main.c +4 -0
@@ -1059,6 +1059,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);
Signature · 1 line
2.51.0
PATCH v6 2/8 pdump: handle primary process exit
In reply to Stephen HemmingerIf primary process exits, then it is not possible (or needed) to cleanup resources. Instead just exit after closing the capture file.
Bugzilla ID: 1760
--- app/pdump/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
app/pdump/main.c +7 -5
@@ -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 ; }
Signature · 1 line
2.51.0
PATCH v6 3/8 pdump: fix races in callbacks
In reply to Stephen HemmingerThe 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.
--- lib/pdump/rte_pdump.c | 48 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-)
lib/pdump/rte_pdump.c +46 -2
@@ -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 ; } }
Signature · 1 line
2.51.0
PATCH v6 4/8 dumpcap: handle pdump requests from primary
In reply to Stephen HemmingerThe 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.
--- app/dumpcap/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
app/dumpcap/main.c +10 -0
@@ -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.
Signature · 1 line
2.51.0
PATCH v6 5/8 pdump: handle pdump requests from primary
In reply to Stephen HemmingerThe 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.
--- app/pdump/main.c | 4 ++++ 1 file changed, 4 insertions(+)
app/pdump/main.c +4 -0
@@ -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 ) {
Signature · 1 line
2.51.0
PATCH v6 6/8 pdump: forward callback enable to secondary
In reply to Stephen HemmingerWhen 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.
--- lib/pdump/rte_pdump.c | 213 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 185 insertions(+), 28 deletions(-)
lib/pdump/rte_pdump.c +185 -28
@@ -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 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 ;
Signature · 1 line
2.51.0
PATCH v6 7/8 pdump: remove use of VLA
In reply to Stephen HemmingerReplace variable length array with refactored loop so that packets are processed in bursts of up to 32.
--- lib/pdump/meson.build | 2 -- lib/pdump/rte_pdump.c | 42 +++++++++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 11 deletions(-)
lib/pdump/meson.build +0 -2
@@ -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']
lib/pdump/rte_pdump.c +33 -9
79 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH v6 8/8 doc: update documentation on pdump library
In reply to Stephen HemmingerThe documentation was missing some of the API, and had some awkward wording. With the help of ChatGPT, update it and make it more concise.
Add a release note
--- doc/guides/prog_guide/img/pdump_overview.svg | 135 ++++++++++++++ doc/guides/prog_guide/pdump_lib.rst | 183 ++++++++++++------- doc/guides/rel_notes/release_25_11.rst | 5 + 3 files changed, 255 insertions(+), 68 deletions(-) create mode 100644 doc/guides/prog_guide/img/pdump_overview.svg
doc/guides/prog_guide/img/pdump_overview.svg new file +135 -0
@@ -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>
doc/guides/prog_guide/pdump_lib.rst +115 -68
205 lines not on this page · raw message
doc/guides/rel_notes/release_25_11.rst +5 -0
@@ -167,6 +167,11 @@ New Features The built-in help text function is available as a public function which can be reused by custom functions, if so desired. +* **Added packet capture (pdump) for secondary process.** + + Added multi-process support to allow packets sent and received by secondary + process to be visible in packet capture. + Removed Items -------------
Signature · 1 line
2.51.0
Re: [PATCH v6 8/8] doc: update documentation on pdump library
04/11/2025 19:07, Stephen Hemminger:
doc/guides/prog_guide/pdump_lib.rst line 85
Quoted 4 lines
doc/guides/prog_guide/pdump_lib.rst +4
-0+DPDK provides the following utilities that use this library: + +* ``app/dpdk-dumpcap`` +* ``app/pdump``
Should be
* ``dpdk-dumpcap``
* ``dpdk-pdump``
Will fix while merging.
Thomas Monjalon
AppliedRe: [PATCH v6 0/8] packet capture bugfixes and secondary support
In reply to Stephen HemmingerQuoted 9 lines
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
Applied, thanks.