vfs: rebuild the epoll ready list on restore instead of saving it
EpollInstance.ready, the per-interest link into it, and the epollInterest.ready flag that mirrors membership are all saved, but readyMu -- which protects all three -- is state:"nosave". state.Save walks the graph without taking it, and not every notifier is quiesced for the save: udp's Pause is a no-op, tcp's stops only the dispatcher, and netstack timers are not enumerated by pauseTimeLocked. A Notify landing inside the encode window is enough for the encoder to read epi.ready at one instant and the list links at another, saving an image in which epi.ready is false while epi is still linked into ep.ready. On restore, epollInterest.afterLoad calls NotifyEvent, whose `if !epi.ready` branch is taken for that interest even though its entry is already linked. PushBack begins with SetNext(nil), so every interest queued behind it is silently unlinked while keeping ready == true, and the `if !epi.ready` guard then skips them forever. The victim is not the torn interest, which ends up self-consistent, but whichever interests were queued behind it. Measured on release-20260803.0 with an instrumented runsc: the inconsistency appears in the restored image on failing restores only (3/3 failures, 0/9704 healthy samples), and the ready list observably collapses from 3 entries to 1 across that afterLoad while every healthy interest adds exactly one. Retrying the restore from the same image rescues nothing (0/7), since the tear is in the image and state.Load is deterministic. Stop saving the list, the link and the flag. afterLoad already calls NotifyEvent for every interest, so an empty list is rebuilt consistently and ReadEvents re-checks real readiness immediately after; the list is a cache of a conclusion that is recomputed on restore anyway. This mirrors the fix for the same class of bug in tcp's epQueue (#13554), resolved by rebuilding on restore rather than persisting a serialized intrusive list whose mutators are not fully quiesced at save time. This removes the consequence, not the race: state.Save still walks the graph without readyMu, and other serialized structures protected by nosave locks remain exposed to the same pattern.
N
NekoPunch committed
cc62901f69f38627ca3edcf6b33c69503ab35e64
Parent: 8c6bfc8