[dhcpmon] Add packet event quiescing - #91
Conversation
Allow packet handlers to be suspended and resumed without terminating socket event loops, and bound each callback batch so quiescing completes under sustained traffic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39f979be-d826-4d5c-949a-f20abb58bb83 Signed-off-by: Xichen96 <lukelin0907@gmail.com>
|
/azp run |
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
Adds infrastructure to safely “quiesce” DHCP packet processing in dhcpmon while runtime socket/interface membership state is refreshed, without tearing down event-loop threads or getting stuck draining sockets under sustained traffic.
Changes:
- Added packet-handler suspend/resume APIs in
sock_mgrand tagged suspend/resume support inevent_mgr. - Added a long-period persistent “keepalive” timer event per socket event base to prevent event-loop exit when packet events are temporarily removed.
- Bounded packet processing per callback invocation (max 64 packets) to ensure suspension has a bounded wait time.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/sock_mgr.h | Exposes new packet-handler suspend/resume APIs. |
| src/sock_mgr.cpp | Adds keepalive event per socket base and implements suspend/resume wrappers for packet handler events. |
| src/packet_handler.cpp | Limits per-callback packet processing to a fixed batch size. |
| src/event_mgr.h | Declares new suspend/resume-by-tag operations. |
| src/event_mgr.cpp | Implements suspend/resume, and fixes logging by reading event fd before freeing the event. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/packet_handler.cpp:873
recvfromcan modify thesocklen_tlength passed viaslen. Sinceslenis initialized once before the loop, subsequent iterations may pass an incorrect length, which can cause truncated address data or errors. Resetslentosizeof(sll)before eachrecvfromcall.
struct sockaddr_ll sll;
socklen_t slen = sizeof(sll);
sock_info_t &sock_info = sock_mgr_get_sock_info(fd);
for (int packet_count = 0; packet_count < MAX_PACKETS_PER_CALLBACK; packet_count++) {
buffer_sz = recvfrom(fd, sock_info.buffer, sock_info.snaplen, MSG_DONTWAIT,
(struct sockaddr *)&sll, &slen);
if (buffer_sz <= 0) {
break;
Reset sockaddr length for each batch receive and reject timeout events from the fd-only resume path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39f979be-d826-4d5c-949a-f20abb58bb83 Signed-off-by: Xichen96 <lukelin0907@gmail.com>
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
src/event_mgr.cpp:110
event_mgr::resume_all_events()returns-1whenevent_add()fails but does not log any details about the failing event/tag/name. This makes operational debugging difficult when resume fails at runtime.
if (event_add(event, NULL) < 0) {
this->suspend_all_events(tag);
return -1;
}
src/sock_mgr.cpp:465
- When resuming packet handlers fails for a particular socket, the function rolls back by suspending all handlers but provides no log indicating which socket failed. Adding a syslog entry here will make failures diagnosable in production.
if (info.event_mgr_ptr->resume_all_events(packet_handler_tag) < 0) {
sock_mgr_suspend_packet_handler();
return -1;
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/event_mgr.cpp:103
resume_all_events()should defensively reject an empty tag (otherwise it will iterate the internal tagless set of all events), and it should log whenevent_add()fails to aid operational debugging.
int event_mgr::resume_all_events(const std::string &tag)
{
for (const auto &event : this->event_map[tag]) {
if (event_get_fd(event) < 0) {
syslog(LOG_ALERT, "event_mgr: Cannot resume non-fd event with tag %s", tag.c_str());
Reject untagged suspend/resume requests and log the event manager and fd when a packet event cannot be restored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39f979be-d826-4d5c-949a-f20abb58bb83 Signed-off-by: Xichen96 <lukelin0907@gmail.com>
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Look up tagged event sets without default insertion, logging unknown suspend tags and returning an error for unknown resume tags. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39f979be-d826-4d5c-949a-f20abb58bb83 Signed-off-by: Xichen96 <lukelin0907@gmail.com>
|
/azp run |
Try the shared counter lock directly when no writer is pending, using the condition-variable wait path only when quiescing or writer contention requires it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39f979be-d826-4d5c-949a-f20abb58bb83 Signed-off-by: Xichen96 <lukelin0907@gmail.com>
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Copy counter maps while holding counter-state and DB synchronization locks, then release packet callbacks before Redis I/O while retaining DB serialization through writeback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39f979be-d826-4d5c-949a-f20abb58bb83 Signed-off-by: Xichen96 <lukelin0907@gmail.com>
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Validate snapshot socket keys before looking up metadata so malformed or stale caller data cannot throw from sock_map.at(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39f979be-d826-4d5c-949a-f20abb58bb83 Signed-off-by: Xichen96 <lukelin0907@gmail.com>
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/sock_mgr.cpp:577
sock_mgr_suspend_packet_handler()returns0even ifsuspend_all_events(packet_handler_tag)fails for one or more sockets (today that failure is only logged insideevent_mgr). This can leave packet-handler events active whilepacket_handlers_enabledis set to false, reintroducing the readable-fd busy-loop risk the quiescing design is trying to avoid. Consider collecting per-socket suspend results and returning an error (and/or avoiding flipping the enabled gate) if any suspend fails.
int sock_mgr_suspend_packet_handler()
{
if (packet_handler_quiesce_lock.owns_lock()) {
syslog(LOG_ALERT, "Packet handlers are already suspended");
return -1;
}
for (const auto &entry : sock_map) {
entry.second.event_mgr_ptr->suspend_all_events(packet_handler_tag);
}
set_packet_handlers_enabled(false);
try {
| void event_mgr::suspend_all_events(const std::string &tag) | ||
| { | ||
| if (tag.empty()) { | ||
| syslog(LOG_ALERT, "event_mgr: Refusing to suspend untagged events for %s", | ||
| this->name.c_str()); | ||
| return; | ||
| } | ||
| const auto tagged_events = this->event_map.find(tag); | ||
| if (tagged_events == this->event_map.end()) { | ||
| syslog(LOG_ALERT, "event_mgr: Cannot suspend unknown tag %s for %s", | ||
| tag.c_str(), this->name.c_str()); | ||
| return; | ||
| } |
| @@ -639,4 +845,9 @@ void sock_mgr_update_db_counters() | |||
| syslog_debug(LOG_INFO, "Skipped aggregated device counter entry of %sfor downstream vlan %s", | |||
| all_skipped_ifname.c_str(), downstream_ifname.c_str()); | |||
Reject non-resumable event tags before deletion, roll back partial event_del failures, and surface suspend errors through the socket manager for coherent fail-stop recovery. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39f979be-d826-4d5c-949a-f20abb58bb83 Signed-off-by: Xichen96 <lukelin0907@gmail.com>
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
| for (const auto &event : tagged_events->second) { | ||
| if (event_get_fd(event) < 0) { | ||
| syslog(LOG_ALERT, "event_mgr: Cannot resume non-fd event with tag %s for %s", | ||
| tag.c_str(), this->name.c_str()); | ||
| this->suspend_all_events(tag); | ||
| return -1; | ||
| } | ||
| if (event_add(event, NULL) < 0) { | ||
| syslog(LOG_ALERT, "event_mgr: Failed to resume event (fd=%d) with tag %s for %s", | ||
| event_get_fd(event), tag.c_str(), this->name.c_str()); | ||
| this->suspend_all_events(tag); | ||
| return -1; | ||
| } | ||
| } | ||
| return 0; | ||
| } |
| void sock_mgr_update_db_counters() | ||
| { | ||
| sock_mgr_update_db_counters(sock_mgr_copy_cache_counters()); |
|
Closing after informal scope review. This draft combined unrelated callback batching, counter synchronization, DB snapshotting, and future event suspension. Replacements: #96 bounded callbacks, #97 counter-state synchronization, and #98 stable DB snapshot writeback. CONFIG_DB event suspension remains deferred. |
Description of PR
Summary:
dhcpmonneeds a safe way to pause packet processing briefly while replacingruntime interface membership state. Removing the last packet event from a
socket event base can terminate its event-loop thread, and the existing packet
callback drains the socket without a batch bound, so a pause can block
indefinitely under sustained traffic.
This focused prerequisite adds reusable packet-event suspend/resume operations,
keeps every socket event base alive while packet events are suspended, and
bounds each callback batch.
Work item tracking
Type of change
Approach
What is the motivation for this PR?
Runtime
VLAN_MEMBERandPORTCHANNEL_MEMBERrefresh must not restartdhcpmon, terminate packet event-loop threads, or hang while DHCP traffic iscontinuous.
How did you do it?
temporarily removing packet events cannot make the event loop exit.
bounded wait under sustained traffic.
path.
How did you verify/test it?
Pending this PR's Azure CI and combined master hardware validation with the
follow-up runtime-membership PR.
Any platform specific information?
No.
Back port request
None. This prerequisite targets master only.
Tested branch
Pending.
Test result
Pending this PR's Azure CI.
Documentation
Not applicable; no user-facing command or counter schema changes.