mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-18 12:32:13 +00:00
health, wgengine/magicsock: remove last of health package globals
Fixes #11874 Updates #4136 Change-Id: Ib70e6831d4c19c32509fe3d7eee4aa0e9f233564 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
71e9258ad9
commit
7f587d0321
@ -30,11 +30,39 @@ var (
|
|||||||
debugHandler map[string]http.Handler
|
debugHandler map[string]http.Handler
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ReceiveFunc is one of the three magicsock Receive funcs (IPv4, IPv6, or
|
||||||
|
// DERP).
|
||||||
|
type ReceiveFunc int
|
||||||
|
|
||||||
|
// ReceiveFunc indices for Tracker.MagicSockReceiveFuncs.
|
||||||
|
const (
|
||||||
|
ReceiveIPv4 ReceiveFunc = 0
|
||||||
|
ReceiveIPv6 ReceiveFunc = 1
|
||||||
|
ReceiveDERP ReceiveFunc = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f ReceiveFunc) String() string {
|
||||||
|
if f < 0 || int(f) >= len(receiveNames) {
|
||||||
|
return fmt.Sprintf("ReceiveFunc(%d)", f)
|
||||||
|
}
|
||||||
|
return receiveNames[f]
|
||||||
|
}
|
||||||
|
|
||||||
|
var receiveNames = []string{
|
||||||
|
ReceiveIPv4: "ReceiveIPv4",
|
||||||
|
ReceiveIPv6: "ReceiveIPv6",
|
||||||
|
ReceiveDERP: "ReceiveDERP",
|
||||||
|
}
|
||||||
|
|
||||||
// Tracker tracks the health of various Tailscale subsystems,
|
// Tracker tracks the health of various Tailscale subsystems,
|
||||||
// comparing each subsystems' state with each other to make sure
|
// comparing each subsystems' state with each other to make sure
|
||||||
// they're consistent based on the user's intended state.
|
// they're consistent based on the user's intended state.
|
||||||
type Tracker struct {
|
type Tracker struct {
|
||||||
// mu guards everything in this var block.
|
// MagicSockReceiveFuncs tracks the state of the three
|
||||||
|
// magicsock receive functions: IPv4, IPv6, and DERP.
|
||||||
|
MagicSockReceiveFuncs [3]ReceiveFuncStats // indexed by ReceiveFunc values
|
||||||
|
|
||||||
|
// mu guards everything that follows.
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
warnables []*Warnable // keys ever set
|
warnables []*Warnable // keys ever set
|
||||||
@ -524,7 +552,7 @@ func (t *Tracker) timerSelfCheck() {
|
|||||||
}
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
checkReceiveFuncs()
|
t.checkReceiveFuncsLocked()
|
||||||
t.selfCheckLocked()
|
t.selfCheckLocked()
|
||||||
if t.timer != nil {
|
if t.timer != nil {
|
||||||
t.timer.Reset(time.Minute)
|
t.timer.Reset(time.Minute)
|
||||||
@ -626,9 +654,10 @@ func (t *Tracker) overallErrorLocked() error {
|
|||||||
_ = t.lastMapRequestHeard
|
_ = t.lastMapRequestHeard
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
for _, recv := range receiveFuncs {
|
for i := range t.MagicSockReceiveFuncs {
|
||||||
if recv.missing {
|
f := &t.MagicSockReceiveFuncs[i]
|
||||||
errs = append(errs, fmt.Errorf("%s is not running", recv.name))
|
if f.missing {
|
||||||
|
errs = append(errs, fmt.Errorf("%s is not running", f.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for sys, err := range t.sysErr {
|
for sys, err := range t.sysErr {
|
||||||
@ -664,63 +693,69 @@ func (t *Tracker) overallErrorLocked() error {
|
|||||||
return multierr.New(errs...)
|
return multierr.New(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
ReceiveIPv4 = ReceiveFuncStats{name: "ReceiveIPv4"}
|
|
||||||
ReceiveIPv6 = ReceiveFuncStats{name: "ReceiveIPv6"}
|
|
||||||
ReceiveDERP = ReceiveFuncStats{name: "ReceiveDERP"}
|
|
||||||
|
|
||||||
receiveFuncs = []*ReceiveFuncStats{&ReceiveIPv4, &ReceiveIPv6, &ReceiveDERP}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if runtime.GOOS == "js" {
|
|
||||||
receiveFuncs = receiveFuncs[2:] // ignore IPv4 and IPv6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReceiveFuncStats tracks the calls made to a wireguard-go receive func.
|
// ReceiveFuncStats tracks the calls made to a wireguard-go receive func.
|
||||||
type ReceiveFuncStats struct {
|
type ReceiveFuncStats struct {
|
||||||
// name is the name of the receive func.
|
// name is the name of the receive func.
|
||||||
|
// It's lazily populated.
|
||||||
name string
|
name string
|
||||||
// numCalls is the number of times the receive func has ever been called.
|
// numCalls is the number of times the receive func has ever been called.
|
||||||
// It is required because it is possible for a receive func's wireguard-go goroutine
|
// It is required because it is possible for a receive func's wireguard-go goroutine
|
||||||
// to be active even though the receive func isn't.
|
// to be active even though the receive func isn't.
|
||||||
// The wireguard-go goroutine alternates between calling the receive func and
|
// The wireguard-go goroutine alternates between calling the receive func and
|
||||||
// processing what the func returned.
|
// processing what the func returned.
|
||||||
numCalls uint64 // accessed atomically
|
numCalls atomic.Uint64
|
||||||
// prevNumCalls is the value of numCalls last time the health check examined it.
|
// prevNumCalls is the value of numCalls last time the health check examined it.
|
||||||
prevNumCalls uint64
|
prevNumCalls uint64
|
||||||
// inCall indicates whether the receive func is currently running.
|
// inCall indicates whether the receive func is currently running.
|
||||||
inCall uint32 // bool, accessed atomically
|
inCall atomic.Bool
|
||||||
// missing indicates whether the receive func is not running.
|
// missing indicates whether the receive func is not running.
|
||||||
missing bool
|
missing bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReceiveFuncStats) Enter() {
|
func (s *ReceiveFuncStats) Enter() {
|
||||||
atomic.AddUint64(&s.numCalls, 1)
|
s.numCalls.Add(1)
|
||||||
atomic.StoreUint32(&s.inCall, 1)
|
s.inCall.Store(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReceiveFuncStats) Exit() {
|
func (s *ReceiveFuncStats) Exit() {
|
||||||
atomic.StoreUint32(&s.inCall, 0)
|
s.inCall.Store(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkReceiveFuncs() {
|
// ReceiveFuncStats returns the ReceiveFuncStats tracker for the given func
|
||||||
for _, recv := range receiveFuncs {
|
// type.
|
||||||
recv.missing = false
|
//
|
||||||
prev := recv.prevNumCalls
|
// If t is nil, it returns nil.
|
||||||
numCalls := atomic.LoadUint64(&recv.numCalls)
|
func (t *Tracker) ReceiveFuncStats(which ReceiveFunc) *ReceiveFuncStats {
|
||||||
recv.prevNumCalls = numCalls
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &t.MagicSockReceiveFuncs[which]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tracker) checkReceiveFuncsLocked() {
|
||||||
|
for i := range t.MagicSockReceiveFuncs {
|
||||||
|
f := &t.MagicSockReceiveFuncs[i]
|
||||||
|
if f.name == "" {
|
||||||
|
f.name = (ReceiveFunc(i)).String()
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "js" && i < 2 {
|
||||||
|
// Skip IPv4 and IPv6 on js.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f.missing = false
|
||||||
|
prev := f.prevNumCalls
|
||||||
|
numCalls := f.numCalls.Load()
|
||||||
|
f.prevNumCalls = numCalls
|
||||||
if numCalls > prev {
|
if numCalls > prev {
|
||||||
// OK: the function has gotten called since last we checked
|
// OK: the function has gotten called since last we checked
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if atomic.LoadUint32(&recv.inCall) == 1 {
|
if f.inCall.Load() {
|
||||||
// OK: the function is active, probably blocked due to inactivity
|
// OK: the function is active, probably blocked due to inactivity
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Not OK: The function is not active, and not accumulating new calls.
|
// Not OK: The function is not active, and not accumulating new calls.
|
||||||
// It is probably MIA.
|
// It is probably MIA.
|
||||||
recv.missing = true
|
f.missing = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -681,8 +681,10 @@ func (c *Conn) runDerpWriter(ctx context.Context, dc *derphttp.Client, ch <-chan
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *connBind) receiveDERP(buffs [][]byte, sizes []int, eps []conn.Endpoint) (int, error) {
|
func (c *connBind) receiveDERP(buffs [][]byte, sizes []int, eps []conn.Endpoint) (int, error) {
|
||||||
health.ReceiveDERP.Enter()
|
if s := c.Conn.health.ReceiveFuncStats(health.ReceiveDERP); s != nil {
|
||||||
defer health.ReceiveDERP.Exit()
|
s.Enter()
|
||||||
|
defer s.Exit()
|
||||||
|
}
|
||||||
|
|
||||||
for dm := range c.derpRecvCh {
|
for dm := range c.derpRecvCh {
|
||||||
if c.isClosed() {
|
if c.isClosed() {
|
||||||
|
@ -1203,12 +1203,12 @@ func (c *Conn) putReceiveBatch(batch *receiveBatch) {
|
|||||||
|
|
||||||
// receiveIPv4 creates an IPv4 ReceiveFunc reading from c.pconn4.
|
// receiveIPv4 creates an IPv4 ReceiveFunc reading from c.pconn4.
|
||||||
func (c *Conn) receiveIPv4() conn.ReceiveFunc {
|
func (c *Conn) receiveIPv4() conn.ReceiveFunc {
|
||||||
return c.mkReceiveFunc(&c.pconn4, &health.ReceiveIPv4, metricRecvDataIPv4)
|
return c.mkReceiveFunc(&c.pconn4, c.health.ReceiveFuncStats(health.ReceiveIPv4), metricRecvDataIPv4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// receiveIPv6 creates an IPv6 ReceiveFunc reading from c.pconn6.
|
// receiveIPv6 creates an IPv6 ReceiveFunc reading from c.pconn6.
|
||||||
func (c *Conn) receiveIPv6() conn.ReceiveFunc {
|
func (c *Conn) receiveIPv6() conn.ReceiveFunc {
|
||||||
return c.mkReceiveFunc(&c.pconn6, &health.ReceiveIPv6, metricRecvDataIPv6)
|
return c.mkReceiveFunc(&c.pconn6, c.health.ReceiveFuncStats(health.ReceiveIPv6), metricRecvDataIPv6)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mkReceiveFunc creates a ReceiveFunc reading from ruc.
|
// mkReceiveFunc creates a ReceiveFunc reading from ruc.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user