Skip to content

Commit 96bf8e8

Browse files
committed
netstack/seamless: on new endpoint, attach before swap
attach immediately kickstarts the endpoint's dispatch loop which helps avoid the heartbeating clients, waiting on the dispatch loop to exit, race ahead.
1 parent 8d37e81 commit 96bf8e8

File tree

2 files changed

+16
-20
lines changed

2 files changed

+16
-20
lines changed

intra/netstack/fdbased.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ func (e *endpoint) swap(fd int, force bool) (err error) {
310310
log.I("ns: tun(%s): (%s => %d) swap: restart looper %t for new fd",
311311
prevfd, prevfd, fd, hasDispatcher)
312312
go dispatchLoop(e.inboundDispatcher, f, &e.wg)
313-
} else {
313+
} else { // wait for Attach to be called eventually
314314
log.E("ns: tun(%s): (%s => %d) swap: no dispatcher? %t for new fd; err %v",
315315
prevfd, prevfd, fd, !hasDispatcher, err)
316316
}
@@ -552,7 +552,6 @@ func dispatchLoop(inbound linkDispatcher, f *fds, wg *sync.WaitGroup) tcpip.Erro
552552
defer f.stop()
553553
return err
554554
} // else: continue dispatching
555-
556555
}
557556
}
558557

intra/netstack/seamless.go

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ func (l *magiclink) Swap(fd, mtu int) (err error) {
155155
needsNewEndpoint = errors.Is(err, errNeedsNewEndpoint)
156156
}
157157

158-
if !needsNewEndpoint {
158+
if hasSwappedFd || !needsNewEndpoint {
159159
logei(!hasSwappedFd)("netstack: magic(%d); swap: ok? %t; err? %v",
160160
fd, hasSwappedFd, err)
161161
return err
@@ -173,21 +173,22 @@ func (l *magiclink) Swap(fd, mtu int) (err error) {
173173
return core.OneErr(err, errMissingEp)
174174
}
175175

176-
old := l.e.Swap(ep)
177-
176+
// attach eventually runs a dispatchLoop which kickstarts the endpoint's
177+
// delivery of packets to netstack's dispatcher.
178178
d := l.d.Load()
179179
if d == nil {
180180
ep.Attach(nil) // attach the new endpoint to the dispatcher
181181
} else {
182182
ep.Attach(l) // attach the new endpoint to the existing dispatcher
183183
}
184-
185-
// Close the old endpoint after the new one is attached to ensure
186-
// proper sequencing and avoid WaitGroup reuse issues
187-
if old != nil {
184+
185+
// swap endpoints after the dispatchLoop has had the chance to start
186+
// to avoid cases where clients end up calling ep.Wait() before dispatchLoop
187+
// could begin (as it is responsible for keeping ep alive)
188+
if old := l.e.Swap(ep); old != nil {
188189
core.Go("magic."+strconv.Itoa(fd), old.Close)
189190
}
190-
191+
191192
logei(d == nil)("netstack: magic(%d) mtu: %d; swap: new ep... dispatch? %t",
192193
fd, umtu, d != nil)
193194

@@ -313,16 +314,12 @@ func (l *magiclink) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error)
313314
}
314315

315316
func (l *magiclink) Wait() {
316-
// Atomically load the current endpoint to prevent race conditions
317-
// during endpoint swapping. If endpoint is swapped while we're
318-
// waiting, we should wait on the endpoint we loaded, not the new one.
319-
// This prevents WaitGroup reuse issues.
320-
e := l.e.Load()
321-
if e != nil {
322-
// Use a recovered call to prevent panics from propagating
323-
// in case of WaitGroup reuse issues
324-
defer core.Recover(core.Exit11, "magiclink.wait")
325-
e.Wait()
317+
if e := l.e.Load(); e != nil {
318+
if e.IsAttached() {
319+
e.Wait() // may panic in case of WaitGroup reuse issues
320+
} else {
321+
log.W("netstack: magic: wait; dispatcher not attached; has dispatcher? %t", l.d.Load() != nil)
322+
}
326323
}
327324
}
328325

0 commit comments

Comments
 (0)