github.com/dshulyak/uring@v0.0.0-20210209113719-1b2ec51f1542/ring_test.go (about) 1 package uring 2 3 import ( 4 "io/ioutil" 5 "math/rand" 6 "os" 7 "syscall" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/require" 12 ) 13 14 func TestWritev(t *testing.T) { 15 f, err := ioutil.TempFile("", "writev-tests-") 16 require.NoError(t, err) 17 defer f.Close() 18 19 ring, err := Setup(4, nil) 20 require.NoError(t, err) 21 defer ring.Close() 22 23 var offset uint64 24 bufs := [4][8]byte{} 25 vectors := [4][]syscall.Iovec{} 26 27 for round := 0; round < 10; round++ { 28 for i := 0; i < 4; i++ { 29 buf := bufs[i] 30 _, _ = rand.Read(buf[:]) 31 bufs[i] = buf 32 vectors[i] = []syscall.Iovec{ 33 { 34 Base: &buf[0], 35 Len: uint64(len(buf)), 36 }, 37 } 38 sqe := ring.GetSQEntry() 39 Writev(sqe, f.Fd(), vectors[i], offset, 0) 40 offset += uint64(len(buf)) 41 } 42 43 _, err = ring.Submit(4) 44 require.NoError(t, err) 45 46 for i := 0; i < 4; i++ { 47 cqe, err := ring.GetCQEntry(0) 48 require.NoError(t, err) 49 require.True(t, cqe.Result() >= 0, "failed with %v", syscall.Errno(-cqe.Result())) 50 } 51 52 buf := [8]byte{} 53 for i := 0; i < 4; i++ { 54 n, err := f.Read(buf[:]) 55 require.NoError(t, err) 56 require.Equal(t, len(buf), n) 57 require.Equal(t, bufs[i], buf) 58 } 59 } 60 } 61 62 func TestReadv(t *testing.T) { 63 f, err := ioutil.TempFile("", "readv-tests-") 64 require.NoError(t, err) 65 defer f.Close() 66 67 ring, err := Setup(4, nil) 68 require.NoError(t, err) 69 defer ring.Close() 70 71 var offset uint64 72 const num = 3 73 bufs := [num][8]byte{} 74 vectors := [num][]syscall.Iovec{} 75 76 for round := 0; round < 10; round++ { 77 78 wbuf := [num * 8]byte{} 79 80 _, _ = rand.Read(wbuf[:]) 81 n, err := f.Write(wbuf[:]) 82 require.NoError(t, err) 83 require.Equal(t, len(wbuf), n) 84 85 for i := 0; i < num; i++ { 86 sqe := ring.GetSQEntry() 87 vectors[i] = []syscall.Iovec{ 88 { 89 Base: &bufs[i][0], 90 Len: uint64(len(bufs[i])), 91 }, 92 } 93 Readv(sqe, f.Fd(), vectors[i], offset, 0) 94 offset += uint64(len(bufs[i])) 95 } 96 97 _, err = ring.Submit(num) 98 require.NoError(t, err) 99 100 for i := 0; i < num; i++ { 101 cqe, err := ring.GetCQEntry(0) 102 require.NoError(t, err) 103 require.Equal(t, len(bufs[i]), int(cqe.Result()), "failed with %v", syscall.Errno(-cqe.Result())) 104 require.Equal(t, wbuf[i*8:(i+1)*8], bufs[i][:]) 105 } 106 } 107 } 108 109 func TestCopy(t *testing.T) { 110 from, err := ioutil.TempFile("", "copy-from-") 111 require.NoError(t, err) 112 defer from.Close() 113 114 to, err := ioutil.TempFile("", "copy-to-") 115 require.NoError(t, err) 116 defer to.Close() 117 118 ring, err := Setup(4, nil) 119 require.NoError(t, err) 120 defer ring.Close() 121 122 buf := make([]byte, 4096) 123 _, _ = rand.Read(buf) 124 _, err = from.Write(buf) 125 require.NoError(t, err) 126 off, err := from.Seek(0, 0) 127 require.NoError(t, err) 128 require.Equal(t, int64(0), off) 129 130 reuse := [32]byte{} 131 rlth := uint64(len(reuse)) 132 vector := []syscall.Iovec{ 133 { 134 Base: &reuse[0], 135 Len: rlth, 136 }, 137 } 138 var ( 139 offset uint64 140 ) 141 for { 142 read := ring.GetSQEntry() 143 write := ring.GetSQEntry() 144 145 Readv(read, from.Fd(), vector, offset, 0) 146 read.SetFlags(IOSQE_IO_LINK) 147 Writev(write, to.Fd(), vector, offset, 0) 148 149 _, err := ring.Submit(2) 150 require.NoError(t, err) 151 152 rcqe, err := ring.GetCQEntry(0) 153 require.NoError(t, err) 154 require.True(t, rcqe.Result() >= 0, "read result %d ('%v')", rcqe.Result(), syscall.Errno(-rcqe.Result())) 155 156 ret := rcqe.Result() 157 if ret == 0 { 158 break 159 } 160 161 wcqe, err := ring.GetCQEntry(0) 162 require.NoError(t, err) 163 require.Equal(t, ret, wcqe.Result(), "write result %d ('%v')", wcqe.Result(), syscall.Errno(-wcqe.Result())) 164 165 offset += rlth 166 } 167 168 fromData, err := ioutil.ReadAll(from) 169 toData, err := ioutil.ReadAll(to) 170 require.NoError(t, err, "failed to read 'from'") 171 require.NoError(t, err, "failed to read 'to'") 172 require.Equal(t, len(fromData), len(toData)) 173 require.Equal(t, fromData, toData) 174 } 175 176 func TestReuseSQEntries(t *testing.T) { 177 ring, err := Setup(2, nil) 178 require.NoError(t, err) 179 180 for r := 0; r < 10; r++ { 181 for i := 1; i <= 2; i++ { 182 sqe := ring.GetSQEntry() 183 sqe.Reset() 184 require.Equal(t, uint64(0), sqe.userData) 185 Nop(sqe) 186 sqe.SetUserData(uint64(i)) 187 } 188 n, err := ring.Submit(2) 189 require.NoError(t, err) 190 require.Equal(t, uint32(2), n) 191 192 for i := 1; i <= 2; i++ { 193 cqe, err := ring.GetCQEntry(0) 194 require.NoError(t, err) 195 require.Equal(t, uint64(i), cqe.UserData()) 196 } 197 } 198 199 } 200 201 func TestNoEnter(t *testing.T) { 202 ring, err := Setup(4, nil) 203 require.NoError(t, err) 204 defer ring.Close() 205 206 sqe := ring.GetSQEntry() 207 Nop(sqe) 208 _, err = ring.Submit(0) 209 require.NoError(t, err) 210 211 start := time.Now() 212 for time.Since(start) < time.Second { 213 _, err := ring.GetCQEntry(0) 214 if err == nil { 215 return 216 } 217 } 218 require.FailNow(t, "nop operation wasn't completed") 219 } 220 221 func TestResubmitBeforeCompletion(t *testing.T) { 222 n := 2048 223 ring, err := Setup(uint(n), nil) 224 require.NoError(t, err) 225 defer ring.Close() 226 227 for round := 0; round < 2; round++ { 228 // sq entry can be reused after call to Submit returned 229 for i := uint64(1); i <= uint64(n); i++ { 230 sqe := ring.GetSQEntry() 231 Nop(sqe) 232 sqe.SetUserData(i) 233 } 234 235 _, err = ring.Submit(0) 236 require.NoError(t, err) 237 } 238 for round := 0; round < 2; round++ { 239 for i := uint64(1); i <= uint64(n); i++ { 240 for { 241 cqe, err := ring.GetCQEntry(0) 242 if err != nil { 243 continue 244 } 245 require.Equal(t, i, cqe.UserData()) 246 break 247 } 248 } 249 } 250 } 251 252 func TestReadWriteFixed(t *testing.T) { 253 ring, err := Setup(32, nil) 254 require.NoError(t, err) 255 defer ring.Close() 256 257 f, err := ioutil.TempFile("", "test") 258 require.NoError(t, err) 259 defer os.Remove(f.Name()) 260 261 data := []byte("ping") 262 resp := make([]byte, len(data)) 263 iovec := []syscall.Iovec{ 264 { 265 Base: &data[0], 266 Len: uint64(len(data)), 267 }, 268 { 269 Base: &resp[0], 270 Len: uint64(len(data)), 271 }, 272 } 273 274 require.NoError(t, ring.RegisterBuffers(iovec)) 275 276 sqe := ring.GetSQEntry() 277 WriteFixed(sqe, f.Fd(), iovec[0].Base, iovec[0].Len, 0, 0, 0) 278 _, err = ring.Submit(1) 279 require.NoError(t, err) 280 281 cqe, err := ring.GetCQEntry(1) 282 require.NoError(t, err) 283 require.Equal(t, int32(len(data)), cqe.Result(), syscall.Errno(-cqe.Result())) 284 285 out := make([]byte, len(data)) 286 _, err = f.ReadAt(out, 0) 287 require.NoError(t, err) 288 require.Equal(t, data, out) 289 290 in := []byte("pong") 291 _, err = f.WriteAt(in, 0) 292 require.NoError(t, err) 293 294 sqe = ring.GetSQEntry() 295 ReadFixed(sqe, f.Fd(), iovec[1].Base, iovec[1].Len, 0, 0, 1) 296 _, err = ring.Submit(1) 297 require.NoError(t, err) 298 299 cqe, err = ring.GetCQEntry(1) 300 require.NoError(t, err) 301 require.Equal(t, int32(len(data)), cqe.Result(), syscall.Errno(-cqe.Result())) 302 303 require.Equal(t, in, resp) 304 } 305 306 func TestIOPoll(t *testing.T) { 307 ring, err := Setup(4, &IOUringParams{Flags: IORING_SETUP_IOPOLL}) 308 require.NoError(t, err) 309 defer ring.Close() 310 311 // returns immediatly 312 _, err = ring.GetCQEntry(0) 313 require.Error(t, syscall.EAGAIN, err) 314 315 // returns once consumed scheduler time slice 316 _, err = ring.GetCQEntry(1) 317 require.Error(t, syscall.EAGAIN, err) 318 319 // TODO IOPOLL currently not supported on my devices 320 }