Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/sync: liburing 2.3 #10

Draft
wants to merge 29 commits into
base: v0.4
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8d37b05
fix: change base type of UserData to uint64
ii64 Oct 13, 2022
527c11c
feat: sync liburing
ii64 Oct 13, 2022
1abd875
feat: seq_cst implementation
ii64 Oct 13, 2022
510f87a
chore: ignore code workspace
ii64 Oct 24, 2022
5a76c34
fix: use sigset from golang.org/x/sys/unix
ii64 Oct 25, 2022
6338cf3
ring: io_uring syscall nr for __ALPHA__
ii64 Oct 25, 2022
56c528f
ring: prepare sqe support
ii64 Oct 25, 2022
8dea91c
ring: big cqe support
ii64 Oct 25, 2022
78032ef
doc: update graph
ii64 Oct 25, 2022
f5548b6
chore: setup go work
ii64 Apr 15, 2023
bd50af5
fix: syscall nr
ii64 Apr 15, 2023
0e7d4fa
chore: sync liburing
ii64 Apr 15, 2023
ff91bfb
feat: essential ioctl and nvme package support
ii64 Apr 15, 2023
2958369
chore: basic usage example
ii64 Apr 15, 2023
c85becd
chore: cleanup debug
ii64 Apr 15, 2023
8f4508a
fix(nvme): constant folds for uring, ioctl cmd ops
ii64 Apr 16, 2023
d3eea9e
chore: adjust read-write, nvme example
ii64 Apr 16, 2023
56bbdd8
feat: added some prepare helper
ii64 Apr 16, 2023
805d8d1
chore: tcp echo example
ii64 Apr 17, 2023
f4992f9
feat: get probe ring
ii64 Apr 17, 2023
64d8c0e
chore: simple eventloop example
ii64 Apr 17, 2023
b97c7aa
chore: implement grace shutdown on simple eventloop
ii64 Apr 18, 2023
0ebe45c
test: ring concurrent enqeue
ii64 Apr 24, 2023
69433c4
chore: example simple-eventloop run client on bg
ii64 Apr 24, 2023
14a4d32
chore: example read-write intensive to memfd file
ii64 Apr 24, 2023
a826825
chore: go work add library
ii64 Apr 25, 2023
fe8ee1c
chore: sync liburing queue
ii64 Apr 25, 2023
cf008fa
chore: example read-write memfd verify bytes
ii64 Apr 25, 2023
c05ef4f
chore: example eventloop add debug
ii64 Apr 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.vscode/
*.cpu
*.mem
*.test
*.test
*.code-workspace
719 changes: 385 additions & 334 deletions assets/nonsqpoll.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,368 changes: 703 additions & 665 deletions assets/sqpoll.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions atomic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package gouring

import _ "unsafe"

var io_uring_smp_mb = io_uring_smp_mb_fallback

func io_uring_smp_mb_fallback()
func io_uring_smp_mb_mfence()

func init() {
// temporary
io_uring_smp_mb = io_uring_smp_mb_mfence
}
12 changes: 12 additions & 0 deletions atomic_amd64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "go_asm.h"
#include "funcdata.h"
#include "textflag.h"

TEXT ·io_uring_smp_mb_fallback(SB), NOSPLIT, $0
LOCK
ORQ $0, 0(SP)
RET

TEXT ·io_uring_smp_mb_mfence(SB), NOSPLIT, $0
MFENCE
RET
15 changes: 15 additions & 0 deletions examples/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/ii64/gouring/examples

go 1.20

replace github.com/ii64/gouring => ../

require (
github.com/ii64/gouring v0.0.0-00010101000000-000000000000
golang.org/x/sys v0.7.0
)

require (
github.com/alphadose/haxmap v1.2.0 // indirect
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect
)
10 changes: 10 additions & 0 deletions examples/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/alphadose/haxmap v1.2.0 h1:noGrAmCE+gNheZ4KpW+sYj9W5uMcO1UAjbAq9XBOAfM=
github.com/alphadose/haxmap v1.2.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
174 changes: 174 additions & 0 deletions examples/nvme/nvme.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package main

