bgpd: fix batch clearing resume to use correct lookup APIs - #20738
Conversation
The batch clearing code was using bgp_node_get() to locate the outer/inner route node when resuming a RIB walk. This caused two problems: 1. Double-walking: bgp_node_get() returns the same node that was already processed in the previous iteration. During GR processing, the second visit to an already-stale path takes the else branch in clearing_clear_one_pi() and deletes the route from the RIB, causing traffic loss. Example: with 10 routes walked 3 at a time, routes 1-3 are marked stale in the first batch. On resume, bgp_node_get(table, 3) returns route 3 again. Since BGP_PATH_STALE is already set, the GR check fails and bgp_rib_remove() is called instead. 2. Phantom node creation: bgp_node_get() has find-or-create semantics, so it creates empty route nodes for prefixes that were deleted between yields. Fix this by using the appropriate lookup APIs for each case: - For the outer/RD table on resume: use bgp_node_lookup() to check if the last RD still exists. If it does, resume from that RD. If it was deleted, fall back to bgp_table_get_next() to advance to the next RD and clear the inner resume state so the new RD's table is walked from the beginning. - For inner/normal prefix resume: use bgp_table_get_next() to find the in-order successor of the last processed prefix, ensuring we never revisit it. Ticket: RM#4866286 Signed-off-by: Mitesh Kanjariya <mkanjariya@nvidia.com>
Greptile OverviewGreptile SummaryThis change updates the batch-clearing βresumeβ behavior in Net effect: prevents double-walking that could remove stale GR paths, and avoids creating empty βphantomβ nodes during resume. Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant S as clear_dests_callback()
participant H as clear_batch_rib_helper()
participant D as clearing_dest_helper()
participant T as bgp_table_* / route_table
S->>H: resume batch clearing (cinfo)
H->>D: find starting dest (table,cinfo,inner_p)
alt RESUME flag set
D->>T: bgp_table_top(table)
alt outer/RD table (VPN) && saved RD exists
D->>T: bgp_node_lookup(table,last_pfx)
note right of D: resumes at same RD
else outer/RD table && saved RD deleted
D->>T: bgp_table_get_next(table,last_pfx)
D->>D: clear INNER + RESUME flags
note right of D: start new RD inner table from top
else normal/inner prefix resume
D->>T: bgp_table_get_next(table,saved_pfx)
note right of D: successor avoids revisiting last prefix
end
else no resume
D->>T: bgp_table_top(table)
end
D-->>H: dest to continue walk
H-->>S: returns 0 when complete / nonzero to reschedule
|
8b7c989 to
c37b532
Compare
mjstapp
left a comment
There was a problem hiding this comment.
thanks, this looks good.
I wonder if there's still a latent bug during the table-walking: when we break an iteration, we don't unlock the node we are visiting. I'll look into that path.
|
I've put the 10.6 label on this fix. |
|
@Mergifyio backport dev/10.6 |
β Backports have been createdDetails
|
bgpd: fix batch clearing resume to use correct lookup APIs (backport #20738)
The batch clearing code was using bgp_node_get() to locate the outer/inner route node when resuming a RIB walk. This caused two problems:
Double-walking: bgp_node_get() returns the same node that was already processed in the previous iteration. During GR processing, the second visit to an already-stale path takes the else branch in clearing_clear_one_pi() and deletes the route from the RIB, causing traffic loss.
Example: with 10 routes walked 3 at a time, routes 1-3 are marked stale in the first batch. On resume, bgp_node_get(table, 3) returns route 3 again. Since BGP_PATH_STALE is already set, the GR check fails and bgp_rib_remove() is called instead.
Phantom node creation: bgp_node_get() has find-or-create semantics, so it creates empty route nodes for prefixes that were deleted between yields.
Fix this by using the appropriate lookup APIs for each case:
For the outer/RD table on resume: use bgp_node_lookup() to check if the last RD still exists. If it does, resume from that RD. If it was deleted, fall back to bgp_table_get_next() to advance to the next RD and clear the inner resume state so the new RD's table is walked from the beginning.
For inner/normal prefix resume: use bgp_table_get_next() to find the in-order successor of the last processed prefix, ensuring we never revisit it.