mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-05 04:06:35 +02:00
update with Josh's changes
This commit is contained in:
parent
c5cb642376
commit
9fd01334cf
@ -1,11 +1,23 @@
|
||||
package uring
|
||||
|
||||
import "runtime"
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"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"
|
||||
return runtime.GOOS == "linux" && *useIOURing
|
||||
}
|
||||
|
||||
var useIOURing = flag.Bool("use-io-uring", true, "attempt to use io_uring if available")
|
||||
|
||||
// NotSupportedError indicates an operation was attempted when io_uring is not supported.
|
||||
var NotSupportedError = errors.New("io_uring not supported")
|
||||
|
||||
// DisabledError indicates that io_uring was explicitly disabled.
|
||||
var DisabledError = errors.New("io_uring disabled")
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package uring
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@ -31,3 +32,24 @@ func TestFileRead(t *testing.T) {
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(buf[:n], qt.DeepEquals, want)
|
||||
}
|
||||
|
||||
func TestFileWrite(t *testing.T) {
|
||||
if !Available() {
|
||||
t.Skip("io_uring not available")
|
||||
}
|
||||
c := qt.New(t)
|
||||
tmpFile, err := ioutil.TempFile(".", "uring-test")
|
||||
c.Assert(err, qt.IsNil)
|
||||
t.Cleanup(func() {
|
||||
os.Remove(tmpFile.Name())
|
||||
})
|
||||
f, err := newFile(tmpFile)
|
||||
c.Assert(err, qt.IsNil)
|
||||
content := []byte("a test string to check writing works 😀 with non-unicode input")
|
||||
n, err := f.Write(content)
|
||||
if n != len(content) {
|
||||
t.Errorf("mismatch between written len and content len: want %d, got %d", len(content), n)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(f.Close(), qt.IsNil)
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ static int initialize(struct io_uring *ring, int fd) {
|
||||
|
||||
struct req {
|
||||
struct msghdr hdr;
|
||||
struct iovec iov;
|
||||
struct iovec iov;
|
||||
struct sockaddr_in sa;
|
||||
struct sockaddr_in6 sa6;
|
||||
// in_kernel indicates (by being non-zero) whether this request is sitting in the kernel
|
||||
@ -84,6 +84,9 @@ static void freeReq(struct req *r) {
|
||||
// TODO: What recvfrom support arrives, maybe use that instead?
|
||||
static int submit_recvmsg_request(struct io_uring *ring, struct req *r, size_t idx) {
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
|
||||
if (!sqe) {
|
||||
return -1;
|
||||
}
|
||||
io_uring_prep_recvmsg(sqe, 0, &r->hdr, 0); // use the 0th file in the list of registered fds
|
||||
io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
|
||||
io_uring_sqe_set_data(sqe, (void *)(idx));
|
||||
@ -105,7 +108,7 @@ static int submit_sendmsg_request(struct io_uring *ring, struct req *r, int bufl
|
||||
|
||||
static void submit_nop_request(struct io_uring *ring) {
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
|
||||
io_uring_prep_nop(sqe);
|
||||
io_uring_prep_nop(sqe);
|
||||
io_uring_sqe_set_data(sqe, (void *)(-1));
|
||||
io_uring_submit(ring);
|
||||
}
|
||||
@ -167,7 +170,7 @@ static go_completion_result completion(struct io_uring *ring, int block) {
|
||||
}
|
||||
|
||||
static int set_deadline(struct io_uring *ring, int64_t sec, long long ns) {
|
||||
// TODO where to put this timeout so that it lives beyond the scope of this call?
|
||||
// TODO where to put this timespec so that it lives beyond the scope of this call?
|
||||
struct __kernel_timespec ts = { sec, ns };
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
|
||||
// TODO should these be through function calls?
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
package uring
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
)
|
||||
|
||||
var useIOURing = flag.Bool("use-io-uring", true, "attempt to use io_uring if available")
|
||||
|
||||
// NotSupportedError indicates an operation was attempted when io_uring is not supported.
|
||||
var NotSupportedError = errors.New("io_uring not supported")
|
||||
|
||||
// DisabledError indicates that io_uring was explicitly disabled.
|
||||
var DisabledError = errors.New("io_uring disabled")
|
||||
@ -78,7 +78,7 @@ func NewUDPConn(pconn net.PacketConn) (*UDPConn, error) {
|
||||
conn, ok := pconn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot use io_uring with conn of type %T", pconn)
|
||||
}
|
||||
}
|
||||
// this is dumb
|
||||
local := conn.LocalAddr()
|
||||
var ipp netaddr.IPPort
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
package uring
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"inet.af/netaddr"
|
||||
)
|
||||
|
||||
func URingAvailable() bool { return false }
|
||||
|
||||
// A UDPConn is a recv-only UDP fd manager.
|
||||
// TODO: Support writes.
|
||||
// TODO: support multiplexing multiple fds?
|
||||
// May be more expensive than having multiple urings, and certainly more complicated.
|
||||
// TODO: API review for performance.
|
||||
// We'd like to enqueue a bunch of recv calls and deqeueue them later,
|
||||
// but we have a problem with buffer management: We get our buffers just-in-time
|
||||
// from wireguard-go, which means we have to make copies.
|
||||
// That's OK for now, but later it could be a performance issue.
|
||||
// For now, keep it simple and enqueue/dequeue in a single step.
|
||||
// TODO: IPv6
|
||||
// TODO: Maybe combine the urings into a single uring with dispatch.
|
||||
type UDPConn struct{}
|
||||
|
||||
func NewUDPConn(conn *net.UDPConn) (*UDPConn, error) { return nil, NotSupportedError }
|
||||
func (c *UDPConn) LocalAddr() net.Addr { panic("Not supported") }
|
||||
|
||||
func (u *UDPConn) Close() error { return NotSupportedError }
|
||||
func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
err = NotSupportedError
|
||||
return
|
||||
}
|
||||
func (u *UDPConn) ReadFromNetaddr(buf []byte) (n int, addr netaddr.IPPort, err error) {
|
||||
err = NotSupportedError
|
||||
return
|
||||
}
|
||||
func (u *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { return 0, NotSupportedError }
|
||||
func (c *UDPConn) SetDeadline(t time.Time) error { return NotSupportedError }
|
||||
func (c *UDPConn) SetReadDeadline(t time.Time) error { return NotSupportedError }
|
||||
func (c *UDPConn) SetWriteDeadline(t time.Time) error { return NotSupportedError }
|
||||
|
||||
// A File is a write-only file fd manager.
|
||||
// TODO: Support reads
|
||||
// TODO: all the todos from UDPConn
|
||||
type File struct{}
|
||||
|
||||
func NewFile(file *os.File) (*File, error) { return nil, NotSupportedError }
|
||||
func (u *File) Write(buf []byte) (int, error) { return 0, NotSupportedError }
|
||||
|
||||
// Read data into buf[offset:].
|
||||
// We are allowed to write junk into buf[offset-4:offset].
|
||||
func (u *File) Read(buf []byte) (n int, err error) { return 0, NotSupportedError }
|
||||
@ -1,117 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package uring
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const TestPort = 3636
|
||||
|
||||
var serverAddr = &net.UDPAddr{
|
||||
Port: TestPort,
|
||||
}
|
||||
|
||||
func NewUDPTestServer(t *testing.T) error {
|
||||
conn, err := net.ListenUDP("udp", serverAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
buf := make([]byte, 0, 512)
|
||||
n, _, err := conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
t.Errorf("failed to read on server: %v", err)
|
||||
break
|
||||
}
|
||||
t.Logf("%s, %v, %v", buf, n, err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewUDPIOURingConnTestServer(t *testing.T) error {
|
||||
conn, err := net.ListenUDP("udp", serverAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
buf := make([]byte, 0, 512)
|
||||
n, _, err := conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
t.Errorf("failed to read on server: %v", err)
|
||||
break
|
||||
}
|
||||
t.Logf("%s, %v, %v", buf, n, err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUDPConn(t *testing.T) {
|
||||
err := NewUDPTestServer(t)
|
||||
if err != nil {
|
||||
t.Errorf("failed to start UDPServer: %v", err)
|
||||
}
|
||||
udpConn, err := net.DialUDP("udp", nil, serverAddr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to start udp connection to server: %v", err)
|
||||
}
|
||||
defer udpConn.Close()
|
||||
|
||||
conn, err := NewUDPConn(udpConn)
|
||||
if err != nil {
|
||||
t.Errorf("failed to start io_uring udp connection: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
content := []byte("a test string to check udpconn works 😀 with non-unicode input")
|
||||
n, err := conn.WriteTo(content, serverAddr)
|
||||
if err != nil {
|
||||
t.Errorf("conn write failed: %v", err)
|
||||
}
|
||||
if n != len(content) {
|
||||
t.Errorf("written len mismatch: want %v, got %v", len(content), n)
|
||||
}
|
||||
|
||||
// Test many writes at once
|
||||
for i := 0; i < 1000; i++ {
|
||||
n, err := conn.WriteTo(content, serverAddr)
|
||||
if err != nil {
|
||||
t.Errorf("conn write failed: %v", err)
|
||||
}
|
||||
if n != len(content) {
|
||||
t.Errorf("written len mismatch: want %v, got %v", len(content), n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFile(t *testing.T) {
|
||||
tmpFile, err := ioutil.TempFile(".", "uring-test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp file: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
os.Remove(tmpFile.Name())
|
||||
})
|
||||
f, err := NewFile(tmpFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create io_uring file: %v", err)
|
||||
}
|
||||
content := []byte("a test string to check writing works 😀 with non-unicode input")
|
||||
n, err := f.Write(content)
|
||||
if n != len(content) {
|
||||
t.Errorf("mismatch between written len and content len: want %d, got %d", len(content), n)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("file write failed: %v", err)
|
||||
}
|
||||
if err = f.Close(); err != nil {
|
||||
t.Errorf("file close failed: %v", err)
|
||||
}
|
||||
}
|
||||
@ -43,3 +43,62 @@ func TestUDPSendRecv(t *testing.T) {
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(recvBuf[:n], qt.DeepEquals, sendBuf)
|
||||
}
|
||||
|
||||
// TODO(jknodt): maybe delete the test below because it's redundant
|
||||
|
||||
const TestPort = 3636
|
||||
|
||||
var serverAddr = &net.UDPAddr{
|
||||
Port: TestPort,
|
||||
}
|
||||
|
||||
func NewUDPTestServer(t *testing.T) error {
|
||||
conn, err := net.ListenUDP("udp", serverAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
buf := make([]byte, 512)
|
||||
_, _, err := conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
t.Errorf("failed to read on server: %v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUDPConn(t *testing.T) {
|
||||
if !Available() {
|
||||
t.Skip("io_uring not available")
|
||||
}
|
||||
c := qt.New(t)
|
||||
// TODO add a closer here
|
||||
err := NewUDPTestServer(t)
|
||||
c.Assert(err, qt.IsNil)
|
||||
udpConn, err := net.DialUDP("udp", nil, serverAddr)
|
||||
c.Assert(err, qt.IsNil)
|
||||
defer udpConn.Close()
|
||||
|
||||
conn, err := NewUDPConn(udpConn)
|
||||
c.Assert(err, qt.IsNil)
|
||||
defer conn.Close()
|
||||
|
||||
content := []byte("a test string to check udpconn works 😀 with non-unicode input")
|
||||
n, err := conn.WriteTo(content, serverAddr)
|
||||
c.Assert(err, qt.IsNil)
|
||||
if n != len(content) {
|
||||
t.Errorf("written len mismatch: want %v, got %v", len(content), n)
|
||||
}
|
||||
|
||||
// Test many writes at once
|
||||
for i := 0; i < 1000; i++ {
|
||||
n, err := conn.WriteTo(content, serverAddr)
|
||||
c.Assert(err, qt.IsNil)
|
||||
if n != len(content) {
|
||||
t.Errorf("written len mismatch: want %v, got %v", len(content), n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user