import (
"fmt"
"syscall"
"unsafe"

uring "github.com/ii64/gouring"
nvme "github.com/ii64/gouring/nvme"
"golang.org/x/sys/unix"
)

// NOTICE NOTICE NOTICE NOTICE NOTICE
//
// This example is performing **READ** access to NVMe via low-level control device.
//
// NOTICE NOTICE NOTICE NOTICE NOTICE

var (
// hardcoded device path
// devicePath = "/dev/nvme0n1"
devicePath = "/dev/ng0n1"

nsid uint32
lbaSize uint32
lbaShift int
BS uint64 = 8192
)

func DoNvmeGetInfo(devPath string) error {
fd, err := unix.Open(devPath, unix.O_RDONLY, 0)
if err != nil {
return err
}
defer func() {
if err := unix.Close(fd); err != nil {
panic(err)
}
}()

var (
ns nvme.NvmeIdNs
cmd nvme.NvmePassthruCmd
)

nsidRet, err := sys_ioctl(fd, uintptr(nvme.NVME_IOCTL_ID()), 0)
if err != nil {
return err
}
nsid = uint32(nsidRet)

cmd = nvme.NvmePassthruCmd{
Opcode: nvme.NVME_ADMIN_IDENTIFY,
Nsid: nsid,
Addr: uint64(uintptr(unsafe.Pointer(&ns))),
DataLen: nvme.NVME_IDENTIFY_DATA_SIZE,
Cdw10: nvme.NVME_IDENTIFY_CNS_NS,
Cdw11: nvme.NVME_CSI_NVM << nvme.NVME_IDENTIFY_CSI_SHIFT,
TimeoutMs: nvme.NVME_DEFAULT_IOCTL_TIMEOUT,
}
_, err = sys_ioctl(fd, uintptr(nvme.NVME_IOCTL_ADMIN_CMD()), uintptr(unsafe.Pointer(&cmd)))
if err != nil {
return err
}

lbaSize = 1 << ns.Lbaf[(ns.Flbas&0x0F)].Ds
lbaShift = ilog2(uint32(lbaSize))

return nil
}

func DoIoUring(devPath string) error {
ring, err := uring.New(64,
uring.IORING_SETUP_IOPOLL|
uring.IORING_SETUP_SQE128|uring.IORING_SETUP_CQE32)
if err != nil {
return err
}
defer ring.Close()

fd, err := unix.Open(devicePath, unix.O_RDONLY, 0) // 0 as it O_RDONLY
if err != nil {
panic(err)
}
defer unix.Close(fd)

var bufs [10][0x1000]byte
var sqe *uring.IoUringSqe
sqe = ring.GetSqe()

buf := bufs[1]
bufSz := len(buf)
uring.PrepRead(sqe, fd, &buf[0], bufSz, 0)

sqe.SetCmdOp(uint32(nvme.NVME_URING_CMD_IO()))
sqe.Opcode = uring.IORING_OP_URING_CMD

var off uint64 = 0
var i uint32 = 1
sqe.UserData.SetUint64(uint64(off<<32) | uint64(i)) // temp

var slba uint64 = off >> lbaShift
var nlb uint64 = BS>>lbaShift - 1
// zero and init
cmd := nvme.NvmeUringCmd{
Opcode: nvme.NVME_CMD_READ,

// cdw10 and cdw11 represent starting lba
Cdw10: uint32(slba & 0xffff_ffff),
Cdw11: uint32(slba >> 32),
// represent number of lba's for read/write
Cdw12: uint32(nlb),

Nsid: nsid,

Addr: uint64(uintptr(unsafe.Pointer(&buf[0]))),
DataLen: uint32(bufSz),
}
cmdPtr := (*nvme.NvmeUringCmd)(sqe.GetCmd())
*cmdPtr = cmd // copy

fmt.Printf("CMD %+#v\n", cmdPtr)

submitted, err := ring.SubmitAndWait(1)
if err != nil {
return err
}
fmt.Println("submitted", submitted)

var cqe *uring.IoUringCqe
// for i := 0; i < 2; i++ {
if err := ring.WaitCqe(&cqe); err != nil {
return err
}
fmt.Printf("CQE:\t%+#v\n", cqe)
cqeExtra := (*[2]uint64)(cqe.GetBigCqe())
fmt.Printf("CQE Extra:\t%+#v\n", cqeExtra)
fmt.Printf("Buffer: %+#v\n", buf)
fmt.Printf("=========\n")
ring.SeenCqe(cqe)
// }
return nil
}

