[intfsorch]: Retry explicit router interface VRF rehome#4764
Conversation
|
/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
This PR extends IntfsOrch’s retry/retention model to explicit physical/subinterface vrf_name updates by fencing new dependencies during a VRF rehome, waiting for local quiescence (prefixes + refcount drain), and then replacing the RIF in the requested VRF. It also updates Route/Neighbor programming paths to honor the new “guarded” RIF lookup and adds mock tests covering the VRF-rehome retry/rollback behavior and attribute preservation.
Changes:
- Add VRF-rehome fencing via
m_pendingVrfUpdatesand a newgetRouterIntfsIdForNewDependency()API to block new dependencies while removal/rehome is pending. - Update
RouteOrch/NeighOrchto treat dependency creation as retryable when the guarded RIF lookup returnsSAI_NULL_OBJECT_ID. - Add focused
IntfsOrchmock tests for VRF rehome drain/retry, partial updates, attribute preservation, and rollback on create failure.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/mock_tests/intfsorch_ut.cpp | Adds mock coverage for VRF rehome retry/drain, attribute preservation, partial updates, and rollback behavior. |
| orchagent/routeorch.cpp | Makes route/NHG programming retry when neighbor NH creation is deferred and uses guarded RIF lookup for interface NHs. |
| orchagent/neighorch.cpp | Uses guarded RIF lookup for new neighbor/next-hop creation to defer work during interface remove/rehome fences. |
| orchagent/intfsorch.h | Adds loopback_action to IntfsEntry, declares guarded RIF lookup, and extends setIntf() with an “explicit VRF” flag. |
| orchagent/intfsorch.cpp | Implements pending VRF-update fencing, VRF-rehome RIF replacement logic, and explicit-field guarding for MPLS/MAC updates. |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
|
/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 no new comments.
Comments suppressed due to low confidence (3)
orchagent/neighorch.cpp:386
- In addNextHop(), after potentially rewriting a remote-system-port next hop to the inband alias (via
nexthop.alias = inbp.m_alias), the new pending-removal/VRF-update fence still checksnh.aliasand also looks up the RIF usingnh.alias. This can fetch/fence the wrong interface (and will be wrong for remote-system-port next hops, which should operate on the rewritten inband alias).
if (!m_intfsOrch->isRemoteSystemPortIntf(nh.alias) &&
(m_intfsOrch->isIntfRemovalPending(nh.alias) ||
m_intfsOrch->isIntfVrfUpdatePending(nh.alias)))
{
SWSS_LOG_INFO("Interface %s is pending removal or VRF update", nh.alias.c_str());
orchagent/intfsorch.cpp:1173
- If
setIntfLoopbackAction()fails (including retryable SAI failures viahandleSaiSetStatus()), the code currently consumes the APP_DB entry anyway, which can permanently drop the intended loopback-action update. Please keep the entry queued for retry when the set fails (consistent with the MAC update retry handling below).
/* Set loopback action */
if (!loopbackAction.empty())
{
if (setIntfLoopbackAction(port, loopbackAction))
{
tests/mock_tests/intfsorch_ut.cpp:84
- The test fixture resets several globals in SetUp(), but
last_loopback_actionis not reset. Since tests updatelast_loopback_actionbased on the last RIF create attributes, leaving it unchanged can make assertions order-dependent if another test observes the previous value.
create_rif_count = 0;
remove_rif_count = 0;
saw_loopback_action = false;
fail_next_rif_create = false;
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6b568a1-5d2a-4309-b1ac-ef21ca155079 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 no new comments.
Comments suppressed due to low confidence (3)
orchagent/neighorch.cpp:385
- In NeighOrch::addNextHop(), the interface pending-removal/VRF-update fence and the subsequent RIF lookup are done using nh.alias (the original kernel alias). For remote system-port next hops, nexthop.alias is rewritten to the inband interface, so using nh.alias here can select the wrong RIF and bypass the intended inband-interface fencing. Use nexthop.alias consistently for the fence and getRouterIntfsId() after the potential rewrite, and log the same alias you actually gate on.
assert(!hasNextHop(nexthop));
if (!m_intfsOrch->isRemoteSystemPortIntf(nh.alias) &&
(m_intfsOrch->isIntfRemovalPending(nh.alias) ||
m_intfsOrch->isIntfVrfUpdatePending(nh.alias)))
{
orchagent/intfsorch.cpp:1173
- Loopback-action updates are still consumed even if setIntfLoopbackAction() fails (including retryable SAI failures). Since this is a SET path, returning false here should keep the entry in m_toSync for retry; otherwise the desired loopback_action can be lost permanently while m_syncdIntfses may remain stale.
/* Set loopback action */
if (!loopbackAction.empty())
{
if (setIntfLoopbackAction(port, loopbackAction))
{
tests/mock_tests/intfsorch_ut.cpp:84
- The test fixture resets saw_loopback_action and other globals in SetUp(), but last_loopback_action is not reset. This can make assertions depend on test execution order if a prior test set a different loopback action. Reset last_loopback_action in SetUp() to keep tests hermetic.
sai_router_intfs_api->create_router_interface = _ut_create_router_interface;
sai_router_intfs_api->remove_router_interface = _ut_remove_router_interface;
create_rif_count = 0;
remove_rif_count = 0;
saw_loopback_action = false;
fail_next_rif_create = false;
|
This PR has backport request label(s) for branch(es): 202605, but is missing required test information. Please make sure you tick the tested branch(es) in the Tested branch section and provide test evidence (e.g., 202605: <test result>) in the Test result section as well in your PR description. ---Powered by SONiC BuildBot
|
|
/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). |
Treat an explicit all-zero MAC as a request to use the switch source MAC while leaving MAC-absent partial SETs unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6b568a1-5d2a-4309-b1ac-ef21ca155079 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 no new comments.
Comments suppressed due to low confidence (2)
orchagent/intfsorch.cpp:1173
- Loopback-action updates are still consumed when
setIntfLoopbackAction()indicates a retryable SAI failure (it returnsfalseontask_need_retry). This can permanently drop the desired loopback action and also leavesm_syncdIntfses[alias].loopback_actionstale, which can break VRF-rehome state preservation. Please keep the APP_DB entry queued for retry on retryable failures, while still ignoring unsupported values.
/* Set loopback action */
if (!loopbackAction.empty())
{
if (setIntfLoopbackAction(port, loopbackAction))
{
tests/mock_tests/intfsorch_ut.cpp:84
- Test fixture state is not fully reset in
SetUp():last_loopback_actionis a global and can carry over from previous tests, making assertions order-dependent. Reset it alongside the other globals to keep tests hermetic.
create_rif_count = 0;
remove_rif_count = 0;
saw_loopback_action = false;
fail_next_rif_create = false;
|
Retrying CI once: both amd64 compile jobs in build 1173110 were canceled by the 60-minute agent limit after extended container startup; no code failure was reported. |
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
How to read this two-PR fix
Start here for the externally visible failure: an explicit VRF-changing interface
SETcould be consumed while old prefixes still existed, so the RIF and its IP2Me routes remained in the old VRF.Then read PR #4746 for the prerequisite: retaining the
SETcannot complete reliably if RouteOrch and NeighOrch can keep attaching new dependencies to the old RIF during the drain.The required implementation and merge order remains PR #4746 first, then this PR, because this PR extends its explicit addition-side removal checks with a separate pending-VRF predicate.
Until #4746 merges, GitHub's Files changed tab includes that prerequisite commit. This PR's own changes implement the rehome state machine in IntfsOrch and extend the standard local route, neighbor, and next-hop admission checks to the pending-VRF state.
What I did
This PR fixes the lost VRF-transition request by making an explicit
vrf_nameupdate use the same retain-and-retry model that whole-interface deletion already uses.Observed failure
The DHCP relay test configured
Vlan1000inVrf01, but hardware tracing showed:The returning DHCP OFFER was addressed to the relay interface in
Vrf01. Because the corresponding IP2Me trap route existed in the default virtual router instead, the packet did not reach the relay process.Root cause 1: a VRF-changing
SETwas consumed instead of retriedintfmgrdintends a VRF move to occur as a delete/recreate sequence:However, its Linux-side checks do not acknowledge orchagent or ASIC completion.
getIntfIpCount() == 0means only that Linux no longer has addresses; APP_DB prefix deletions may still be waiting for IntfsOrch.There are two additional ordering effects:
ProducerStateTable/ConsumerStateTable communicates the latest state of a key. If interface
DELand replacement interfaceSEToccur before the consumer pops that key, the intermediate interfaceDELcan be represented to orchagent as only:The interface row and prefix rows are different APP_DB keys. When they overlap in IntfsOrch's pending multimap, the bare key sorts first:
The trace confirmed that the explicit interface
SETwas processed before the final IPv6 prefixDEL.At that point the old RIF still had prefixes and could not safely be replaced. The critical bug was an asymmetry between
DELandSET:DELfalse; keep the command queued and retrySETtrue; erase the command without moving the RIFAfter the later prefix deletions drained, the explicit VRF request was gone. Nothing remained in
m_toSyncto retry the move, so the RIF and later IP2Me routes stayed in the old virtual router.This is the central root cause:
Root cause 2: the old RIF could refill while the move waited
Preserving the VRF
SETis necessary but not sufficient. The RIF cannot be removed while routes, neighbors, or next hops still reference it.The hardware trace observed new references admitted after the VRF transition had already been seen:
Without a transition fence, the rehome could repeatedly wait for
ref_count == 0while other orchagents continued attaching work to the old RIF.PR #4746 introduces an explicit removal-state predicate checked only by addition paths. This PR adds a separate pending-VRF predicate and makes those same addition paths check both states.
Fix
The fix turns VRF rehome into an explicit per-interface state machine:
1. Distinguish explicit VRF intent
APP_DB
SETis a partial update. These are not equivalent:Without remembering whether
vrf_namewas actually present, a later MTU or admin-only update could be mistaken for a request to move the interface back to the default VRF.The consumer therefore records
vrfNameIsExplicitand enters the rehome path only when:2. Retain the unresolved VRF
SETWhen prefixes or references remain:
m_pendingVrfUpdatesis only the transition marker; it does not duplicate the requested VRF. The retained APP_DB tuple remains authoritative.3. Drain old work and retain new work
During the pending transition:
false.isIntfVrfUpdatePending()and returnfalsebefore obtaining the old RIF ID.For the processing order discussed during review:
the effective execution becomes:
The multimap may visit
SET → DEL → ADD, but dependency checks produce the required semantic order:4. Recreate safely
The replacement preserves the existing interface state used to create the RIF, including:
IntfsEntrystate.Simultaneous subinterface MTU/admin changes are applied during the rehome. A later partial
SETwithoutvrf_namedoes not trigger another move.Logical VRF/refcount state changes only after the replacement RIF is created successfully. If creation fails:
If SAG is enabled, the task remains pending rather than attempting to migrate shared SAG link-local accounting as part of this targeted fix.
Why I did it
Why not require APP_DB to deliver
DEL → SET → ADD?APP_DB StateTable is a latest desired-state synchronization mechanism, not a durable transactional event log. Preserving one producer's call order is insufficient because:
A FIFO or priority queue inside IntfsOrch would address only one table and could cause head-of-line blocking. It would not order IntfsOrch against RouteOrch or NeighOrch.
A producer-side barrier or Redis transaction would also be insufficient by itself. Atomic publication does not guarantee ordered execution across consumers, retries, dependencies, or restarts.
The fully general solution would require an interface generation or transaction protocol, for example:
with every dependent route and neighbor carrying the same generation. That would allow orchagent to classify an addition intended for a future interface even if it arrived before the transition signal. Such a design requires coordinated schema and behavior changes across
intfmgrd, IntfsOrch, RouteOrch, NeighOrch, producers, and recovery logic.This PR instead follows the existing SWSS model:
Boundary of this fix
Once the interface transition is visible:
Before any transition signal is visible, an operation naming
Vlan1000must be interpreted against the currently active old interface. Without generation metadata, orchagent cannot know that an early addition belongs to a futureVlan1000.That means a true
ADD(new) → SET transition → DEL(old)inversion cannot be classified perfectly. The old dependency will safely block the move until it receives a deletion; the code does not remove a still-referenced RIF.The traced hardware failure is within the solved boundary: the explicit VRF
SEThad already been processed, but the old code did not retain it or establish a transition fence, so 228 later additions were admitted to the old RIF.This PR also does not reconstruct a prefix
DELthat ProducerStateTable may have coalesced with a same-key replacement prefixSETbefore either operation reached IntfsOrch. A priority queue cannot restore an operation that is no longer present.How I verified it
Focused mock coverage verifies:
SETremains queued across prefix and reference drain;DELfollowed by replacementSETis applied to the new RIF;vrf_namepreserve the current VRF;The earlier combined branches containing this implementation completed the amd64, ARM, ASAN, Docker, and Trixie compile stages in Azure builds
1168368(master) and1168367(202605). Theirvstestjobs stopped before executing tests because those workers lackedpip3.An earlier combined candidate, hardware build
1167872, passed traced and production 1x/5x runs and all ten production 10x test bodies. This smaller stacked PR adds state-preservation and rollback hardening after that run, so exact-commit validation is provided by this PR's CI.Current exact-head master PR build
ae4666cadab9015ca2533f5f7ce2c2ef380fd4901171071is superseded because its retry could not merge current master after PR [intfsorch]Fix race condition between VRF bind and ip address removal #4758 landed.202605 PR-build validation
202605stack includes merged PR [intfsorch]Fix race condition between VRF bind and ip address removal #4758 and all six user PRs in final dependency order: [intfsorch]: Fence new RIF dependencies during interface removal #4746, [intfsorch]: Retry loopback action update failures #4766, [neighorch]: Track remote next hops on inband RIF #4769, [neighorch]: Avoid inserting missing bulk neighbors #4772, this PR, then [neighorch]: Retry until inband port is available #4770.d4b0502ecdac76d56fdf063a633045c0fb0de977Tested for 202605 branchlabel must remain absent until that full-stack result is complete.Details if related