io_uring_linux.go 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487
  1. //go:build linux && !android && !e2e_testing
  2. // +build linux,!android,!e2e_testing
  3. package udp
  4. import (
  5. "errors"
  6. "fmt"
  7. "net"
  8. "runtime"
  9. "sync"
  10. "sync/atomic"
  11. "syscall"
  12. "unsafe"
  13. "github.com/sirupsen/logrus"
  14. "golang.org/x/sys/unix"
  15. )
  16. const (
  17. ioringOpSendmsg = 9
  18. ioringOpRecvmsg = 10
  19. ioringEnterGetevents = 1 << 0
  20. ioringSetupClamp = 1 << 4
  21. ioringSetupCoopTaskrun = 1 << 8 // Kernel 5.19+: reduce thread creation
  22. ioringSetupSingleIssuer = 1 << 12 // Kernel 6.0+: single submitter optimization
  23. ioringRegisterIowqMaxWorkers = 19 // Register opcode to limit workers
  24. ioringOffSqRing = 0
  25. ioringOffCqRing = 0x8000000
  26. ioringOffSqes = 0x10000000
  27. defaultIoUringEntries = 256
  28. ioUringSqeSize = 64 // struct io_uring_sqe size defined by kernel ABI
  29. )
  30. type ioSqringOffsets struct {
  31. Head uint32
  32. Tail uint32
  33. RingMask uint32
  34. RingEntries uint32
  35. Flags uint32
  36. Dropped uint32
  37. Array uint32
  38. Resv1 uint32
  39. Resv2 uint64
  40. }
  41. type ioCqringOffsets struct {
  42. Head uint32
  43. Tail uint32
  44. RingMask uint32
  45. RingEntries uint32
  46. Overflow uint32
  47. Cqes uint32
  48. Resv [2]uint32
  49. }
  50. type ioUringParams struct {
  51. SqEntries uint32
  52. CqEntries uint32
  53. Flags uint32
  54. SqThreadCPU uint32
  55. SqThreadIdle uint32
  56. Features uint32
  57. WqFd uint32
  58. Resv [3]uint32
  59. SqOff ioSqringOffsets
  60. CqOff ioCqringOffsets
  61. }
  62. type ioUringSqe struct {
  63. Opcode uint8
  64. Flags uint8
  65. Ioprio uint16
  66. Fd int32
  67. Off uint64
  68. Addr uint64
  69. Len uint32
  70. MsgFlags uint32
  71. UserData uint64
  72. BufIndex uint16
  73. Personality uint16
  74. SpliceFdIn int32
  75. SpliceOffIn uint64
  76. Addr2 uint64
  77. }
  78. type ioUringCqe struct {
  79. UserData uint64
  80. Res int32
  81. Flags uint32
  82. // No explicit padding needed - Go will align uint64 to 8 bytes,
  83. // and int32/uint32 will be naturally aligned. Total size should be 16 bytes.
  84. // Kernel structure: __u64 user_data; __s32 res; __u32 flags;
  85. }
  86. func init() {
  87. if sz := unsafe.Sizeof(ioUringSqe{}); sz != ioUringSqeSize {
  88. panic(fmt.Sprintf("io_uring SQE size mismatch: expected %d, got %d", ioUringSqeSize, sz))
  89. }
  90. if sz := unsafe.Sizeof(ioUringCqe{}); sz != 16 {
  91. panic(fmt.Sprintf("io_uring CQE size mismatch: expected %d, got %d", 16, sz))
  92. }
  93. }
  94. // pendingSend tracks all heap-allocated structures for a single io_uring submission
  95. // to ensure they remain valid until the kernel completes the operation
  96. type pendingSend struct {
  97. msgCopy *unix.Msghdr
  98. iovCopy *unix.Iovec
  99. sockaddrCopy []byte
  100. controlCopy []byte
  101. payloadRef unsafe.Pointer
  102. userData uint64
  103. }
  104. type pendingRecv struct {
  105. msgCopy *unix.Msghdr
  106. iovCopy *unix.Iovec
  107. nameBuf []byte
  108. controlBuf []byte
  109. payloadBuf []byte
  110. callerMsg *unix.Msghdr
  111. userData uint64
  112. }
  113. type ioUringBatchResult struct {
  114. res int32
  115. flags uint32
  116. err error
  117. }
  118. type ioUringBatchEntry struct {
  119. fd int
  120. msg *unix.Msghdr
  121. msgFlags uint32
  122. payloadLen uint32
  123. userData uint64
  124. result *ioUringBatchResult
  125. }
  126. type ioUringState struct {
  127. fd int
  128. sqRing []byte
  129. cqRing []byte
  130. sqesMap []byte
  131. sqes []ioUringSqe
  132. cqCqes []ioUringCqe
  133. sqHead *uint32
  134. sqTail *uint32
  135. sqRingMask *uint32
  136. sqRingEntries *uint32
  137. sqArray []uint32
  138. cqHead *uint32
  139. cqTail *uint32
  140. cqRingMask *uint32
  141. cqRingEntries *uint32
  142. mu sync.Mutex
  143. userData uint64
  144. pendingSends map[uint64]*pendingSend
  145. sqEntryCount uint32
  146. cqEntryCount uint32
  147. pendingReceives map[uint64]*pendingRecv
  148. completedCqes map[uint64]*ioUringCqe
  149. }
  150. // recvBuffer represents a single receive operation with its associated buffers
  151. type recvBuffer struct {
  152. payloadBuf []byte // Buffer for packet data
  153. nameBuf []byte // Buffer for source address
  154. controlBuf []byte // Buffer for control messages
  155. msghdr *unix.Msghdr // Message header for recvmsg
  156. iovec *unix.Iovec // IO vector pointing to payloadBuf
  157. userData uint64 // User data for tracking this operation
  158. inFlight atomic.Bool // Whether this buffer has a pending io_uring operation
  159. inUse atomic.Bool // Buffer handed to caller; wait for release before reuse
  160. }
  161. // ioUringRecvState manages a dedicated io_uring for receiving packets
  162. // It maintains a pool of receive buffers and continuously keeps receives queued
  163. type ioUringRecvState struct {
  164. fd int
  165. sqRing []byte
  166. cqRing []byte
  167. sqesMap []byte
  168. sqes []ioUringSqe
  169. cqCqes []ioUringCqe
  170. sqHead *uint32
  171. sqTail *uint32
  172. sqRingMask *uint32
  173. sqRingEntries *uint32
  174. sqArray []uint32
  175. cqHead *uint32
  176. cqTail *uint32
  177. cqRingMask *uint32
  178. cqRingEntries *uint32
  179. mu sync.Mutex
  180. userData uint64
  181. bufferPool []*recvBuffer // Pool of all receive buffers
  182. bufferMap map[uint64]*recvBuffer // Map userData -> buffer
  183. sqEntryCount uint32
  184. cqEntryCount uint32
  185. sockFd int // Socket file descriptor to receive from
  186. closed atomic.Bool
  187. }
  188. func alignUint32(v, alignment uint32) uint32 {
  189. if alignment == 0 {
  190. return v
  191. }
  192. mod := v % alignment
  193. if mod == 0 {
  194. return v
  195. }
  196. return v + alignment - mod
  197. }
  198. func newIoUringState(entries uint32) (*ioUringState, error) {
  199. const minEntries = 8
  200. if entries == 0 {
  201. entries = defaultIoUringEntries
  202. }
  203. if entries < minEntries {
  204. entries = minEntries
  205. }
  206. tries := entries
  207. var params ioUringParams
  208. // Try flag combinations in order (5.19+ -> baseline)
  209. // Note: SINGLE_ISSUER causes EEXIST errors, so it's excluded
  210. flagSets := []uint32{
  211. ioringSetupClamp | ioringSetupCoopTaskrun, // Kernel 5.19+: reduce thread creation
  212. ioringSetupClamp, // All kernels
  213. }
  214. flagSetIdx := 0
  215. for {
  216. params = ioUringParams{Flags: flagSets[flagSetIdx]}
  217. fd, _, errno := unix.Syscall(unix.SYS_IO_URING_SETUP, uintptr(tries), uintptr(unsafe.Pointer(&params)), 0)
  218. if errno != 0 {
  219. // If EINVAL, try next flag set (kernel doesn't support these flags)
  220. if errno == unix.EINVAL && flagSetIdx < len(flagSets)-1 {
  221. flagSetIdx++
  222. continue
  223. }
  224. if errno == unix.ENOMEM && tries > minEntries {
  225. tries /= 2
  226. if tries < minEntries {
  227. tries = minEntries
  228. }
  229. continue
  230. }
  231. return nil, errno
  232. }
  233. ring := &ioUringState{
  234. fd: int(fd),
  235. sqEntryCount: params.SqEntries,
  236. cqEntryCount: params.CqEntries,
  237. userData: 1,
  238. pendingSends: make(map[uint64]*pendingSend),
  239. pendingReceives: make(map[uint64]*pendingRecv),
  240. completedCqes: make(map[uint64]*ioUringCqe),
  241. }
  242. if err := ring.mapRings(&params); err != nil {
  243. ring.Close()
  244. if errors.Is(err, unix.ENOMEM) && tries > minEntries {
  245. tries /= 2
  246. if tries < minEntries {
  247. tries = minEntries
  248. }
  249. continue
  250. }
  251. return nil, err
  252. }
  253. // Limit kernel worker threads to prevent thousands being spawned
  254. // [0] = bounded workers, [1] = unbounded workers
  255. maxWorkers := [2]uint32{4, 4} // Limit to 4 workers of each type
  256. _, _, errno = unix.Syscall6(
  257. unix.SYS_IO_URING_REGISTER,
  258. uintptr(fd),
  259. uintptr(ioringRegisterIowqMaxWorkers),
  260. uintptr(unsafe.Pointer(&maxWorkers[0])),
  261. 2, // array length
  262. 0, 0,
  263. )
  264. // Ignore errors - older kernels don't support this
  265. return ring, nil
  266. }
  267. }
  268. func (r *ioUringState) mapRings(params *ioUringParams) error {
  269. pageSize := uint32(unix.Getpagesize())
  270. sqRingSize := alignUint32(params.SqOff.Array+params.SqEntries*4, pageSize)
  271. cqRingSize := alignUint32(params.CqOff.Cqes+params.CqEntries*uint32(unsafe.Sizeof(ioUringCqe{})), pageSize)
  272. sqesSize := alignUint32(params.SqEntries*ioUringSqeSize, pageSize)
  273. sqRing, err := unix.Mmap(r.fd, ioringOffSqRing, int(sqRingSize), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
  274. if err != nil {
  275. return err
  276. }
  277. r.sqRing = sqRing
  278. cqRing, err := unix.Mmap(r.fd, ioringOffCqRing, int(cqRingSize), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
  279. if err != nil {
  280. unix.Munmap(r.sqRing)
  281. r.sqRing = nil
  282. return err
  283. }
  284. r.cqRing = cqRing
  285. sqesMap, err := unix.Mmap(r.fd, ioringOffSqes, int(sqesSize), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
  286. if err != nil {
  287. unix.Munmap(r.cqRing)
  288. unix.Munmap(r.sqRing)
  289. r.cqRing = nil
  290. r.sqRing = nil
  291. return err
  292. }
  293. r.sqesMap = sqesMap
  294. sqBase := unsafe.Pointer(&sqRing[0])
  295. r.sqHead = (*uint32)(unsafe.Pointer(uintptr(sqBase) + uintptr(params.SqOff.Head)))
  296. r.sqTail = (*uint32)(unsafe.Pointer(uintptr(sqBase) + uintptr(params.SqOff.Tail)))
  297. r.sqRingMask = (*uint32)(unsafe.Pointer(uintptr(sqBase) + uintptr(params.SqOff.RingMask)))
  298. r.sqRingEntries = (*uint32)(unsafe.Pointer(uintptr(sqBase) + uintptr(params.SqOff.RingEntries)))
  299. arrayPtr := unsafe.Pointer(uintptr(sqBase) + uintptr(params.SqOff.Array))
  300. r.sqArray = unsafe.Slice((*uint32)(arrayPtr), int(params.SqEntries))
  301. sqesBase := unsafe.Pointer(&sqesMap[0])
  302. r.sqes = unsafe.Slice((*ioUringSqe)(sqesBase), int(params.SqEntries))
  303. cqBase := unsafe.Pointer(&cqRing[0])
  304. r.cqHead = (*uint32)(unsafe.Pointer(uintptr(cqBase) + uintptr(params.CqOff.Head)))
  305. r.cqTail = (*uint32)(unsafe.Pointer(uintptr(cqBase) + uintptr(params.CqOff.Tail)))
  306. r.cqRingMask = (*uint32)(unsafe.Pointer(uintptr(cqBase) + uintptr(params.CqOff.RingMask)))
  307. r.cqRingEntries = (*uint32)(unsafe.Pointer(uintptr(cqBase) + uintptr(params.CqOff.RingEntries)))
  308. cqesPtr := unsafe.Pointer(uintptr(cqBase) + uintptr(params.CqOff.Cqes))
  309. // CRITICAL: Ensure CQE array pointer is properly aligned
  310. // The kernel's CQE structure is 16 bytes, and the array must be aligned
  311. // Verify alignment and log if misaligned
  312. cqeSize := uintptr(unsafe.Sizeof(ioUringCqe{}))
  313. if cqeSize != 16 {
  314. return fmt.Errorf("io_uring CQE size mismatch: expected 16, got %d", cqeSize)
  315. }
  316. cqesOffset := uintptr(cqesPtr) % 8
  317. if cqesOffset != 0 {
  318. logrus.WithFields(logrus.Fields{
  319. "cqes_ptr": fmt.Sprintf("%p", cqesPtr),
  320. "cqes_offset": cqesOffset,
  321. "cq_base": fmt.Sprintf("%p", cqBase),
  322. "cq_off_cqes": params.CqOff.Cqes,
  323. }).Warn("io_uring CQE array may be misaligned")
  324. }
  325. r.cqCqes = unsafe.Slice((*ioUringCqe)(cqesPtr), int(params.CqEntries))
  326. return nil
  327. }
  328. func (r *ioUringState) getSqeLocked() (*ioUringSqe, error) {
  329. iterations := 0
  330. for {
  331. head := atomic.LoadUint32(r.sqHead)
  332. tail := atomic.LoadUint32(r.sqTail)
  333. entries := atomic.LoadUint32(r.sqRingEntries)
  334. used := tail - head
  335. if tail-head < entries {
  336. mask := atomic.LoadUint32(r.sqRingMask)
  337. idx := tail & mask
  338. sqe := &r.sqes[idx]
  339. *sqe = ioUringSqe{}
  340. r.sqArray[idx] = idx
  341. atomic.StoreUint32(r.sqTail, tail+1)
  342. if iterations > 0 {
  343. logrus.WithFields(logrus.Fields{
  344. "iterations": iterations,
  345. "used": used,
  346. "entries": entries,
  347. }).Debug("getSqeLocked got slot after waiting")
  348. }
  349. return sqe, nil
  350. }
  351. logrus.WithFields(logrus.Fields{
  352. "head": head,
  353. "tail": tail,
  354. "entries": entries,
  355. "used": used,
  356. }).Warn("getSqeLocked: io_uring ring is FULL, waiting for completions")
  357. if err := r.submitAndWaitLocked(0, 1); err != nil {
  358. return nil, err
  359. }
  360. iterations++
  361. }
  362. }
  363. func (r *ioUringState) submitAndWaitLocked(submit, wait uint32) error {
  364. var flags uintptr
  365. if wait > 0 {
  366. flags = ioringEnterGetevents
  367. }
  368. for {
  369. _, _, errno := unix.Syscall6(unix.SYS_IO_URING_ENTER, uintptr(r.fd), uintptr(submit), uintptr(wait), flags, 0, 0)
  370. if errno == 0 {
  371. return nil
  372. }
  373. if errno == unix.EINTR {
  374. continue
  375. }
  376. return errno
  377. }
  378. }
  379. func (r *ioUringState) enqueueSendmsgLocked(fd int, msg *unix.Msghdr, msgFlags uint32, payloadLen uint32) (uint64, error) {
  380. sqe, err := r.getSqeLocked()
  381. if err != nil {
  382. return 0, err
  383. }
  384. userData := r.userData
  385. r.userData++
  386. msgCopy := new(unix.Msghdr)
  387. *msgCopy = *msg
  388. var iovCopy *unix.Iovec
  389. var payloadRef unsafe.Pointer
  390. if msg.Iov != nil {
  391. iovCopy = new(unix.Iovec)
  392. *iovCopy = *msg.Iov
  393. msgCopy.Iov = iovCopy
  394. if iovCopy.Base != nil {
  395. payloadRef = unsafe.Pointer(iovCopy.Base)
  396. }
  397. }
  398. var sockaddrCopy []byte
  399. if msg.Name != nil && msg.Namelen > 0 {
  400. sockaddrCopy = make([]byte, msg.Namelen)
  401. copy(sockaddrCopy, (*[256]byte)(unsafe.Pointer(msg.Name))[:msg.Namelen])
  402. msgCopy.Name = &sockaddrCopy[0]
  403. }
  404. var controlCopy []byte
  405. if msg.Control != nil && msg.Controllen > 0 {
  406. controlCopy = make([]byte, msg.Controllen)
  407. copy(controlCopy, (*[256]byte)(unsafe.Pointer(msg.Control))[:msg.Controllen])
  408. msgCopy.Control = &controlCopy[0]
  409. }
  410. pending := &pendingSend{
  411. msgCopy: msgCopy,
  412. iovCopy: iovCopy,
  413. sockaddrCopy: sockaddrCopy,
  414. controlCopy: controlCopy,
  415. payloadRef: payloadRef,
  416. userData: userData,
  417. }
  418. r.pendingSends[userData] = pending
  419. sqe.Opcode = ioringOpSendmsg
  420. sqe.Fd = int32(fd)
  421. sqe.Addr = uint64(uintptr(unsafe.Pointer(msgCopy)))
  422. sqe.Len = 0
  423. sqe.MsgFlags = msgFlags
  424. sqe.Flags = 0
  425. userDataPtr := (*uint64)(unsafe.Pointer(&sqe.UserData))
  426. atomic.StoreUint64(userDataPtr, userData)
  427. _ = atomic.LoadUint64(userDataPtr)
  428. runtime.KeepAlive(msgCopy)
  429. runtime.KeepAlive(sqe)
  430. if payloadRef != nil {
  431. runtime.KeepAlive(payloadRef)
  432. }
  433. _ = atomic.LoadUint32(r.sqTail)
  434. atomic.StoreUint32(r.sqTail, atomic.LoadUint32(r.sqTail))
  435. return userData, nil
  436. }
  437. func (r *ioUringState) abortPendingSendLocked(userData uint64) {
  438. if pending, ok := r.pendingSends[userData]; ok {
  439. delete(r.pendingSends, userData)
  440. delete(r.completedCqes, userData)
  441. if pending != nil {
  442. runtime.KeepAlive(pending.msgCopy)
  443. runtime.KeepAlive(pending.iovCopy)
  444. runtime.KeepAlive(pending.sockaddrCopy)
  445. runtime.KeepAlive(pending.controlCopy)
  446. if pending.payloadRef != nil {
  447. runtime.KeepAlive(pending.payloadRef)
  448. }
  449. }
  450. }
  451. }
  452. func (r *ioUringState) abortPendingRecvLocked(userData uint64) {
  453. if pending, ok := r.pendingReceives[userData]; ok {
  454. delete(r.pendingReceives, userData)
  455. delete(r.completedCqes, userData)
  456. if pending != nil {
  457. runtime.KeepAlive(pending.msgCopy)
  458. runtime.KeepAlive(pending.iovCopy)
  459. runtime.KeepAlive(pending.payloadBuf)
  460. runtime.KeepAlive(pending.nameBuf)
  461. runtime.KeepAlive(pending.controlBuf)
  462. }
  463. }
  464. }
  465. func (r *ioUringState) completeSendLocked(userData uint64) (int32, uint32, error) {
  466. cqe, err := r.waitForCqeLocked(userData)
  467. if err != nil {
  468. r.abortPendingSendLocked(userData)
  469. return 0, 0, err
  470. }
  471. var pending *pendingSend
  472. if p, ok := r.pendingSends[userData]; ok {
  473. pending = p
  474. delete(r.pendingSends, userData)
  475. }
  476. if pending != nil {
  477. runtime.KeepAlive(pending.msgCopy)
  478. runtime.KeepAlive(pending.iovCopy)
  479. runtime.KeepAlive(pending.sockaddrCopy)
  480. runtime.KeepAlive(pending.controlCopy)
  481. if pending.payloadRef != nil {
  482. runtime.KeepAlive(pending.payloadRef)
  483. }
  484. }
  485. return cqe.Res, cqe.Flags, nil
  486. }
  487. func (r *ioUringState) enqueueRecvmsgLocked(fd int, msg *unix.Msghdr, msgFlags uint32) (uint64, error) {
  488. if msg == nil {
  489. return 0, syscall.EINVAL
  490. }
  491. var iovCount int
  492. if msg.Iov != nil {
  493. iovCount = int(msg.Iovlen)
  494. if iovCount <= 0 {
  495. return 0, syscall.EINVAL
  496. }
  497. if iovCount > 1 {
  498. return 0, syscall.ENOTSUP
  499. }
  500. }
  501. sqe, err := r.getSqeLocked()
  502. if err != nil {
  503. return 0, err
  504. }
  505. userData := r.userData
  506. r.userData++
  507. msgCopy := new(unix.Msghdr)
  508. *msgCopy = *msg
  509. var iovCopy *unix.Iovec
  510. var payloadBuf []byte
  511. if msg.Iov != nil {
  512. iovCopy = new(unix.Iovec)
  513. *iovCopy = *msg.Iov
  514. msgCopy.Iov = iovCopy
  515. setMsghdrIovlen(msgCopy, 1)
  516. if iovCopy.Base != nil {
  517. payloadLen := int(iovCopy.Len)
  518. if payloadLen < 0 {
  519. return 0, syscall.EINVAL
  520. }
  521. if payloadLen > 0 {
  522. payloadBuf = unsafe.Slice((*byte)(iovCopy.Base), payloadLen)
  523. }
  524. }
  525. }
  526. var nameBuf []byte
  527. if msgCopy.Name != nil && msgCopy.Namelen > 0 {
  528. nameLen := int(msgCopy.Namelen)
  529. if nameLen < 0 {
  530. return 0, syscall.EINVAL
  531. }
  532. nameBuf = unsafe.Slice(msgCopy.Name, nameLen)
  533. }
  534. var controlBuf []byte
  535. if msgCopy.Control != nil && msgCopy.Controllen > 0 {
  536. ctrlLen := int(msgCopy.Controllen)
  537. if ctrlLen < 0 {
  538. return 0, syscall.EINVAL
  539. }
  540. if ctrlLen > 0 {
  541. controlBuf = unsafe.Slice((*byte)(msgCopy.Control), ctrlLen)
  542. }
  543. }
  544. pending := &pendingRecv{
  545. msgCopy: msgCopy,
  546. iovCopy: iovCopy,
  547. nameBuf: nameBuf,
  548. controlBuf: controlBuf,
  549. payloadBuf: payloadBuf,
  550. callerMsg: msg,
  551. userData: userData,
  552. }
  553. r.pendingReceives[userData] = pending
  554. sqe.Opcode = ioringOpRecvmsg
  555. sqe.Fd = int32(fd)
  556. sqe.Addr = uint64(uintptr(unsafe.Pointer(msgCopy)))
  557. sqe.Len = 0
  558. sqe.MsgFlags = msgFlags
  559. sqe.Flags = 0
  560. userDataPtr := (*uint64)(unsafe.Pointer(&sqe.UserData))
  561. atomic.StoreUint64(userDataPtr, userData)
  562. _ = atomic.LoadUint64(userDataPtr)
  563. runtime.KeepAlive(msgCopy)
  564. runtime.KeepAlive(iovCopy)
  565. runtime.KeepAlive(payloadBuf)
  566. runtime.KeepAlive(nameBuf)
  567. runtime.KeepAlive(controlBuf)
  568. return userData, nil
  569. }
  570. func (r *ioUringState) completeRecvLocked(userData uint64) (int32, uint32, error) {
  571. cqe, err := r.waitForCqeLocked(userData)
  572. if err != nil {
  573. r.abortPendingRecvLocked(userData)
  574. return 0, 0, err
  575. }
  576. var pending *pendingRecv
  577. if p, ok := r.pendingReceives[userData]; ok {
  578. pending = p
  579. delete(r.pendingReceives, userData)
  580. }
  581. if pending != nil {
  582. if pending.callerMsg != nil && pending.msgCopy != nil {
  583. pending.callerMsg.Namelen = pending.msgCopy.Namelen
  584. pending.callerMsg.Controllen = pending.msgCopy.Controllen
  585. pending.callerMsg.Flags = pending.msgCopy.Flags
  586. }
  587. runtime.KeepAlive(pending.msgCopy)
  588. runtime.KeepAlive(pending.iovCopy)
  589. runtime.KeepAlive(pending.payloadBuf)
  590. runtime.KeepAlive(pending.nameBuf)
  591. runtime.KeepAlive(pending.controlBuf)
  592. }
  593. return cqe.Res, cqe.Flags, nil
  594. }
  595. func (r *ioUringState) SendmsgBatch(entries []ioUringBatchEntry) error {
  596. if len(entries) == 0 {
  597. return nil
  598. }
  599. r.mu.Lock()
  600. defer r.mu.Unlock()
  601. startTail := atomic.LoadUint32(r.sqTail)
  602. prepared := 0
  603. for i := range entries {
  604. entry := &entries[i]
  605. userData, err := r.enqueueSendmsgLocked(entry.fd, entry.msg, entry.msgFlags, entry.payloadLen)
  606. if err != nil {
  607. for j := 0; j < prepared; j++ {
  608. r.abortPendingSendLocked(entries[j].userData)
  609. }
  610. return err
  611. }
  612. entry.userData = userData
  613. prepared++
  614. }
  615. submit := atomic.LoadUint32(r.sqTail) - startTail
  616. if submit == 0 {
  617. return nil
  618. }
  619. if err := r.submitAndWaitLocked(submit, submit); err != nil {
  620. for i := 0; i < prepared; i++ {
  621. r.abortPendingSendLocked(entries[i].userData)
  622. }
  623. return err
  624. }
  625. for i := range entries {
  626. entry := &entries[i]
  627. res, flags, err := r.completeSendLocked(entry.userData)
  628. if entry.result != nil {
  629. entry.result.res = res
  630. entry.result.flags = flags
  631. entry.result.err = err
  632. }
  633. }
  634. return nil
  635. }
  636. func (r *ioUringState) popCqeLocked() (*ioUringCqe, error) {
  637. for {
  638. // According to io_uring ABI specification:
  639. // 1. Load tail with acquire semantics (ensures we see kernel's update)
  640. // 2. Load head (our consumer index)
  641. // 3. If head != tail, CQE available at index (head & mask)
  642. // 4. Read CQE (must happen before updating head)
  643. // 5. Update head with release semantics (marks CQE as consumed)
  644. // CRITICAL: According to io_uring ABI, the correct order is:
  645. // 1. Load tail with acquire semantics (ensures we see kernel's tail update)
  646. // 2. Load head (our consumer index)
  647. // 3. If head != tail, read CQE at index (head & mask)
  648. // 4. Update head with release semantics (marks CQE as consumed)
  649. // The acquire/release pair ensures we see the kernel's CQE writes
  650. // Load tail with acquire semantics - this ensures we see all kernel writes
  651. // including the CQE data
  652. tail := atomic.LoadUint32(r.cqTail)
  653. // Load head (our consumer index)
  654. head := atomic.LoadUint32(r.cqHead)
  655. if head != tail {
  656. // CQE available - calculate index using mask
  657. mask := atomic.LoadUint32(r.cqRingMask)
  658. idx := head & mask
  659. // Get pointer to CQE entry - this points into mmapped kernel memory
  660. cqe := &r.cqCqes[idx]
  661. // CRITICAL: The kernel writes the CQE with release semantics when it
  662. // updates tail. Since we loaded tail with acquire semantics above,
  663. // we should see the CQE correctly. However, we need to ensure we're
  664. // reading the fields in the correct order and with proper barriers.
  665. // Read UserData field - use atomic load to ensure proper ordering
  666. // The kernel's write to user_data happens before updating tail (release)
  667. userDataPtr := (*uint64)(unsafe.Pointer(&cqe.UserData))
  668. userData := atomic.LoadUint64(userDataPtr)
  669. // Memory barrier: ensure UserData read completes before reading other fields
  670. // This creates a proper acquire barrier
  671. _ = atomic.LoadUint32(r.cqTail)
  672. // Read other fields - these should be visible after the barrier
  673. res := cqe.Res
  674. flags := cqe.Flags
  675. // NOW update head with release semantics
  676. // This marks the CQE as consumed and must happen AFTER all reads
  677. atomic.StoreUint32(r.cqHead, head+1)
  678. // Return a copy to ensure consistency - the original CQE in mmapped
  679. // memory might be overwritten by the kernel for the next submission
  680. return &ioUringCqe{
  681. UserData: userData,
  682. Res: res,
  683. Flags: flags,
  684. }, nil
  685. }
  686. // No CQE available - wait for kernel to add one
  687. if err := r.submitAndWaitLocked(0, 1); err != nil {
  688. return nil, err
  689. }
  690. }
  691. }
  692. // waitForCqeLocked waits for a CQE matching the expected userData.
  693. // It drains any CQEs that don't match (from previous submissions that completed
  694. // out of order) until it finds the one we're waiting for.
  695. func (r *ioUringState) waitForCqeLocked(expectedUserData uint64) (*ioUringCqe, error) {
  696. if cqe, ok := r.completedCqes[expectedUserData]; ok {
  697. delete(r.completedCqes, expectedUserData)
  698. return cqe, nil
  699. }
  700. const maxIterations = 1000
  701. for iterations := 0; ; iterations++ {
  702. if iterations >= maxIterations {
  703. logrus.WithFields(logrus.Fields{
  704. "expected_userdata": expectedUserData,
  705. "pending_sends": len(r.pendingSends),
  706. "pending_recvs": len(r.pendingReceives),
  707. "completed_cache": len(r.completedCqes),
  708. }).Error("io_uring waitForCqeLocked exceeded max iterations - possible bug")
  709. return nil, syscall.EIO
  710. }
  711. cqe, err := r.popCqeLocked()
  712. if err != nil {
  713. return nil, err
  714. }
  715. userData := cqe.UserData
  716. logrus.WithFields(logrus.Fields{
  717. "cqe_userdata": userData,
  718. "cqe_userdata_hex": fmt.Sprintf("0x%x", userData),
  719. "cqe_res": cqe.Res,
  720. "cqe_flags": cqe.Flags,
  721. "expected_userdata": expectedUserData,
  722. }).Debug("io_uring CQE received")
  723. if userData == expectedUserData {
  724. return cqe, nil
  725. }
  726. if _, exists := r.completedCqes[userData]; exists {
  727. logrus.WithFields(logrus.Fields{
  728. "cqe_userdata": userData,
  729. }).Warn("io_uring received duplicate CQE for userData; overwriting previous entry")
  730. }
  731. r.completedCqes[userData] = cqe
  732. if _, sendPending := r.pendingSends[userData]; !sendPending {
  733. if _, recvPending := r.pendingReceives[userData]; !recvPending {
  734. logrus.WithFields(logrus.Fields{
  735. "cqe_userdata": userData,
  736. "cqe_res": cqe.Res,
  737. "cqe_flags": cqe.Flags,
  738. }).Warn("io_uring received CQE for unknown userData; stored for later but no pending op found")
  739. }
  740. }
  741. }
  742. }
  743. func (r *ioUringState) Sendmsg(fd int, msg *unix.Msghdr, msgFlags uint32, payloadLen uint32) (int, error) {
  744. if r == nil {
  745. return 0, &net.OpError{Op: "sendmsg", Err: syscall.EINVAL}
  746. }
  747. r.mu.Lock()
  748. defer r.mu.Unlock()
  749. userData, err := r.enqueueSendmsgLocked(fd, msg, msgFlags, payloadLen)
  750. if err != nil {
  751. return 0, &net.OpError{Op: "sendmsg", Err: err}
  752. }
  753. if err := r.submitAndWaitLocked(1, 1); err != nil {
  754. r.abortPendingSendLocked(userData)
  755. return 0, &net.OpError{Op: "sendmsg", Err: err}
  756. }
  757. res, cqeFlags, err := r.completeSendLocked(userData)
  758. if err != nil {
  759. return 0, &net.OpError{Op: "sendmsg", Err: err}
  760. }
  761. if res < 0 {
  762. errno := syscall.Errno(-res)
  763. return 0, &net.OpError{Op: "sendmsg", Err: errno}
  764. }
  765. if res == 0 && payloadLen > 0 {
  766. logrus.WithFields(logrus.Fields{
  767. "payload_len": payloadLen,
  768. "msg_namelen": msg.Namelen,
  769. "msg_flags": msgFlags,
  770. "cqe_flags": cqeFlags,
  771. "cqe_userdata": userData,
  772. }).Warn("io_uring sendmsg returned zero bytes")
  773. }
  774. return int(res), nil
  775. }
  776. func (r *ioUringState) Recvmsg(fd int, msg *unix.Msghdr, msgFlags uint32) (int, uint32, error) {
  777. if r == nil {
  778. logrus.Error("io_uring Recvmsg: r is nil")
  779. return 0, 0, &net.OpError{Op: "recvmsg", Err: syscall.EINVAL}
  780. }
  781. if msg == nil {
  782. logrus.Error("io_uring Recvmsg: msg is nil")
  783. return 0, 0, &net.OpError{Op: "recvmsg", Err: syscall.EINVAL}
  784. }
  785. r.mu.Lock()
  786. defer r.mu.Unlock()
  787. userData, err := r.enqueueRecvmsgLocked(fd, msg, msgFlags)
  788. if err != nil {
  789. return 0, 0, &net.OpError{Op: "recvmsg", Err: err}
  790. }
  791. if err := r.submitAndWaitLocked(1, 1); err != nil {
  792. r.abortPendingRecvLocked(userData)
  793. return 0, 0, &net.OpError{Op: "recvmsg", Err: err}
  794. }
  795. res, cqeFlags, err := r.completeRecvLocked(userData)
  796. if err != nil {
  797. logrus.WithFields(logrus.Fields{
  798. "userData": userData,
  799. "error": err,
  800. }).Error("io_uring completeRecvLocked failed")
  801. return 0, 0, &net.OpError{Op: "recvmsg", Err: err}
  802. }
  803. logrus.WithFields(logrus.Fields{
  804. "userData": userData,
  805. "res": res,
  806. "cqeFlags": cqeFlags,
  807. "bytesRecv": res,
  808. }).Debug("io_uring recvmsg completed")
  809. if res < 0 {
  810. errno := syscall.Errno(-res)
  811. logrus.WithFields(logrus.Fields{
  812. "userData": userData,
  813. "res": res,
  814. "errno": errno,
  815. }).Error("io_uring recvmsg negative result")
  816. return 0, cqeFlags, &net.OpError{Op: "recvmsg", Err: errno}
  817. }
  818. return int(res), cqeFlags, nil
  819. }
  820. func (r *ioUringState) Close() error {
  821. if r == nil {
  822. return nil
  823. }
  824. r.mu.Lock()
  825. // Clean up any remaining pending sends
  826. for _, pending := range r.pendingSends {
  827. runtime.KeepAlive(pending)
  828. }
  829. r.pendingSends = nil
  830. for _, pending := range r.pendingReceives {
  831. runtime.KeepAlive(pending)
  832. }
  833. r.pendingReceives = nil
  834. r.completedCqes = nil
  835. r.mu.Unlock()
  836. var err error
  837. if r.sqRing != nil {
  838. if e := unix.Munmap(r.sqRing); e != nil && err == nil {
  839. err = e
  840. }
  841. r.sqRing = nil
  842. }
  843. if r.cqRing != nil {
  844. if e := unix.Munmap(r.cqRing); e != nil && err == nil {
  845. err = e
  846. }
  847. r.cqRing = nil
  848. }
  849. if r.sqesMap != nil {
  850. if e := unix.Munmap(r.sqesMap); e != nil && err == nil {
  851. err = e
  852. }
  853. r.sqesMap = nil
  854. }
  855. if r.fd >= 0 {
  856. if e := unix.Close(r.fd); e != nil && err == nil {
  857. err = e
  858. }
  859. r.fd = -1
  860. }
  861. return err
  862. }
  863. // RecvPacket represents a received packet with its metadata
  864. type RecvPacket struct {
  865. Data []byte
  866. N int
  867. From *unix.RawSockaddrInet6
  868. Flags uint32
  869. Control []byte
  870. Controllen int
  871. RecycleFunc func()
  872. }
  873. // newIoUringRecvState creates a dedicated io_uring for receiving packets
  874. // poolSize determines how many receive operations to keep queued
  875. func newIoUringRecvState(sockFd int, entries uint32, poolSize int, bufferSize int) (*ioUringRecvState, error) {
  876. const minEntries = 8
  877. if poolSize < 1 {
  878. poolSize = 64 // Default pool size
  879. }
  880. if poolSize > 2048 {
  881. poolSize = 2048 // Cap pool size
  882. }
  883. if entries == 0 {
  884. entries = uint32(poolSize)
  885. }
  886. if entries < uint32(poolSize) {
  887. entries = uint32(poolSize)
  888. }
  889. if entries < minEntries {
  890. entries = minEntries
  891. }
  892. tries := entries
  893. var params ioUringParams
  894. // Try flag combinations in order (5.19+ -> baseline)
  895. // Note: SINGLE_ISSUER causes EEXIST errors, so it's excluded
  896. flagSets := []uint32{
  897. ioringSetupClamp | ioringSetupCoopTaskrun, // Kernel 5.19+: reduce thread creation
  898. ioringSetupClamp, // All kernels
  899. }
  900. flagSetIdx := 0
  901. for {
  902. params = ioUringParams{Flags: flagSets[flagSetIdx]}
  903. fd, _, errno := unix.Syscall(unix.SYS_IO_URING_SETUP, uintptr(tries), uintptr(unsafe.Pointer(&params)), 0)
  904. if errno != 0 {
  905. // If EINVAL, try next flag set (kernel doesn't support these flags)
  906. if errno == unix.EINVAL && flagSetIdx < len(flagSets)-1 {
  907. flagSetIdx++
  908. continue
  909. }
  910. if errno == unix.ENOMEM && tries > minEntries {
  911. tries /= 2
  912. if tries < minEntries {
  913. tries = minEntries
  914. }
  915. continue
  916. }
  917. return nil, errno
  918. }
  919. ring := &ioUringRecvState{
  920. fd: int(fd),
  921. sqEntryCount: params.SqEntries,
  922. cqEntryCount: params.CqEntries,
  923. userData: 1,
  924. bufferMap: make(map[uint64]*recvBuffer),
  925. sockFd: sockFd,
  926. }
  927. if err := ring.mapRings(&params); err != nil {
  928. ring.Close()
  929. if errors.Is(err, unix.ENOMEM) && tries > minEntries {
  930. tries /= 2
  931. if tries < minEntries {
  932. tries = minEntries
  933. }
  934. continue
  935. }
  936. return nil, err
  937. }
  938. // Allocate buffer pool
  939. ring.bufferPool = make([]*recvBuffer, poolSize)
  940. for i := 0; i < poolSize; i++ {
  941. buf := &recvBuffer{
  942. payloadBuf: make([]byte, bufferSize),
  943. nameBuf: make([]byte, unix.SizeofSockaddrInet6),
  944. controlBuf: make([]byte, 256),
  945. msghdr: &unix.Msghdr{},
  946. iovec: &unix.Iovec{},
  947. userData: ring.userData,
  948. }
  949. ring.userData++
  950. // Initialize iovec to point to payload buffer
  951. buf.iovec.Base = &buf.payloadBuf[0]
  952. buf.iovec.SetLen(len(buf.payloadBuf))
  953. // Initialize msghdr
  954. buf.msghdr.Name = &buf.nameBuf[0]
  955. buf.msghdr.Namelen = uint32(len(buf.nameBuf))
  956. buf.msghdr.Iov = buf.iovec
  957. buf.msghdr.Iovlen = 1
  958. buf.msghdr.Control = &buf.controlBuf[0]
  959. buf.msghdr.Controllen = controllen(len(buf.controlBuf))
  960. ring.bufferPool[i] = buf
  961. ring.bufferMap[buf.userData] = buf
  962. }
  963. logrus.WithFields(logrus.Fields{
  964. "poolSize": poolSize,
  965. "entries": ring.sqEntryCount,
  966. "bufferSize": bufferSize,
  967. }).Info("io_uring receive ring created")
  968. // Limit kernel worker threads to prevent thousands being spawned
  969. // [0] = bounded workers, [1] = unbounded workers
  970. maxWorkers := [2]uint32{4, 4} // Limit to 4 workers of each type
  971. _, _, errno = unix.Syscall6(
  972. unix.SYS_IO_URING_REGISTER,
  973. uintptr(fd),
  974. uintptr(ioringRegisterIowqMaxWorkers),
  975. uintptr(unsafe.Pointer(&maxWorkers[0])),
  976. 2, // array length
  977. 0, 0,
  978. )
  979. // Ignore errors - older kernels don't support this
  980. return ring, nil
  981. }
  982. }
  983. func (r *ioUringRecvState) mapRings(params *ioUringParams) error {
  984. pageSize := uint32(unix.Getpagesize())
  985. sqRingSize := alignUint32(params.SqOff.Array+params.SqEntries*4, pageSize)
  986. cqRingSize := alignUint32(params.CqOff.Cqes+params.CqEntries*16, pageSize)
  987. if params.Features&(1<<0) != 0 { // IORING_FEAT_SINGLE_MMAP
  988. if sqRingSize > cqRingSize {
  989. cqRingSize = sqRingSize
  990. } else {
  991. sqRingSize = cqRingSize
  992. }
  993. }
  994. sqRingPtr, err := unix.Mmap(r.fd, int64(ioringOffSqRing), int(sqRingSize), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED|unix.MAP_POPULATE)
  995. if err != nil {
  996. return err
  997. }
  998. r.sqRing = sqRingPtr
  999. if params.Features&(1<<0) != 0 {
  1000. r.cqRing = sqRingPtr
  1001. } else {
  1002. cqRingPtr, err := unix.Mmap(r.fd, int64(ioringOffCqRing), int(cqRingSize), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED|unix.MAP_POPULATE)
  1003. if err != nil {
  1004. return err
  1005. }
  1006. r.cqRing = cqRingPtr
  1007. }
  1008. sqesSize := int(params.SqEntries) * ioUringSqeSize
  1009. sqesPtr, err := unix.Mmap(r.fd, int64(ioringOffSqes), sqesSize, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED|unix.MAP_POPULATE)
  1010. if err != nil {
  1011. return err
  1012. }
  1013. r.sqesMap = sqesPtr
  1014. // Set up SQ pointers
  1015. r.sqHead = (*uint32)(unsafe.Pointer(&r.sqRing[params.SqOff.Head]))
  1016. r.sqTail = (*uint32)(unsafe.Pointer(&r.sqRing[params.SqOff.Tail]))
  1017. r.sqRingMask = (*uint32)(unsafe.Pointer(&r.sqRing[params.SqOff.RingMask]))
  1018. r.sqRingEntries = (*uint32)(unsafe.Pointer(&r.sqRing[params.SqOff.RingEntries]))
  1019. // Set up SQ array
  1020. arrayBase := unsafe.Pointer(&r.sqRing[params.SqOff.Array])
  1021. r.sqArray = unsafe.Slice((*uint32)(arrayBase), params.SqEntries)
  1022. // Set up SQE slice
  1023. r.sqes = unsafe.Slice((*ioUringSqe)(unsafe.Pointer(&sqesPtr[0])), params.SqEntries)
  1024. // Set up CQ pointers
  1025. r.cqHead = (*uint32)(unsafe.Pointer(&r.cqRing[params.CqOff.Head]))
  1026. r.cqTail = (*uint32)(unsafe.Pointer(&r.cqRing[params.CqOff.Tail]))
  1027. r.cqRingMask = (*uint32)(unsafe.Pointer(&r.cqRing[params.CqOff.RingMask]))
  1028. r.cqRingEntries = (*uint32)(unsafe.Pointer(&r.cqRing[params.CqOff.RingEntries]))
  1029. cqesBase := unsafe.Pointer(&r.cqRing[params.CqOff.Cqes])
  1030. r.cqCqes = unsafe.Slice((*ioUringCqe)(cqesBase), params.CqEntries)
  1031. return nil
  1032. }
  1033. // submitRecvLocked submits a single receive operation. Must be called with mutex held.
  1034. func (r *ioUringRecvState) submitRecvLocked(buf *recvBuffer) error {
  1035. if buf.inFlight.Load() {
  1036. return fmt.Errorf("buffer already in flight")
  1037. }
  1038. // Reset buffer state for reuse
  1039. buf.msghdr.Namelen = uint32(len(buf.nameBuf))
  1040. buf.msghdr.Controllen = controllen(len(buf.controlBuf))
  1041. buf.msghdr.Flags = 0
  1042. buf.iovec.SetLen(len(buf.payloadBuf))
  1043. // Get next SQE
  1044. tail := atomic.LoadUint32(r.sqTail)
  1045. head := atomic.LoadUint32(r.sqHead)
  1046. mask := *r.sqRingMask
  1047. if tail-head >= *r.sqRingEntries {
  1048. return fmt.Errorf("submission queue full")
  1049. }
  1050. idx := tail & mask
  1051. sqe := &r.sqes[idx]
  1052. // Set up SQE for IORING_OP_RECVMSG
  1053. *sqe = ioUringSqe{}
  1054. sqe.Opcode = ioringOpRecvmsg
  1055. sqe.Fd = int32(r.sockFd)
  1056. sqe.Addr = uint64(uintptr(unsafe.Pointer(buf.msghdr)))
  1057. sqe.Len = 1
  1058. sqe.UserData = buf.userData
  1059. r.sqArray[idx] = uint32(idx)
  1060. atomic.StoreUint32(r.sqTail, tail+1)
  1061. buf.inFlight.Store(true)
  1062. return nil
  1063. }
  1064. func (r *ioUringRecvState) recycleBuffer(buf *recvBuffer) {
  1065. if r == nil || buf == nil {
  1066. return
  1067. }
  1068. if r.closed.Load() {
  1069. return
  1070. }
  1071. if !buf.inUse.Swap(false) {
  1072. // Already released or never handed out
  1073. return
  1074. }
  1075. r.mu.Lock()
  1076. defer r.mu.Unlock()
  1077. if r.closed.Load() {
  1078. return
  1079. }
  1080. if buf.inFlight.Load() {
  1081. return
  1082. }
  1083. if err := r.submitRecvLocked(buf); err != nil {
  1084. logrus.WithError(err).Warn("io_uring recv: failed to resubmit buffer")
  1085. return
  1086. }
  1087. if err := r.submitAndWaitLocked(1, 0); err != nil {
  1088. buf.inFlight.Store(false)
  1089. logrus.WithError(err).Warn("io_uring recv: submit failed during recycle")
  1090. }
  1091. }
  1092. // submitAndWaitLocked submits pending SQEs and optionally waits for completions
  1093. func (r *ioUringRecvState) submitAndWaitLocked(submit, wait uint32) error {
  1094. var flags uintptr
  1095. if wait > 0 {
  1096. flags = ioringEnterGetevents
  1097. }
  1098. for {
  1099. ret, _, errno := unix.Syscall6(unix.SYS_IO_URING_ENTER, uintptr(r.fd), uintptr(submit), uintptr(wait), flags, 0, 0)
  1100. if errno == 0 {
  1101. if wait > 0 && ret > 0 {
  1102. logrus.WithFields(logrus.Fields{
  1103. "completed": ret,
  1104. "submitted": submit,
  1105. }).Debug("io_uring recv: operations completed")
  1106. }
  1107. return nil
  1108. }
  1109. if errno == unix.EINTR {
  1110. continue
  1111. }
  1112. return errno
  1113. }
  1114. }
  1115. // fillRecvQueue fills the submission queue with as many receives as possible
  1116. func (r *ioUringRecvState) fillRecvQueue() error {
  1117. r.mu.Lock()
  1118. defer r.mu.Unlock()
  1119. if r.closed.Load() {
  1120. return fmt.Errorf("ring closed")
  1121. }
  1122. submitted := uint32(0)
  1123. for _, buf := range r.bufferPool {
  1124. if !buf.inFlight.Load() && !buf.inUse.Load() {
  1125. if err := r.submitRecvLocked(buf); err != nil {
  1126. if submitted > 0 {
  1127. break // Queue full, submit what we have
  1128. }
  1129. return err
  1130. }
  1131. submitted++
  1132. }
  1133. }
  1134. if submitted > 0 {
  1135. return r.submitAndWaitLocked(submitted, 0)
  1136. }
  1137. return nil
  1138. }
  1139. // receivePackets processes all completed receives and returns packets
  1140. // Returns a slice of completed packets
  1141. func (r *ioUringRecvState) receivePackets(wait bool) ([]RecvPacket, error) {
  1142. r.mu.Lock()
  1143. defer r.mu.Unlock()
  1144. if r.closed.Load() {
  1145. return nil, fmt.Errorf("ring closed")
  1146. }
  1147. // First submit any pending (to ensure we always have receives queued)
  1148. submitted := uint32(0)
  1149. for _, buf := range r.bufferPool {
  1150. if !buf.inFlight.Load() {
  1151. if err := r.submitRecvLocked(buf); err != nil {
  1152. break // Queue might be full
  1153. }
  1154. submitted++
  1155. }
  1156. }
  1157. waitCount := uint32(0)
  1158. if wait {
  1159. waitCount = 1
  1160. }
  1161. if submitted > 0 || wait {
  1162. if err := r.submitAndWaitLocked(submitted, waitCount); err != nil {
  1163. return nil, err
  1164. }
  1165. }
  1166. // Process completed CQEs
  1167. var packets []RecvPacket
  1168. head := atomic.LoadUint32(r.cqHead)
  1169. tail := atomic.LoadUint32(r.cqTail)
  1170. mask := *r.cqRingMask
  1171. completions := uint32(0)
  1172. errors := 0
  1173. eagains := 0
  1174. for head != tail {
  1175. idx := head & mask
  1176. cqe := &r.cqCqes[idx]
  1177. userData := cqe.UserData
  1178. res := cqe.Res
  1179. flags := cqe.Flags
  1180. head++
  1181. atomic.StoreUint32(r.cqHead, head)
  1182. completions++
  1183. buf, ok := r.bufferMap[userData]
  1184. if !ok {
  1185. logrus.WithField("userData", userData).Warn("io_uring recv: unknown userData in completion")
  1186. continue
  1187. }
  1188. buf.inFlight.Store(false)
  1189. if res < 0 {
  1190. errno := syscall.Errno(-res)
  1191. // EAGAIN is expected for non-blocking - just resubmit
  1192. if errno == unix.EAGAIN {
  1193. eagains++
  1194. } else {
  1195. errors++
  1196. logrus.WithFields(logrus.Fields{
  1197. "userData": userData,
  1198. "errno": errno,
  1199. }).Debug("io_uring recv error")
  1200. }
  1201. continue
  1202. }
  1203. if res == 0 {
  1204. // Connection closed or no data
  1205. continue
  1206. }
  1207. // Successfully received packet
  1208. n := int(res)
  1209. // Copy address into standalone struct
  1210. var from unix.RawSockaddrInet6
  1211. if buf.msghdr.Namelen > 0 && buf.msghdr.Namelen <= uint32(len(buf.nameBuf)) {
  1212. copy((*(*[unix.SizeofSockaddrInet6]byte)(unsafe.Pointer(&from)))[:], buf.nameBuf[:buf.msghdr.Namelen])
  1213. }
  1214. controllen := int(buf.msghdr.Controllen)
  1215. var controlSlice []byte
  1216. if controllen > 0 && controllen <= len(buf.controlBuf) {
  1217. controlSlice = buf.controlBuf[:controllen]
  1218. }
  1219. buf.inUse.Store(true)
  1220. bufferRef := buf
  1221. packets = append(packets, RecvPacket{
  1222. Data: buf.payloadBuf[:n],
  1223. N: n,
  1224. From: &from,
  1225. Flags: flags,
  1226. Control: controlSlice,
  1227. Controllen: controllen,
  1228. RecycleFunc: func() { r.recycleBuffer(bufferRef) },
  1229. })
  1230. }
  1231. return packets, nil
  1232. }
  1233. // Close shuts down the receive ring
  1234. func (r *ioUringRecvState) Close() error {
  1235. if r == nil {
  1236. return nil
  1237. }
  1238. r.closed.Store(true)
  1239. r.mu.Lock()
  1240. defer r.mu.Unlock()
  1241. // Clean up buffers
  1242. for _, buf := range r.bufferPool {
  1243. buf.inFlight.Store(false)
  1244. }
  1245. r.bufferPool = nil
  1246. r.bufferMap = nil
  1247. var err error
  1248. if r.sqesMap != nil {
  1249. if e := unix.Munmap(r.sqesMap); e != nil && err == nil {
  1250. err = e
  1251. }
  1252. r.sqesMap = nil
  1253. }
  1254. if r.sqRing != nil {
  1255. if e := unix.Munmap(r.sqRing); e != nil && err == nil {
  1256. err = e
  1257. }
  1258. r.sqRing = nil
  1259. }
  1260. if r.cqRing != nil && len(r.cqRing) > 0 {
  1261. // Only unmap if it's a separate mapping
  1262. if len(r.cqRing) != len(r.sqRing) || uintptr(unsafe.Pointer(&r.cqRing[0])) != uintptr(unsafe.Pointer(&r.sqRing[0])) {
  1263. if e := unix.Munmap(r.cqRing); e != nil && err == nil {
  1264. err = e
  1265. }
  1266. }
  1267. r.cqRing = nil
  1268. }
  1269. if r.fd >= 0 {
  1270. if e := unix.Close(r.fd); e != nil && err == nil {
  1271. err = e
  1272. }
  1273. r.fd = -1
  1274. }
  1275. return err
  1276. }