func main() {
err := DoNvmeGetInfo(devicePath)
if err != nil {
panic(err)
}
fmt.Printf("lbaSize: %d lbaShift: %d\n", lbaSize, lbaShift)

if err := DoIoUring(devicePath); err != nil {
panic(err)
}

}

func sys_ioctl(fd int, a1, a2 uintptr) (int, error) {
r1, _, err := syscall.Syscall(syscall.SYS_IOCTL,
uintptr(fd), a1, a2)
if err != 0 {
return 0, err
}
return int(r1), nil
}

func ilog2(i uint32) int {
log := -1
for i > 0 {
i >>= 1
log++
}
return log
}
132 changes: 132 additions & 0 deletions examples/read-write-memfd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package main

import (
"bytes"
"fmt"
"reflect"
"runtime"
"syscall"
"time"
"unsafe"

uring "github.com/ii64/gouring"
"golang.org/x/sys/unix"
)

func main() {
ring, err := uring.New(64, 0)
if err != nil {
panic(err)
}
defer ring.Close()

fd, err := unix.MemfdCreate("mymemfd", unix.MFD_CLOEXEC)
if err != nil {
panic(err)
}
defer unix.Close(fd)

const BSIZE = 512
unix.Ftruncate(fd, BSIZE)

addr, err := mmap(nil, BSIZE, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_SHARED, fd, 0)
if err != nil {
panic(err)
}
defer munmap(addr, BSIZE)

var rbuf []byte
sh := (*reflect.SliceHeader)(unsafe.Pointer(&rbuf))
sh.Data = uintptr(addr)
sh.Cap = BSIZE
sh.Len = BSIZE

tnow := func() string { return fmt.Sprintf("CLOCK:%d\n", time.Now().UnixMilli()) }

go func() {
flen := len(tnow())
// monitor written bytes
for {
// copy
payload := string(rbuf[:flen])
fmt.Printf("> %q\n", payload)
time.Sleep(time.Millisecond * 50)
}
}()

var buf [BSIZE]byte
refresh := func() int {
b := []byte(tnow())
copy(buf[:], b)
return len(b)
}

qWrite := func() {
sqe := ring.GetSqe()
uring.PrepWrite(sqe, fd, &buf[0], refresh(), 0)
sqe.UserData.SetUint64(0xaaaaaaaa)
}
qRead := func() {
sqe := ring.GetSqe()
uring.PrepRead(sqe, fd, &buf[0], len(buf), 0)
sqe.UserData.SetUint64(0xbbbbbbbb)
}

qWrite()

submitted, err := ring.SubmitAndWait(1)
if err != nil {
panic(err)
}
println("submitted:", submitted)

var cqe *uring.IoUringCqe
for {
err = ring.WaitCqe(&cqe)
switch err {
case syscall.EINTR, syscall.EAGAIN, syscall.ETIME:
runtime.Gosched()
continue
case nil:
goto cont
default:
panic(err)
}
cont:
switch cqe.UserData {
case 0xaaaaaaaa:
qRead()
case 0xbbbbbbbb:
// verify
if !bytes.Equal(buf[:], rbuf) {
panic("check failed")
}
qWrite()
}

ring.SeenCqe(cqe)
submitted, err := ring.Submit()
if err != nil {
panic(err)
} else {
_ = submitted
// println("submitted:", submitted)
}
}

}

//go:linkname mmap syscall.mmap
func mmap(addr unsafe.Pointer, length uintptr, prot int, flags int, fd int, offset int64) (xaddr unsafe.Pointer, err error)

//go:linkname munmap syscall.munmap
func munmap(addr unsafe.Pointer, length uintptr) (err error)

func msync(addr unsafe.Pointer, length uintptr, flags uintptr) error {
r1, _, e1 := syscall.Syscall(syscall.SYS_MSYNC, uintptr(addr), length, flags)
if e1 != 0 {
return syscall.Errno(e1)
}
_ = r1
return nil
}
Loading