<div dir="auto">Not registering pdump in secondary means there is no capture in secondary. Still think that is misuse</div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Sun, Jul 5, 2026, 23:08 Kumar, Pushpendra1X <<a href="mailto:pushpendra1x.kumar@intel.com">pushpendra1x.kumar@intel.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div lang="EN-IN" link="blue" vlink="purple" style="overflow-wrap: break-word;">
<div class="m_-4574549248435063437WordSection1">
<p class="MsoNormal"><span>Hi Stephen,<u></u><u></u></span></p>
<p class="MsoNormal"><span><u></u> <u></u></span></p>
<p class="MsoNormal"><span>Thanks for taking time to review.<u></u><u></u></span></p>
<p class="MsoNormal"><span><u></u> <u></u></span></p>
<p class="MsoNormal"><span>Just to clarify, the async ENABLE/DISABLE approach was part of v1 and I<u></u><u></u></span></p>
<p class="MsoNormal"><span>dropped that direction in v2.<u></u><u></u></span></p>
<p class="MsoNormal"><span><u></u> <u></u></span></p>
<p class="MsoNormal"><span>The current proposal still uses synchronous request handling. The EAL<u></u><u></u></span></p>
<p class="MsoNormal"><span>change is only intended to allow pdump to ignore peers that do not<u></u><u></u></span></p>
<p class="MsoNormal"><span>register the pdump action, while preserving existing<u></u><u></u></span></p>
<p class="MsoNormal"><span>rte_mp_request_sync() semantics for current users.<u></u><u></u></span></p>
<p class="MsoNormal"><span><u></u> <u></u></span></p>
<p class="MsoNormal"><span>The generation tracking and inflight synchronization added in pdump are<u></u><u></u></span></p>
<p class="MsoNormal"><span>there to prevent teardown races from delayed processing after a DISABLE<u></u><u></u></span></p>
<p class="MsoNormal"><span>request, not to make enable/disable asynchronous. The intent is simply<u></u><u></u></span></p>
<p class="MsoNormal"><span>to avoid releasing shared resources while another secondary may still be<u></u><u></u></span></p>
<p class="MsoNormal"><span>processing a previously forwarded request.<u></u><u></u></span></p>
<p class="MsoNormal"><span>I may be being overly cautious here, but I'd appreciate your thoughts on<u></u><u></u></span></p>
<p class="MsoNormal"><span>whether you consider this protection unnecessary.<u></u><u></u></span></p>
<p class="MsoNormal"><span><u></u> <u></u></span></p>
<p class="MsoNormal"><span>My concern is that the forwarding model currently assumes all<u></u><u></u></span></p>
<p class="MsoNormal"><span>secondaries participate in pdump, while EAL does not enforce or track<u></u><u></u></span></p>
<p class="MsoNormal"><span>that assumption.<u></u><u></u></span></p>
<p class="MsoNormal"><span><u></u> <u></u></span></p>
<p class="MsoNormal"><span>On the other hand, if EAL were to handle this generically, it would need<u></u><u></u></span></p>
<p class="MsoNormal"><span>a mechanism to determine which secondary processes have registered a<u></u><u></u></span></p>
<p class="MsoNormal"><span>given action before forwarding requests. The proposed API remains<u></u><u></u></span></p>
<p class="MsoNormal"><span>generic, while pdump is simply its first user.<u></u><u></u></span></p>
<p class="MsoNormal"><span><u></u> <u></u></span></p>
<p class="MsoNormal"><span>Thanks,<u></u><u></u></span></p>
<p class="MsoNormal"><span>Pushpendra<u></u><u></u></span></p>
<p class="MsoNormal"><span><u></u> <u></u></span></p>
<div style="border-width:1pt medium medium;border-style:solid none none;border-color:rgb(225,225,225) currentcolor currentcolor;padding:3pt 0in 0in">
<p class="MsoNormal"><b><span lang="EN-US" style="font-size:11pt;font-family:Calibri,sans-serif">From:</span></b><span lang="EN-US" style="font-size:11pt;font-family:Calibri,sans-serif"> Stephen Hemminger <<a href="mailto:stephen@networkplumber.org" target="_blank" rel="noreferrer">stephen@networkplumber.org</a>>
<br>
<b>Sent:</b> Sunday, July 5, 2026 11:58 PM<br>
<b>To:</b> Kumar, Pushpendra1X <<a href="mailto:pushpendra1x.kumar@intel.com" target="_blank" rel="noreferrer">pushpendra1x.kumar@intel.com</a>><br>
<b>Cc:</b> dev <<a href="mailto:dev@dpdk.org" target="_blank" rel="noreferrer">dev@dpdk.org</a>>; stable <<a href="mailto:stable@dpdk.org" target="_blank" rel="noreferrer">stable@dpdk.org</a>>; Thomas Monjalon <<a href="mailto:thomas@monjalon.net" target="_blank" rel="noreferrer">thomas@monjalon.net</a>>; Pattan, Reshma <<a href="mailto:reshma.pattan@intel.com" target="_blank" rel="noreferrer">reshma.pattan@intel.com</a>>; Burakov, Anatoly <<a href="mailto:anatoly.burakov@intel.com" target="_blank" rel="noreferrer">anatoly.burakov@intel.com</a>>; Dmitry Kozlyuk <<a href="mailto:dmitry.kozliuk@gmail.com" target="_blank" rel="noreferrer">dmitry.kozliuk@gmail.com</a>>; Richardson, Bruce <<a href="mailto:bruce.richardson@intel.com" target="_blank" rel="noreferrer">bruce.richardson@intel.com</a>>;
<a href="mailto:14pwcse1224@uetpeshawar.edu.pk" target="_blank" rel="noreferrer">14pwcse1224@uetpeshawar.edu.pk</a>; <a href="mailto:pushpendra.kumar@tieto.com" target="_blank" rel="noreferrer">pushpendra.kumar@tieto.com</a><br>
<b>Subject:</b> Re: [PATCH v2] pdump: fix teardown race with opt-in MP request mode<u></u><u></u></span></p>
</div>
<p class="MsoNormal"><u></u> <u></u></p>
<div>
<p class="MsoNormal">Root cause is not calling rte_pdump_init in secondary. This should be detected or fixed automatically by EAL. Working around with async adds unnecessary complexity <u></u><u></u></p>
</div>
<p class="MsoNormal"><u></u> <u></u></p>
<div>
<div>
<p class="MsoNormal">On Sun, Jul 5, 2026, 01:48 Pushpendra Kumar <<a href="mailto:pushpendra1x.kumar@intel.com" target="_blank" rel="noreferrer">pushpendra1x.kumar@intel.com</a>> wrote:<u></u><u></u></p>
</div>
<blockquote style="border-width:medium medium medium 1pt;border-style:none none none solid;border-color:currentcolor currentcolor currentcolor rgb(204,204,204);padding:0in 0in 0in 6pt;margin-left:4.8pt;margin-right:0in">
<p class="MsoNormal" style="margin-bottom:12pt">This change addresses two pdump teardown failure cases:<br>
<br>
1. A requester secondary can time out if a forwarded secondary is slow,<br>
unresponsive, or does not implement rte_pdump_init().<br>
2. After a DISABLE request times out, the requester secondary receives<br>
the reply and releases shared resources, which other slow or<br>
unresponsive secondaries may access, leading to a crash.<br>
<br>
Add an opt-in EAL multiprocess request mode that allows a caller to<br>
ignore peers that do not implement the requested action, while preserving<br>
the existing rte_mp_request_sync() behavior for legacy users.<br>
<br>
pdump uses this new mode when forwarding enable/disable requests and adds<br>
the following behavior:<br>
- track request generation so stale callbacks cannot act on a newer<br>
enable/disable cycle<br>
- wait for in-flight callbacks to drain before completing disable<br>
- prevent shared resource teardown from racing with delayed secondary<br>
processing<br>
- keep pdump intent-driven without extra consumer-specific workaround<br>
logic<br>
<br>
The EAL change is required because the current mp request path treats<br>
a missing action as a failure, which cannot satisfy pdump's needs without<br>
changing behavior for all existing users. The new API is opt-in, so<br>
existing callers of rte_mp_request_sync() keep their current semantics.<br>
<br>
Validation:<br>
- Baseline-vs-changes benchmark used DPDK-native test-pmd as primary and<br>
dumpcap as secondary.<br>
- Both runs captured 1024 packets with the same packet-count exit<br>
condition.<br>
- A 5-second perf stat sample on the forwarding primary showed no<br>
meaningful regression: with changes recorded 12,402,055,320 cycles and<br>
17,391,769,084 instructions, versus 12,450,031,900 cycles and<br>
17,499,706,053 instructions on the parent baseline.<br>
- The extra synchronization in the hot path did not show material<br>
overhead in this sample.<br>
<br>
Fixes: c3ceb8742295 ("pdump: forward callback enable to secondary process")<br>
Signed-off-by: Pushpendra Kumar <<a href="mailto:pushpendra1x.kumar@intel.com" target="_blank" rel="noreferrer">pushpendra1x.kumar@intel.com</a>><br>
<br>
Cc: <a href="mailto:stable@dpdk.org" target="_blank" rel="noreferrer">stable@dpdk.org</a><br>
---<br>
This V2 patch addresses two pdump teardown failure scenarios:<br>
<br>
- A requester secondary can time out if a forwarded secondary is slow,<br>
unresponsive, or does not implement rte_pdump_init().<br>
- A delayed DISABLE reply can race with requester-side teardown, which may<br>
lead to crashes if peer processes still access shared pdump resources.<br>
<br>
To fix this end-to-end:<br>
- EAL adds an opt-in MP request mode to ignore peers with no matching action,<br>
while preserving legacy rte_mp_request_sync() behavior.<br>
- pdump uses that mode, tracks generation, and waits for in-flight callbacks<br>
to drain before completing disable.<br>
- dumpcap and pdump app cleanup paths are fail-closed when disable does not<br>
complete successfully.<br>
<br>
If this direction is accepted, I can split from the next revision into a<br>
focused EAL patch followed by pdump and app-user updates.<br>
<br>
.mailmap | 1 +<br>
app/dumpcap/main.c | 23 ++-<br>
app/pdump/main.c | 32 +++-<br>
doc/guides/rel_notes/release_26_07.rst | 7 +<br>
lib/eal/common/eal_common_proc.c | 37 +++-<br>
lib/eal/include/rte_eal.h | 33 ++++<br>
lib/eal/windows/eal_mp.c | 9 +<br>
lib/pdump/rte_pdump.c | 241 ++++++++++++++++++++++---<br>
lib/pdump/rte_pdump.h | 40 +++-<br>
9 files changed, 379 insertions(+), 44 deletions(-)<br>
<br>
diff --git a/.mailmap b/.mailmap<br>
index c5bc728fae..c1313c7148 100644<br>
--- a/.mailmap<br>
+++ b/.mailmap<br>
@@ -1332,6 +1332,7 @@ Przemyslaw Gierszynski <<a href="mailto:przemyslaw.gierszynski@intel.com" target="_blank" rel="noreferrer">przemyslaw.gierszynski@intel.com</a>><br>
Przemyslaw Patynowski <<a href="mailto:przemyslawx.patynowski@intel.com" target="_blank" rel="noreferrer">przemyslawx.patynowski@intel.com</a>><br>
Przemyslaw Zegan <<a href="mailto:przemyslawx.zegan@intel.com" target="_blank" rel="noreferrer">przemyslawx.zegan@intel.com</a>><br>
Pu Xu <<a href="mailto:583493798@qq.com" target="_blank" rel="noreferrer">583493798@qq.com</a>><br>
+Pushpendra Kumar <<a href="mailto:pushpendra1x.kumar@intel.com" target="_blank" rel="noreferrer">pushpendra1x.kumar@intel.com</a>><br>
Qi Fu <<a href="mailto:qi.fu@intel.com" target="_blank" rel="noreferrer">qi.fu@intel.com</a>><br>
Qi Zhang <<a href="mailto:qi.z.zhang@intel.com" target="_blank" rel="noreferrer">qi.z.zhang@intel.com</a>><br>
Qian Hao <<a href="mailto:qi_an_hao@126.com" target="_blank" rel="noreferrer">qi_an_hao@126.com</a>><br>
diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c<br>
index 46a6cb251e..f421a17034 100644<br>
--- a/app/dumpcap/main.c<br>
+++ b/app/dumpcap/main.c<br>
@@ -521,14 +521,33 @@ static void<br>
cleanup_pdump_resources(void)<br>
{<br>
struct interface *intf;<br>
+ int ret;<br>
+ bool disable_failed = false;<br>
<br>
TAILQ_FOREACH(intf, &interfaces, next) {<br>
- rte_pdump_disable(intf->port,<br>
- RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX);<br>
+ ret = rte_pdump_disable(intf->port,<br>
+ RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX);<br>
+ if (ret < 0) {<br>
+ disable_failed = true;<br>
+ fprintf(stderr,<br>
+ "Disable pdump failed on %u:%s: %s\n",<br>
+ intf->port, intf->name,<br>
+ rte_strerror(rte_errno));<br>
+ }<br>
if (intf->opts.promisc_mode)<br>
rte_eth_promiscuous_disable(intf->port);<br>
}<br>
<br>
+ if (disable_failed) {<br>
+ /*<br>
+ * Fail-closed: if disable did not complete, keep pdump action/state<br>
+ * alive and do not uninit shared capture resources.<br>
+ */<br>
+ fprintf(stderr,<br>
+ "Skipping pdump uninit because disable did not complete on all interfaces\n");<br>
+ return;<br>
+ }<br>
+<br>
rte_pdump_uninit();<br>
}<br>
<br>
diff --git a/app/pdump/main.c b/app/pdump/main.c<br>
index 1e62c8adc1..4cfb038395 100644<br>
--- a/app/pdump/main.c<br>
+++ b/app/pdump/main.c<br>
@@ -451,14 +451,17 @@ print_pdump_stats(void)<br>
}<br>
}<br>
<br>
-static inline void<br>
+static inline int<br>
disable_pdump(struct pdump_tuples *pt)<br>
{<br>
if (pt->dump_by_type == DEVICE_ID)<br>
- rte_pdump_disable_by_deviceid(pt->device_id, pt->queue,<br>
- pt->dir);<br>
+ return rte_pdump_disable_by_deviceid(pt->device_id, pt->queue,<br>
+ pt->dir);<br>
else if (pt->dump_by_type == PORT_ID)<br>
- rte_pdump_disable(pt->port, pt->queue, pt->dir);<br>
+ return rte_pdump_disable(pt->port, pt->queue, pt->dir);<br>
+<br>
+ rte_errno = EINVAL;<br>
+ return -EINVAL;<br>
}<br>
<br>
static inline void<br>
@@ -518,15 +521,30 @@ static void<br>
cleanup_pdump_resources(void)<br>
{<br>
int i;<br>
+ int ret;<br>
+ bool disable_failed = false;<br>
struct pdump_tuples *pt;<br>
char name[RTE_ETH_NAME_MAX_LEN];<br>
<br>
- /* disable pdump and free the pdump_tuple resources */<br>
+ /* Disable all callbacks first; freeing shared objects before this is unsafe. */<br>
for (i = 0; i < num_tuples; i++) {<br>
pt = &pdump_t[i];<br>
<br>
- /* remove callbacks */<br>
- disable_pdump(pt);<br>
+ ret = disable_pdump(pt);<br>
+ if (ret < 0) {<br>
+ disable_failed = true;<br>
+ printf("pdump disable failed (tuple=%d, errno=%d: %s); "<br>
+ "skip teardown to avoid stale callback access\n",<br>
+ i, rte_errno, rte_strerror(rte_errno));<br>
+ }<br>
+ }<br>
+<br>
+ if (disable_failed)<br>
+ return;<br>
+<br>
+ /* free the pdump_tuple resources */<br>
+ for (i = 0; i < num_tuples; i++) {<br>
+ pt = &pdump_t[i];<br>
<br>
/*<br>
* transmit rest of the enqueued packets of the rings on to<br>
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst<br>
index 4ca0a9ac77..1700a56bc7 100644<br>
--- a/doc/guides/rel_notes/release_26_07.rst<br>
+++ b/doc/guides/rel_notes/release_26_07.rst<br>
@@ -177,6 +177,13 @@ New Features<br>
Added AGENTS.md file for AI review<br>
and supporting scripts to review patches and documentation.<br>
<br>
+* **Hardened pdump teardown on disable failure.**<br>
+<br>
+ Hardened pdump request completion and application cleanup behavior so timeout<br>
+ or disable failure does not trigger premature pdump resource teardown.<br>
+ pdump now uses an opt-in MP request mode that ignores peers without the<br>
+ pdump action, while preserving legacy EAL MP behavior for all other users.<br>
+<br>
<br>
Removed Items<br>
-------------<br>
diff --git a/lib/eal/common/eal_common_proc.c b/lib/eal/common/eal_common_proc.c<br>
index 06f151818c..5a458c0417 100644<br>
--- a/lib/eal/common/eal_common_proc.c<br>
+++ b/lib/eal/common/eal_common_proc.c<br>
@@ -56,6 +56,7 @@ static struct action_entry_list action_entry_list =<br>
enum mp_type {<br>
MP_MSG, /* Share message with peers, will not block */<br>
MP_REQ, /* Request for information, Will block for a reply */<br>
+ MP_REQ_IGN, /* Request where missing action should return ignore */<br>
MP_REP, /* Response to previously-received request */<br>
MP_IGN, /* Response telling requester to ignore this response */<br>
};<br>
@@ -384,11 +385,11 @@ process_msg(struct mp_msg_internal *m, struct sockaddr_un *s)<br>
pthread_mutex_unlock(&mp_mutex_action);<br>
<br>
if (!action) {<br>
- if (m->type == MP_REQ && !internal_conf->init_complete) {<br>
- /* if this is a request, and init is not yet complete,<br>
- * and callback wasn't registered, we should tell the<br>
- * requester to ignore our existence because we're not<br>
- * yet ready to process this request.<br>
+ if ((m->type == MP_REQ && !internal_conf->init_complete) ||<br>
+ m->type == MP_REQ_IGN) {<br>
+ /*<br>
+ * Ask requester to ignore this peer when action is not<br>
+ * registered either due to early init or explicit request policy.<br>
*/<br>
struct rte_mp_msg dummy;<br>
<br>
@@ -936,7 +937,8 @@ mp_request_async(const char *dst, struct rte_mp_msg *req,<br>
<br>
static int<br>
mp_request_sync(const char *dst, struct rte_mp_msg *req,<br>
- struct rte_mp_reply *reply, const struct timespec *ts)<br>
+ struct rte_mp_reply *reply, const struct timespec *ts,<br>
+ enum mp_type req_type)<br>
{<br>
int ret;<br>
pthread_condattr_t attr;<br>
@@ -959,7 +961,7 @@ mp_request_sync(const char *dst, struct rte_mp_msg *req,<br>
return -1;<br>
}<br>
<br>
- ret = send_msg(dst, req, MP_REQ);<br>
+ ret = send_msg(dst, req, req_type);<br>
if (ret < 0) {<br>
EAL_LOG(ERR, "Fail to send request %s:%s",<br>
dst, req->name);<br>
@@ -1010,11 +1012,20 @@ RTE_EXPORT_SYMBOL(rte_mp_request_sync)<br>
int<br>
rte_mp_request_sync(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
const struct timespec *ts)<br>
+{<br>
+ return rte_mp_request_sync_ex(req, reply, ts, 0);<br>
+}<br>
+<br>
+RTE_EXPORT_SYMBOL(rte_mp_request_sync_ex)<br>
+int<br>
+rte_mp_request_sync_ex(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
+ const struct timespec *ts, uint32_t flags)<br>
{<br>
int dir_fd, ret = -1;<br>
DIR *mp_dir;<br>
struct dirent *ent;<br>
struct timespec now, end;<br>
+ enum mp_type req_type = MP_REQ;<br>
const struct internal_config *internal_conf =<br>
eal_get_internal_configuration();<br>
<br>
@@ -1027,6 +1038,14 @@ rte_mp_request_sync(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
if (check_input(req) != 0)<br>
goto end;<br>
<br>
+ if (flags & ~RTE_MP_REQ_F_IGNORE_NO_ACTION) {<br>
+ rte_errno = EINVAL;<br>
+ goto end;<br>
+ }<br>
+<br>
+ if (flags & RTE_MP_REQ_F_IGNORE_NO_ACTION)<br>
+ req_type = MP_REQ_IGN;<br>
+<br>
if (internal_conf->no_shconf) {<br>
EAL_LOG(DEBUG, "No shared files mode enabled, IPC is disabled");<br>
rte_errno = ENOTSUP;<br>
@@ -1046,7 +1065,7 @@ rte_mp_request_sync(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
/* for secondary process, send request to the primary process only */<br>
if (rte_eal_process_type() == RTE_PROC_SECONDARY) {<br>
pthread_mutex_lock(&pending_requests.lock);<br>
- ret = mp_request_sync(eal_mp_socket_path(), req, reply, &end);<br>
+ ret = mp_request_sync(eal_mp_socket_path(), req, reply, &end, req_type);<br>
pthread_mutex_unlock(&pending_requests.lock);<br>
goto end;<br>
}<br>
@@ -1086,7 +1105,7 @@ rte_mp_request_sync(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
/* unlocks the mutex while waiting for response,<br>
* locks on receive<br>
*/<br>
- if (mp_request_sync(path, req, reply, &end))<br>
+ if (mp_request_sync(path, req, reply, &end, req_type))<br>
goto unlock_end;<br>
}<br>
ret = 0;<br>
diff --git a/lib/eal/include/rte_eal.h b/lib/eal/include/rte_eal.h<br>
index 7241f3be5d..415b974945 100644<br>
--- a/lib/eal/include/rte_eal.h<br>
+++ b/lib/eal/include/rte_eal.h<br>
@@ -173,6 +173,15 @@ struct rte_mp_reply {<br>
struct rte_mp_msg *msgs; /* caller to free */<br>
};<br>
<br>
+/** Request flags for rte_mp_request_sync_ex(). */<br>
+enum rte_mp_request_flags {<br>
+ /**<br>
+ * Ask peers that do not have a registered action to return MP_IGN<br>
+ * instead of causing timeout for this request.<br>
+ */<br>
+ RTE_MP_REQ_F_IGNORE_NO_ACTION = 1u << 0,<br>
+};<br>
+<br>
/**<br>
* Action function typedef used by other components.<br>
*<br>
@@ -292,6 +301,30 @@ int<br>
rte_mp_request_sync(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
const struct timespec *ts);<br>
<br>
+/**<br>
+ * Send a request to peer processes with explicit request flags.<br>
+ *<br>
+ * This API is equivalent to rte_mp_request_sync() with opt-in behavior<br>
+ * controls provided through @p flags.<br>
+ *<br>
+ * @param req<br>
+ * The req argument contains the customized request message.<br>
+ * @param reply<br>
+ * The reply argument will be for storing all the replied messages;<br>
+ * the caller is responsible for free reply->msgs.<br>
+ * @param ts<br>
+ * The ts argument specifies how long we can wait for the peer(s) to reply.<br>
+ * @param flags<br>
+ * Bitmask of values from enum rte_mp_request_flags.<br>
+ *<br>
+ * @return<br>
+ * - On success, return 0.<br>
+ * - On failure, return -1, and the reason will be stored in rte_errno.<br>
+ */<br>
+int<br>
+rte_mp_request_sync_ex(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
+ const struct timespec *ts, uint32_t flags);<br>
+<br>
/**<br>
* Send a request to the peer process and expect a reply in a separate callback.<br>
*<br>
diff --git a/lib/eal/windows/eal_mp.c b/lib/eal/windows/eal_mp.c<br>
index 6703355318..1066a4d2bc 100644<br>
--- a/lib/eal/windows/eal_mp.c<br>
+++ b/lib/eal/windows/eal_mp.c<br>
@@ -56,10 +56,19 @@ RTE_EXPORT_SYMBOL(rte_mp_request_sync)<br>
int<br>
rte_mp_request_sync(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
const struct timespec *ts)<br>
+{<br>
+ return rte_mp_request_sync_ex(req, reply, ts, 0);<br>
+}<br>
+<br>
+RTE_EXPORT_SYMBOL(rte_mp_request_sync_ex)<br>
+int<br>
+rte_mp_request_sync_ex(struct rte_mp_msg *req, struct rte_mp_reply *reply,<br>
+ const struct timespec *ts, uint32_t flags)<br>
{<br>
RTE_SET_USED(req);<br>
RTE_SET_USED(reply);<br>
RTE_SET_USED(ts);<br>
+ RTE_SET_USED(flags);<br>
EAL_LOG_NOT_IMPLEMENTED();<br>
return -1;<br>
}<br>
diff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c<br>
index ac94efe7ff..3764aeb2d3 100644<br>
--- a/lib/pdump/rte_pdump.c<br>
+++ b/lib/pdump/rte_pdump.c<br>
@@ -3,9 +3,12 @@<br>
*/<br>
<br>
#include <stdlib.h><br>
+#include <errno.h><br>
<br>
#include <eal_export.h><br>
+#include <rte_eal.h><br>
#include <rte_alarm.h><br>
+#include <rte_cycles.h><br>
#include <rte_mbuf.h><br>
#include <rte_ethdev.h><br>
#include <rte_lcore.h><br>
@@ -68,6 +71,7 @@ struct pdump_request {<br>
<br>
const struct rte_bpf_prm *prm;<br>
uint32_t snaplen;<br>
+ uint32_t generation;<br>
};<br>
<br>
struct pdump_response {<br>
@@ -81,6 +85,11 @@ struct pdump_bundle {<br>
char peer[];<br>
};<br>
<br>
+struct pdump_shared_ctl {<br>
+ RTE_ATOMIC(uint32_t) enabled;<br>
+ RTE_ATOMIC(uint32_t) inflight;<br>
+};<br>
+<br>
static struct pdump_rxtx_cbs {<br>
struct rte_ring *ring;<br>
struct rte_mempool *mp;<br>
@@ -88,11 +97,11 @@ static struct pdump_rxtx_cbs {<br>
const struct rte_bpf *filter;<br>
enum pdump_version ver;<br>
uint32_t snaplen;<br>
+ uint32_t generation;<br>
RTE_ATOMIC(uint32_t) use_count;<br>
} rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT],<br>
tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];<br>
<br>
-<br>
/*<br>
* The packet capture statistics keep track of packets<br>
* accepted, filtered and dropped. These are per-queue<br>
@@ -102,9 +111,59 @@ static const char MZ_RTE_PDUMP_STATS[] = "rte_pdump_stats";<br>
static struct {<br>
struct rte_pdump_stats rx[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];<br>
struct rte_pdump_stats tx[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];<br>
+ struct pdump_shared_ctl rx_ctl[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];<br>
+ struct pdump_shared_ctl tx_ctl[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];<br>
+ RTE_ATOMIC(uint32_t) generation;<br>
const struct rte_memzone *mz;<br>
} *pdump_stats;<br>
<br>
+static int<br>
+pdump_enter(struct pdump_shared_ctl *ctl, const struct pdump_rxtx_cbs *cbs)<br>
+{<br>
+ uint32_t generation = rte_atomic_load_explicit(&pdump_stats->generation,<br>
+ rte_memory_order_relaxed);<br>
+<br>
+ if (rte_atomic_load_explicit(&ctl->enabled, rte_memory_order_relaxed) == 0 ||<br>
+ cbs->generation != generation)<br>
+ return 0;<br>
+<br>
+ rte_atomic_fetch_add_explicit(&ctl->inflight, 1, rte_memory_order_acquire);<br>
+<br>
+ /*<br>
+ * Disable/re-enable may race with callback entry. Re-check enabled and<br>
+ * generation after taking inflight reference so disable can wait for this<br>
+ * callback to drain and stale callbacks become no-op.<br>
+ */<br>
+ generation = rte_atomic_load_explicit(&pdump_stats->generation,<br>
+ rte_memory_order_relaxed);<br>
+ if (rte_atomic_load_explicit(&ctl->enabled, rte_memory_order_relaxed) != 0 &&<br>
+ cbs->generation == generation)<br>
+ return 1;<br>
+<br>
+ rte_atomic_fetch_sub_explicit(&ctl->inflight, 1, rte_memory_order_release);<br>
+ return 0;<br>
+}<br>
+<br>
+static __rte_always_inline void<br>
+pdump_exit(struct pdump_shared_ctl *ctl)<br>
+{<br>
+ rte_atomic_fetch_sub_explicit(&ctl->inflight, 1, rte_memory_order_release);<br>
+}<br>
+<br>
+static int<br>
+pdump_wait_inflight(struct pdump_shared_ctl *ctl)<br>
+{<br>
+ uint64_t end_tsc = rte_get_timer_cycles() + MP_TIMEOUT_S * rte_get_timer_hz();<br>
+<br>
+ while (rte_atomic_load_explicit(&ctl->inflight, rte_memory_order_acquire) != 0) {<br>
+ if (rte_get_timer_cycles() > end_tsc)<br>
+ return -ETIMEDOUT;<br>
+ rte_pause();<br>
+ }<br>
+<br>
+ return 0;<br>
+}<br>
+<br>
static void<br>
pdump_cb_wait(struct pdump_rxtx_cbs *cbs)<br>
{<br>
@@ -224,12 +283,17 @@ pdump_rx(uint16_t port, uint16_t queue,<br>
uint16_t max_pkts __rte_unused, void *user_params)<br>
{<br>
struct pdump_rxtx_cbs *cbs = user_params;<br>
+ struct pdump_shared_ctl *ctl = &pdump_stats->rx_ctl[port][queue];<br>
struct rte_pdump_stats *stats = &pdump_stats->rx[port][queue];<br>
<br>
+ if (!pdump_enter(ctl, cbs))<br>
+ return nb_pkts;<br>
+<br>
pdump_cb_hold(cbs);<br>
pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_IN,<br>
pkts, nb_pkts, cbs, stats);<br>
pdump_cb_release(cbs);<br>
+ pdump_exit(ctl);<br>
<br>
return nb_pkts;<br>
}<br>
@@ -239,12 +303,17 @@ pdump_tx(uint16_t port, uint16_t queue,<br>
struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params)<br>
{<br>
struct pdump_rxtx_cbs *cbs = user_params;<br>
+ struct pdump_shared_ctl *ctl = &pdump_stats->tx_ctl[port][queue];<br>
struct rte_pdump_stats *stats = &pdump_stats->tx[port][queue];<br>
<br>
+ if (!pdump_enter(ctl, cbs))<br>
+ return nb_pkts;<br>
+<br>
pdump_cb_hold(cbs);<br>
pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_OUT,<br>
pkts, nb_pkts, cbs, stats);<br>
pdump_cb_release(cbs);<br>
+ pdump_exit(ctl);<br>
<br>
return nb_pkts;<br>
}<br>
@@ -255,20 +324,43 @@ pdump_register_rx_callbacks(enum pdump_version ver,<br>
uint16_t end_q, uint16_t port, uint16_t queue,<br>
struct rte_ring *ring, struct rte_mempool *mp,<br>
struct rte_bpf *filter,<br>
- uint16_t operation, uint32_t snaplen)<br>
+ uint16_t operation, uint32_t snaplen,<br>
+ uint32_t generation)<br>
{<br>
uint16_t qid;<br>
<br>
qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue;<br>
for (; qid < end_q; qid++) {<br>
struct pdump_rxtx_cbs *cbs = &rx_cbs[port][qid];<br>
+ struct pdump_shared_ctl *ctl = &pdump_stats->rx_ctl[port][qid];<br>
<br>
if (operation == ENABLE) {<br>
+ int ret;<br>
+<br>
if (cbs->cb) {<br>
- PDUMP_LOG_LINE(ERR,<br>
- "rx callback for port=%d queue=%d, already exists",<br>
- port, qid);<br>
- return -EEXIST;<br>
+ if (cbs->generation == generation &&<br>
+ rte_atomic_load_explicit(&ctl->enabled,<br>
+ rte_memory_order_relaxed) != 0) {<br>
+ PDUMP_LOG_LINE(ERR,<br>
+ "rx callback for port=%d queue=%d, already exists",<br>
+ port, qid);<br>
+ return -EEXIST;<br>
+ }<br>
+<br>
+ PDUMP_LOG_LINE(DEBUG,<br>
+ "reconciling stale rx callback for port=%d queue=%d"<br>
+ " old_gen=%u new_gen=%u",<br>
+ port, qid, cbs->generation, generation);<br>
+<br>
+ ret = rte_eth_remove_rx_callback(port, qid, cbs->cb);<br>
+ if (ret < 0) {<br>
+ PDUMP_LOG_LINE(ERR,<br>
+ "failed to reconcile stale rx callback, errno=%d",<br>
+ -ret);<br>
+ return ret;<br>
+ }<br>
+ pdump_cb_wait(cbs);<br>
+ cbs->cb = NULL;<br>
}<br>
cbs->use_count = 0;<br>
cbs->ver = ver;<br>
@@ -276,6 +368,7 @@ pdump_register_rx_callbacks(enum pdump_version ver,<br>
cbs->mp = mp;<br>
cbs->snaplen = snaplen;<br>
cbs->filter = filter;<br>
+ cbs->generation = generation;<br>
<br>
cbs->cb = rte_eth_add_first_rx_callback(port, qid,<br>
pdump_rx, cbs);<br>
@@ -286,10 +379,15 @@ pdump_register_rx_callbacks(enum pdump_version ver,<br>
return rte_errno;<br>
}<br>
<br>
+ rte_atomic_store_explicit(&ctl->inflight, 0, rte_memory_order_relaxed);<br>
+ rte_atomic_store_explicit(&ctl->enabled, 1, rte_memory_order_release);<br>
+<br>
memset(&pdump_stats->rx[port][qid], 0, sizeof(struct rte_pdump_stats));<br>
} else if (operation == DISABLE) {<br>
int ret;<br>
<br>
+ rte_atomic_store_explicit(&ctl->enabled, 0, rte_memory_order_release);<br>
+<br>
if (cbs->cb == NULL) {<br>
PDUMP_LOG_LINE(ERR,<br>
"no existing rx callback for port=%d queue=%d",<br>
@@ -298,6 +396,11 @@ pdump_register_rx_callbacks(enum pdump_version ver,<br>
}<br>
ret = rte_eth_remove_rx_callback(port, qid, cbs->cb);<br>
if (ret < 0) {<br>
+ /* Keep state coherent: callback is still registered,<br>
+ * so restore enabled.<br>
+ */<br>
+ rte_atomic_store_explicit(&ctl->enabled, 1,<br>
+ rte_memory_order_release);<br>
PDUMP_LOG_LINE(ERR,<br>
"failed to remove rx callback, errno=%d",<br>
-ret);<br>
@@ -305,6 +408,10 @@ pdump_register_rx_callbacks(enum pdump_version ver,<br>
}<br>
pdump_cb_wait(cbs);<br>
cbs->cb = NULL;<br>
+<br>
+ ret = pdump_wait_inflight(ctl);<br>
+ if (ret < 0)<br>
+ return ret;<br>
}<br>
}<br>
<br>
@@ -316,7 +423,8 @@ pdump_register_tx_callbacks(enum pdump_version ver,<br>
uint16_t end_q, uint16_t port, uint16_t queue,<br>
struct rte_ring *ring, struct rte_mempool *mp,<br>
struct rte_bpf *filter,<br>
- uint16_t operation, uint32_t snaplen)<br>
+ uint16_t operation, uint32_t snaplen,<br>
+ uint32_t generation)<br>
{<br>
<br>
uint16_t qid;<br>
@@ -324,13 +432,36 @@ pdump_register_tx_callbacks(enum pdump_version ver,<br>
qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue;<br>
for (; qid < end_q; qid++) {<br>
struct pdump_rxtx_cbs *cbs = &tx_cbs[port][qid];<br>
+ struct pdump_shared_ctl *ctl = &pdump_stats->tx_ctl[port][qid];<br>
<br>
if (operation == ENABLE) {<br>
+ int ret;<br>
+<br>
if (cbs->cb) {<br>
- PDUMP_LOG_LINE(ERR,<br>
- "tx callback for port=%d queue=%d, already exists",<br>
- port, qid);<br>
- return -EEXIST;<br>
+ if (cbs->generation == generation &&<br>
+ rte_atomic_load_explicit(&ctl->enabled,<br>
+ rte_memory_order_relaxed) != 0) {<br>
+ PDUMP_LOG_LINE(ERR,<br>
+ "tx callback for port=%d queue=%d, already exists",<br>
+ port, qid);<br>
+ return -EEXIST;<br>
+ }<br>
+<br>
+ PDUMP_LOG_LINE(DEBUG,<br>
+ "reconciling stale tx callback for port=%d queue=%d"<br>
+ " old_gen=%u new_gen=%u",<br>
+ port, qid, cbs->generation, generation);<br>
+<br>
+ ret = rte_eth_remove_tx_callback(port, qid, cbs->cb);<br>
+ if (ret < 0) {<br>
+ PDUMP_LOG_LINE(ERR,<br>
+ "failed to reconcile stale tx callback, errno=%d",<br>
+ -ret);<br>
+ return ret;<br>
+ }<br>
+<br>
+ pdump_cb_wait(cbs);<br>
+ cbs->cb = NULL;<br>
}<br>
cbs->use_count = 0;<br>
cbs->ver = ver;<br>
@@ -338,6 +469,7 @@ pdump_register_tx_callbacks(enum pdump_version ver,<br>
cbs->mp = mp;<br>
cbs->snaplen = snaplen;<br>
cbs->filter = filter;<br>
+ cbs->generation = generation;<br>
<br>
cbs->cb = rte_eth_add_tx_callback(port, qid, pdump_tx,<br>
cbs);<br>
@@ -347,10 +479,15 @@ pdump_register_tx_callbacks(enum pdump_version ver,<br>
rte_errno);<br>
return rte_errno;<br>
}<br>
+<br>
+ rte_atomic_store_explicit(&ctl->inflight, 0, rte_memory_order_relaxed);<br>
+ rte_atomic_store_explicit(&ctl->enabled, 1, rte_memory_order_release);<br>
memset(&pdump_stats->tx[port][qid], 0, sizeof(struct rte_pdump_stats));<br>
} else if (operation == DISABLE) {<br>
int ret;<br>
<br>
+ rte_atomic_store_explicit(&ctl->enabled, 0, rte_memory_order_release);<br>
+<br>
if (cbs->cb == NULL) {<br>
PDUMP_LOG_LINE(ERR,<br>
"no existing tx callback for port=%d queue=%d",<br>
@@ -359,6 +496,11 @@ pdump_register_tx_callbacks(enum pdump_version ver,<br>
}<br>
ret = rte_eth_remove_tx_callback(port, qid, cbs->cb);<br>
if (ret < 0) {<br>
+ /* Keep state coherent: callback is still registered,<br>
+ * so restore enabled.<br>
+ */<br>
+ rte_atomic_store_explicit(&ctl->enabled, 1,<br>
+ rte_memory_order_release);<br>
PDUMP_LOG_LINE(ERR,<br>
"failed to remove tx callback, errno=%d",<br>
-ret);<br>
@@ -367,6 +509,10 @@ pdump_register_tx_callbacks(enum pdump_version ver,<br>
<br>
pdump_cb_wait(cbs);<br>
cbs->cb = NULL;<br>
+<br>
+ ret = pdump_wait_inflight(ctl);<br>
+ if (ret < 0)<br>
+ return ret;<br>
}<br>
}<br>
<br>
@@ -459,7 +605,8 @@ set_pdump_rxtx_cbs(const struct pdump_request *p)<br>
end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_rx_q : queue + 1;<br>
ret = pdump_register_rx_callbacks(p->ver, end_q, port, queue,<br>
ring, mp, filter,<br>
- operation, p->snaplen);<br>
+ operation, p->snaplen,<br>
+ p->generation);<br>
if (ret < 0)<br>
return ret;<br>
}<br>
@@ -469,7 +616,8 @@ set_pdump_rxtx_cbs(const struct pdump_request *p)<br>
end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_tx_q : queue + 1;<br>
ret = pdump_register_tx_callbacks(p->ver, end_q, port, queue,<br>
ring, mp, filter,<br>
- operation, p->snaplen);<br>
+ operation, p->snaplen,<br>
+ p->generation);<br>
if (ret < 0)<br>
return ret;<br>
}<br>
@@ -477,12 +625,14 @@ set_pdump_rxtx_cbs(const struct pdump_request *p)<br>
return ret;<br>
}<br>
<br>
-static void<br>
-pdump_request_to_secondary(const struct pdump_request *req)<br>
+static int<br>
+pdump_request_to_secondary_sync(const struct pdump_request *req)<br>
{<br>
struct rte_mp_msg mp_req = { };<br>
struct rte_mp_reply mp_reply;<br>
struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};<br>
+ int ret = 0;<br>
+ uint16_t i;<br>
<br>
PDUMP_LOG_LINE(DEBUG, "forward req %s to secondary", pdump_opname(req->op));<br>
<br>
@@ -490,14 +640,41 @@ pdump_request_to_secondary(const struct pdump_request *req)<br>
strlcpy(<a href="http://mp_req.name" target="_blank" rel="noreferrer">mp_req.name</a>, PDUMP_MP, sizeof(<a href="http://mp_req.name" target="_blank" rel="noreferrer">mp_req.name</a>));<br>
mp_req.len_param = sizeof(*req);<br>
<br>
- if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) != 0)<br>
- PDUMP_LOG_LINE(ERR, "rte_mp_request_sync failed");<br>
+ if (rte_mp_request_sync_ex(&mp_req, &mp_reply, &ts,<br>
+ RTE_MP_REQ_F_IGNORE_NO_ACTION) != 0) {<br>
+ PDUMP_LOG_LINE(ERR, "rte_mp_request_sync failed: %s",<br>
+ strerror(rte_errno));<br>
+ return -rte_errno;<br>
+ }<br>
<br>
- else if (mp_reply.nb_sent != mp_reply.nb_received)<br>
+ if (mp_reply.nb_sent != mp_reply.nb_received) {<br>
PDUMP_LOG_LINE(ERR, "not all secondary's replied (sent %u recv %u)",<br>
mp_reply.nb_sent, mp_reply.nb_received);<br>
+ ret = -ETIMEDOUT;<br>
+ }<br>
+<br>
+ for (i = 0; i < mp_reply.nb_received; i++) {<br>
+ struct rte_mp_msg *mp_rep = &mp_reply.msgs[i];<br>
+ const struct pdump_response *resp;<br>
+<br>
+ if (mp_rep->len_param != sizeof(*resp)) {<br>
+ PDUMP_LOG_LINE(ERR, "invalid secondary reply size %u", mp_rep->len_param);<br>
+ if (ret == 0)<br>
+ ret = -EINVAL;<br>
+ continue;<br>
+ }<br>
+<br>
+ resp = (const struct pdump_response *)mp_rep->param;<br>
+ if (resp->err_value != 0) {<br>
+ PDUMP_LOG_LINE(ERR, "secondary reply failed: op=%u err=%d",<br>
+ resp->res_op, resp->err_value);<br>
+ if (ret == 0)<br>
+ ret = resp->err_value;<br>
+ }<br>
+ }<br>
<br>
free(mp_reply.msgs);<br>
+ return ret;<br>
}<br>
<br>
/* Allocate temporary storage for passing state to the alarm thread for deferred handling */<br>
@@ -556,7 +733,10 @@ pdump_handle_primary_request(const struct rte_mp_msg *mp_msg, const void *peer)<br>
PDUMP_LOG_LINE(DEBUG, "secondary pdump %s", pdump_opname(req->op));<br>
<br>
/* Can just do it now, no need for interrupt thread */<br>
- ret = set_pdump_rxtx_cbs(req);<br>
+ if (req->op == ENABLE || req->op == DISABLE)<br>
+ ret = set_pdump_rxtx_cbs(req);<br>
+ else<br>
+ ret = -EINVAL;<br>
}<br>
<br>
return pdump_send_response(req, ret, peer);<br>
@@ -569,17 +749,29 @@ __pdump_request(void *param)<br>
{<br>
struct pdump_bundle *bundle = param;<br>
struct rte_mp_msg *msg = &bundle->msg;<br>
- const struct pdump_request *req =<br>
- (const struct pdump_request *)msg->param;<br>
+ struct pdump_request *req =<br>
+ (struct pdump_request *)msg->param;<br>
int ret;<br>
<br>
PDUMP_LOG_LINE(DEBUG, "primary pdump %s", pdump_opname(req->op));<br>
<br>
+ if (req->op == DISABLE) {<br>
+ req->generation = rte_atomic_fetch_add_explicit(&pdump_stats->generation, 1,<br>
+ rte_memory_order_acq_rel) + 1;<br>
+ } else if (req->op == ENABLE) {<br>
+ req->generation = rte_atomic_load_explicit(&pdump_stats->generation,<br>
+ rte_memory_order_acquire);<br>
+ }<br>
+<br>
ret = set_pdump_rxtx_cbs(req);<br>
<br>
- /* Primary process is responsible for broadcasting request to all secondaries */<br>
+ /*<br>
+ * Primary process is responsible for broadcasting the request to all<br>
+ * secondaries. The sync request uses opt-in ignore-missing-action mode so<br>
+ * pdump does not depend on unrelated secondary processes.<br>
+ */<br>
if (ret == 0)<br>
- pdump_request_to_secondary(req);<br>
+ ret = pdump_request_to_secondary_sync(req);<br>
<br>
pdump_send_response(req, ret, bundle->peer);<br>
free(bundle);<br>
@@ -659,6 +851,9 @@ rte_pdump_init(void)<br>
<br>
pdump_stats = mz->addr;<br>
pdump_stats->mz = mz;<br>
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY)<br>
+ rte_atomic_store_explicit(&pdump_stats->generation, 1,<br>
+ rte_memory_order_release);<br>
<br>
return 0;<br>
}<br>
diff --git a/lib/pdump/rte_pdump.h b/lib/pdump/rte_pdump.h<br>
index 1e32d46097..77e5914420 100644<br>
--- a/lib/pdump/rte_pdump.h<br>
+++ b/lib/pdump/rte_pdump.h<br>
@@ -64,7 +64,7 @@ rte_pdump_uninit(void);<br>
* queues of a given port.<br>
* @param flags<br>
* flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX<br>
- * on which packet capturing should be enabled for a given port and queue.<br>
+ * on which packet capturing should be disabled for a given port and queue.<br>
* @param ring<br>
* ring on which captured packets will be enqueued for user.<br>
* @param mp<br>
@@ -72,6 +72,11 @@ rte_pdump_uninit(void);<br>
* @param filter<br>
* Unused should be NULL.<br>
*<br>
+ * @note<br>
+ * In applications that enable capture on multiple interfaces, enable may be<br>
+ * partially applied before an error is returned. Callers should explicitly<br>
+ * unwind partial enable state.<br>
+ *<br>
* @return<br>
* 0 on success, -1 on error, rte_errno is set accordingly.<br>
*/<br>
@@ -103,6 +108,11 @@ rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags,<br>
* @param prm<br>
* Use BPF program to run to filter packes (can be NULL)<br>
*<br>
+ * @note<br>
+ * In applications that enable capture on multiple interfaces, enable may be<br>
+ * partially applied before an error is returned. Callers should explicitly<br>
+ * unwind partial enable state.<br>
+ *<br>
* @return<br>
* 0 on success, -1 on error, rte_errno is set accordingly.<br>
*/<br>
@@ -124,7 +134,14 @@ rte_pdump_enable_bpf(uint16_t port_id, uint16_t queue,<br>
* queues of a given port.<br>
* @param flags<br>
* flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX<br>
- * on which packet capturing should be enabled for a given port and queue.<br>
+ * on which packet capturing should be disabled for a given port and queue.<br>
+ *<br>
+ * @note<br>
+ * A disable failure (including timeout/no response) means teardown is not<br>
+ * complete across pdump-enabled peers. The caller must not release shared<br>
+ * pdump resources and must not uninitialize pdump for that capture session<br>
+ * until disable succeeds. Releasing resources on disable failure can lead to<br>
+ * crashes in peer processes still accessing those resources.<br>
*<br>
* @return<br>
* 0 on success, -1 on error, rte_errno is set accordingly.<br>
@@ -153,6 +170,11 @@ rte_pdump_disable(uint16_t port, uint16_t queue, uint32_t flags);<br>
* @param filter<br>
* unused should be NULL<br>
*<br>
+ * @note<br>
+ * In applications that enable capture on multiple interfaces, enable may be<br>
+ * partially applied before an error is returned. Callers should explicitly<br>
+ * unwind partial enable state.<br>
+ *<br>
* @return<br>
* 0 on success, -1 on error, rte_errno is set accordingly.<br>
*/<br>
@@ -186,6 +208,11 @@ rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue,<br>
* @param filter<br>
* Use BPF program to run to filter packes (can be NULL)<br>
*<br>
+ * @note<br>
+ * In applications that enable capture on multiple interfaces, enable may be<br>
+ * partially applied before an error is returned. Callers should explicitly<br>
+ * unwind partial enable state.<br>
+ *<br>
* @return<br>
* 0 on success, -1 on error, rte_errno is set accordingly.<br>
*/<br>
@@ -210,7 +237,14 @@ rte_pdump_enable_bpf_by_deviceid(const char *device_id, uint16_t queue,<br>
* queues of a given device id.<br>
* @param flags<br>
* flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX<br>
- * on which packet capturing should be enabled for a given port and queue.<br>
+ * on which packet capturing should be disabled for a given port and queue.<br>
+ *<br>
+ * @note<br>
+ * A disable failure (including timeout/no response) means teardown is not<br>
+ * complete across pdump-enabled peers. The caller must not release shared<br>
+ * pdump resources and must not uninitialize pdump for that capture session<br>
+ * until disable succeeds. Releasing resources on disable failure can lead to<br>
+ * crashes in peer processes still accessing those resources.<br>
*<br>
* @return<br>
* 0 on success, -1 on error, rte_errno is set accordingly.<br>
-- <br>
2.43.0<u></u><u></u></p>
</blockquote>
</div>
</div>
</div>
</blockquote></div>