diff --git a/net/uring/all.go b/net/uring/all.go new file mode 100644 index 000000000..d2361e444 --- /dev/null +++ b/net/uring/all.go @@ -0,0 +1,11 @@ +package uring + +import "runtime" + +// This file contains code shared across all platforms. + +// Available reports whether io_uring is available on this machine. +// If Available reports false, no other package uring APIs should be called. +func Available() bool { + return runtime.GOOS == "linux" +} diff --git a/net/uring/stubs.go b/net/uring/stubs.go new file mode 100644 index 000000000..74a68906d --- /dev/null +++ b/net/uring/stubs.go @@ -0,0 +1,32 @@ +// +build !linux + +package uring + +import ( + "net" + "os" + "time" + + "inet.af/netaddr" +) + +// This file contains stubs for platforms that are known at compile time not to support io_uring. + +type UDPConn struct{} + +func NewUDPConn(*net.UDPConn) (*UDPConn, error) { panic("io_uring unavailable") } +func (u *UDPConn) ReadFromNetaddr([]byte) (int, netaddr.IPPort, error) { panic("io_uring unavailable") } +func (u *UDPConn) Close() error { panic("io_uring unavailable") } +func (c *UDPConn) ReadFrom([]byte) (int, net.Addr, error) { panic("io_uring unavailable") } +func (u *UDPConn) WriteTo([]byte, net.Addr) (int, error) { panic("io_uring unavailable") } +func (c *UDPConn) LocalAddr() net.Addr { panic("io_uring unavailable") } +func (c *UDPConn) SetDeadline(time.Time) error { panic("io_uring unavailable") } +func (c *UDPConn) SetReadDeadline(time.Time) error { panic("io_uring unavailable") } +func (c *UDPConn) SetWriteDeadline(time.Time) error { panic("io_uring unavailable") } + +type File struct{} + +func NewFile(file *os.File) (*File, error) { panic("io_uring unavailable") } +func (u *File) Read([]byte) (int, error) { panic("io_uring unavailable") } +func (u *File) Write([]byte) (int, error) { panic("io_uring unavailable") } +func (u *File) Close() error { panic("io_uring unavailable") } diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 2d7dcbf8c..3208d3175 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -2690,13 +2690,15 @@ func (c *Conn) bindSocket(rucPtr **RebindingUDPConn, network string, curPortFate continue } // Success. - uring, err := uring.NewUDPConn(pconn.(*net.UDPConn)) - if err != nil { - c.logf("uring not available: %v", err) - ruc.pconn = pconn - } else { - c.logf("using uring") - ruc.pconn = uring + ruc.pconn = pconn + if uring.Available() { + uringConn, err := uring.NewUDPConn(pconn.(*net.UDPConn)) + if err != nil { + c.logf("not using io_uring for %v: %v", pconn.LocalAddr(), err) + } else { + c.logf("using uring for %v", pconn.LocalAddr()) + ruc.pconn = uringConn + } } if network == "udp4" { health.SetUDP4Unbound(false) @@ -2853,10 +2855,11 @@ func (c *RebindingUDPConn) ReadFromNetaddr(b []byte) (n int, ipp netaddr.IPPort, for { pconn := c.currentConn() - // Optimization: Treat *net.UDPConn specially. - // ReadFromUDP gets partially inlined, avoiding allocating a *net.UDPAddr, + // Optimization: Treat a few conn types specially. + // *uring.UDPConn can return netaddr.IPPorts directly. + // For *net.UDPConn, ReadFromUDP gets partially inlined, avoiding allocating a *net.UDPAddr, // as long as pAddr itself doesn't escape. - // The non-*net.UDPConn case works, but it allocates. + // The default case works, but it allocates. var pAddr *net.UDPAddr switch pconn := pconn.(type) { case *uring.UDPConn: @@ -2925,16 +2928,7 @@ func (c *RebindingUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) { pconn := c.pconn c.mu.Unlock() - var n int - var err error - switch pconn := pconn.(type) { - case *net.UDPConn: - n, err = pconn.WriteTo(b, addr) - case *uring.UDPConn: - n, err = pconn.WriteTo(b, addr) - default: - n, err = pconn.WriteTo(b, addr) - } + n, err := pconn.WriteTo(b, addr) if err != nil { c.mu.Lock() pconn2 := c.pconn