github.com/pawelgaczynski/giouring@v0.0.0-20230826085535-69588b89acb9/lib.go (about) 1 // MIT License 2 // 3 // Copyright (c) 2023 Paweł Gaczyński 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a 6 // copy of this software and associated documentation files (the 7 // "Software"), to deal in the Software without restriction, including 8 // without limitation the rights to use, copy, modify, merge, publish, 9 // distribute, sublicense, and/or sell copies of the Software, and to 10 // permit persons to whom the Software is furnished to do so, subject to 11 // the following conditions: 12 // 13 // The above copyright notice and this permission notice shall be included 14 // in all copies or substantial portions of the Software. 15 // 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 24 package giouring 25 26 import ( 27 "sync/atomic" 28 "syscall" 29 "unsafe" 30 ) 31 32 const ( 33 sysSetup = 425 34 sysEnter = 426 35 sysRegister = 427 36 ) 37 38 // liburing: io_uring_sq 39 type SubmissionQueue struct { 40 head *uint32 41 tail *uint32 42 ringMask *uint32 43 ringEntries *uint32 44 flags *uint32 45 dropped *uint32 46 array *uint32 47 sqes *SubmissionQueueEntry 48 49 ringSize uint 50 ringPtr unsafe.Pointer 51 52 sqeHead uint32 53 sqeTail uint32 54 55 // nolint: unused 56 pad [2]uint32 57 } 58 59 // liburing: io_uring_cq 60 type CompletionQueue struct { 61 head *uint32 62 tail *uint32 63 ringMask *uint32 64 ringEntries *uint32 65 flags *uint32 66 overflow *uint32 67 cqes *CompletionQueueEvent 68 69 ringSize uint 70 ringPtr unsafe.Pointer 71 72 // nolint: unused 73 pad [2]uint32 74 } 75 76 // liburing: io_uring 77 type Ring struct { 78 sqRing *SubmissionQueue 79 cqRing *CompletionQueue 80 flags uint32 81 ringFd int 82 features uint32 83 enterRingFd int 84 intFlags uint8 85 // nolint: unused 86 pad [3]uint8 87 // nolint: unused 88 pad2 uint32 89 } 90 91 // liburing: io_uring_cqe_shift 92 func (ring *Ring) cqeShift() uint32 { 93 if ring.flags&SetupCQE32 != 0 { 94 return 1 95 } 96 97 return 0 98 } 99 100 // liburing: io_uring_cqe_index 101 func (ring *Ring) cqeIndex(ptr, mask uint32) uintptr { 102 return uintptr((ptr & mask) << ring.cqeShift()) 103 } 104 105 // liburing: io_uring_for_each_cqe - https://manpages.debian.org/unstable/liburing-dev/io_uring_for_each_cqe.3.en.html 106 func (ring *Ring) ForEachCQE(callback func(cqe *CompletionQueueEvent)) { 107 var cqe *CompletionQueueEvent 108 for head := atomic.LoadUint32(ring.cqRing.head); ; head++ { 109 if head != atomic.LoadUint32(ring.cqRing.tail) { 110 cqeIndex := ring.cqeIndex(head, *ring.cqRing.ringMask) 111 cqe = (*CompletionQueueEvent)( 112 unsafe.Add(unsafe.Pointer(ring.cqRing.cqes), cqeIndex*unsafe.Sizeof(CompletionQueueEvent{})), 113 ) 114 callback(cqe) 115 } else { 116 break 117 } 118 } 119 } 120 121 // liburing: io_uring_cq_advance - https://manpages.debian.org/unstable/liburing-dev/io_uring_cq_advance.3.en.html 122 func (ring *Ring) CQAdvance(numberOfCQEs uint32) { 123 atomic.StoreUint32(ring.cqRing.head, *ring.cqRing.head+numberOfCQEs) 124 } 125 126 // liburing: io_uring_cqe_seen - https://manpages.debian.org/unstable/liburing-dev/io_uring_cqe_seen.3.en.html 127 func (ring *Ring) CQESeen(event *CompletionQueueEvent) { 128 if event != nil { 129 ring.CQAdvance(1) 130 } 131 } 132 133 // liburing: io_uring_sqe_set_data - https://manpages.debian.org/unstable/liburing-dev/io_uring_sqe_set_data.3.en.html 134 func (entry *SubmissionQueueEntry) SetData(data unsafe.Pointer) { 135 entry.UserData = uint64(uintptr(data)) 136 } 137 138 // liburing: io_uring_cqe_get_data - https://manpages.debian.org/unstable/liburing-dev/io_uring_cqe_get_data.3.en.html 139 func (c *CompletionQueueEvent) GetData() unsafe.Pointer { 140 return unsafe.Pointer(uintptr(c.UserData)) 141 } 142 143 // liburing: io_uring_sqe_set_data64 - https://manpages.debian.org/unstable/liburing-dev/io_uring_sqe_set_data64.3.en.html 144 func (entry *SubmissionQueueEntry) SetData64(data uint64) { 145 entry.UserData = data 146 } 147 148 // liburing: io_uring_cqe_get_data64 - https://manpages.debian.org/unstable/liburing-dev/io_uring_cqe_get_data64.3.en.html 149 func (c *CompletionQueueEvent) GetData64() uint64 { 150 return c.UserData 151 } 152 153 // liburing: io_uring_sqe_set_flags - https://manpages.debian.org/unstable/liburing-dev/io_uring_sqe_set_flags.3.en.html 154 func (entry *SubmissionQueueEntry) SetFlags(flags uint32) { 155 entry.Flags = uint8(flags) 156 } 157 158 // liburing: io_uring_sq_ready - https://manpages.debian.org/unstable/liburing-dev/io_uring_sq_ready.3.en.html 159 func (ring *Ring) SQReady() uint32 { 160 khead := *ring.sqRing.head 161 162 if ring.flags&SetupSQPoll != 0 { 163 khead = atomic.LoadUint32(ring.sqRing.head) 164 } 165 166 return ring.sqRing.sqeTail - khead 167 } 168 169 // liburing: io_uring_sq_space_left - https://manpages.debian.org/unstable/liburing-dev/io_uring_sq_space_left.3.en.html 170 func (ring *Ring) SQSpaceLeft() uint32 { 171 return *ring.sqRing.ringEntries - ring.SQReady() 172 } 173 174 // liburing: io_uring_sqring_wait - https://manpages.debian.org/unstable/liburing-dev/io_uring_sqring_wait.3.en.html 175 func (ring *Ring) SQRingWait() (uint, error) { 176 if ring.flags&SetupSQPoll == 0 { 177 return 0, nil 178 } 179 if ring.SQSpaceLeft() != 0 { 180 return 0, nil 181 } 182 183 return ring.internalSQRingWait() 184 } 185 186 // liburing: io_uring_cq_ready - https://manpages.debian.org/unstable/liburing-dev/io_uring_cq_ready.3.en.html 187 func (ring *Ring) CQReady() uint32 { 188 return atomic.LoadUint32(ring.cqRing.tail) - *ring.cqRing.head 189 } 190 191 // liburing: io_uring_cq_has_overflow - https://manpages.debian.org/unstable/liburing-dev/io_uring_cq_has_overflow.3.en.html 192 func (ring *Ring) CQHasOverflow() bool { 193 return atomic.LoadUint32(ring.sqRing.flags)&SQCQOverflow != 0 194 } 195 196 // liburing: io_uring_cq_eventfd_enabled 197 func (ring *Ring) CQEventfdEnabled() bool { 198 if *ring.cqRing.flags == 0 { 199 return true 200 } 201 202 return !(*ring.cqRing.flags&CQEventFdDisabled != 0) 203 } 204 205 // liburing: io_uring_cq_eventfd_toggle 206 func (ring *Ring) CqEventfdToggle(enabled bool) error { 207 var flags uint32 208 209 if enabled == ring.CQEventfdEnabled() { 210 return nil 211 } 212 213 if *ring.cqRing.flags == 0 { 214 return syscall.EOPNOTSUPP 215 } 216 217 flags = *ring.cqRing.flags 218 219 if enabled { 220 flags &= ^CQEventFdDisabled 221 } else { 222 flags |= CQEventFdDisabled 223 } 224 225 atomic.StoreUint32(ring.cqRing.flags, flags) 226 227 return nil 228 } 229 230 // liburing: io_uring_wait_cqe_nr - https://manpages.debian.org/unstable/liburing-dev/io_uring_wait_cqe_nr.3.en.html 231 func (ring *Ring) WaitCQENr(waitNr uint32) (*CompletionQueueEvent, error) { 232 return ring.internalGetCQE(0, waitNr, nil) 233 } 234 235 // liburing: __io_uring_peek_cqe 236 func internalPeekCQE(ring *Ring, nrAvailable *uint32) (*CompletionQueueEvent, error) { 237 var cqe *CompletionQueueEvent 238 var err error 239 var available uint32 240 var shift uint32 241 mask := *ring.cqRing.ringMask 242 243 if ring.flags&SetupCQE32 != 0 { 244 shift = 1 245 } 246 247 for { 248 tail := atomic.LoadUint32(ring.cqRing.tail) 249 head := *ring.cqRing.head 250 251 cqe = nil 252 available = tail - head 253 if available == 0 { 254 break 255 } 256 257 cqe = (*CompletionQueueEvent)( 258 unsafe.Add(unsafe.Pointer(ring.cqRing.cqes), uintptr((head&mask)<<shift)*unsafe.Sizeof(CompletionQueueEvent{})), 259 ) 260 261 if ring.features&FeatExtArg == 0 && cqe.UserData == liburingUdataTimeout { 262 if cqe.Res < 0 { 263 err = syscall.Errno(uintptr(-cqe.Res)) 264 } 265 ring.CQAdvance(1) 266 if err == nil { 267 continue 268 } 269 cqe = nil 270 } 271 272 break 273 } 274 275 if nrAvailable != nil { 276 *nrAvailable = available 277 } 278 279 return cqe, err 280 } 281 282 // liburing: io_uring_peek_cqe - https://manpages.debian.org/unstable/liburing-dev/io_uring_peek_cqe.3.en.html 283 func (ring *Ring) PeekCQE() (*CompletionQueueEvent, error) { 284 cqe, err := internalPeekCQE(ring, nil) 285 if err == nil && cqe != nil { 286 return cqe, nil 287 } 288 289 return ring.WaitCQENr(0) 290 } 291 292 // liburing: io_uring_wait_cqe - https://manpages.debian.org/unstable/liburing-dev/io_uring_wait_cqe.3.en.html 293 func (ring *Ring) WaitCQE() (*CompletionQueueEvent, error) { 294 // return ring.WaitCQENr(1) 295 cqe, err := internalPeekCQE(ring, nil) 296 if err == nil && cqe != nil { 297 return cqe, nil 298 } 299 300 return ring.WaitCQENr(1) 301 } 302 303 // liburing: _io_uring_get_sqe 304 func privateGetSQE(ring *Ring) *SubmissionQueueEntry { 305 sq := ring.sqRing 306 var head, next uint32 307 var shift int 308 309 if ring.flags&SetupSQE128 != 0 { 310 shift = 1 311 } 312 head = atomic.LoadUint32(sq.head) 313 next = sq.sqeTail + 1 314 if next-head <= *sq.ringEntries { 315 sqe := (*SubmissionQueueEntry)( 316 unsafe.Add(unsafe.Pointer(ring.sqRing.sqes), 317 uintptr((sq.sqeTail&*sq.ringMask)<<shift)*unsafe.Sizeof(SubmissionQueueEntry{})), 318 ) 319 sq.sqeTail = next 320 321 return sqe 322 } 323 324 return nil 325 } 326 327 // liburing: io_uring_get_sqe - https://manpages.debian.org/unstable/liburing-dev/io_uring_get_sqe.3.en.html 328 func (ring *Ring) GetSQE() *SubmissionQueueEntry { 329 return privateGetSQE(ring) 330 }