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  }