github.com/pawelgaczynski/giouring@v0.0.0-20230826085535-69588b89acb9/register.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  	"fmt"
    28  	"os"
    29  	"runtime"
    30  	"syscall"
    31  	"unsafe"
    32  
    33  	"golang.org/x/sys/unix"
    34  )
    35  
    36  func (ring *Ring) doRegisterErrno(opCode uint32, arg unsafe.Pointer, nrArgs uint32) (uint, syscall.Errno) {
    37  	var fd int
    38  
    39  	if ring.intFlags&IntFlagRegRing != 0 {
    40  		opCode |= RegisterUseRegisteredRing
    41  		fd = ring.enterRingFd
    42  	} else {
    43  		fd = ring.ringFd
    44  	}
    45  
    46  	return ring.Register(fd, opCode, arg, nrArgs)
    47  }
    48  
    49  func (ring *Ring) doRegister(opCode uint32, arg unsafe.Pointer, nrArgs uint32) (uint, error) {
    50  	ret, errno := ring.doRegisterErrno(opCode, arg, nrArgs)
    51  	if errno != 0 {
    52  		return 0, os.NewSyscallError("io_uring_register", errno)
    53  	}
    54  
    55  	return ret, nil
    56  }
    57  
    58  // liburing: io_uring_register_buffers_update_tag - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_buffers_update_tag.3.en.html
    59  func (ring *Ring) RegisterBuffersUpdateTag(off uint32, iovecs []syscall.Iovec, tags *uint64, nr uint32) (uint, error) {
    60  	rsrcUpdate := &RsrcUpdate2{
    61  		Offset: off,
    62  		Data:   uint64(uintptr(unsafe.Pointer(&iovecs[0]))),
    63  		Tags:   *tags,
    64  		Nr:     nr,
    65  	}
    66  
    67  	result, err := ring.doRegister(RegisterBuffersUpdate, unsafe.Pointer(rsrcUpdate), uint32(unsafe.Sizeof(*rsrcUpdate)))
    68  	runtime.KeepAlive(rsrcUpdate)
    69  
    70  	return result, err
    71  }
    72  
    73  // liburing: io_uring_register_buffers_tags - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_buffers_tags.3.en.html
    74  func (ring *Ring) RegisterBuffersTags(iovecs []syscall.Iovec, tags []uint64) (uint, error) {
    75  	reg := &RsrcRegister{
    76  		Nr:   uint32(len(tags)),
    77  		Data: uint64(uintptr(unsafe.Pointer(&iovecs[0]))),
    78  		Tags: uint64(uintptr(unsafe.Pointer(&tags[0]))),
    79  	}
    80  
    81  	result, err := ring.doRegister(RegisterBuffers2, unsafe.Pointer(reg), uint32(unsafe.Sizeof(*reg)))
    82  	runtime.KeepAlive(reg)
    83  
    84  	return result, err
    85  }
    86  
    87  // liburing: io_uring_register_buffers_sparse - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_buffers_sparse.3.en.html
    88  func (ring *Ring) RegisterBuffersSparse(nr uint32) (uint, error) {
    89  	reg := &RsrcRegister{
    90  		Flags: RsrcRegisterSparse,
    91  		Nr:    nr,
    92  	}
    93  
    94  	result, err := ring.doRegister(RegisterBuffers2, unsafe.Pointer(reg), uint32(unsafe.Sizeof(*reg)))
    95  	runtime.KeepAlive(reg)
    96  
    97  	return result, err
    98  }
    99  
   100  // liburing: io_uring_register_buffers - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_buffers.3.en.html
   101  func (ring *Ring) RegisterBuffers(iovecs []syscall.Iovec) (uint, error) {
   102  	return ring.doRegister(RegisterBuffers, unsafe.Pointer(&iovecs[0]), uint32(len(iovecs)))
   103  }
   104  
   105  // liburing: io_uring_unregister_buffers - https://manpages.debian.org/unstable/liburing-dev/io_uring_unregister_buffers.3.en.html
   106  func (ring *Ring) UnregisterBuffers() (uint, error) {
   107  	return ring.doRegister(UnregisterBuffers, unsafe.Pointer(nil), 0)
   108  }
   109  
   110  // liburing: io_uring_register_files_update_tag - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_files_update_tag.3.en.html
   111  func (ring *Ring) RegisterFilesUpdateTag(off uint, files []int, tags []uint64) (uint, error) {
   112  	update := &RsrcUpdate2{
   113  		Offset: uint32(off),
   114  		Data:   uint64(uintptr(unsafe.Pointer(&files[0]))),
   115  		Tags:   uint64(uintptr(unsafe.Pointer(&tags[0]))),
   116  		Nr:     uint32(len(files)),
   117  	}
   118  
   119  	result, err := ring.doRegister(RegisterBuffers2, unsafe.Pointer(update), 1)
   120  	runtime.KeepAlive(update)
   121  
   122  	return result, err
   123  }
   124  
   125  // liburing: io_uring_register_files_update - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_files_update.3.en.html
   126  func (ring *Ring) RegisterFilesUpdate(off uint, files []int) (uint, error) {
   127  	update := &FilesUpdate{
   128  		Offset: uint32(off),
   129  		Fds:    uint64(uintptr(unsafe.Pointer(&files[0]))),
   130  	}
   131  
   132  	result, err := ring.doRegister(RegisterFilesUpdate, unsafe.Pointer(update), uint32(len(files)))
   133  	runtime.KeepAlive(update)
   134  
   135  	return result, err
   136  }
   137  
   138  // liburing: increase_rlimit_nofile
   139  func increaseRlimitNofile(nr uint64) error {
   140  	rlim := syscall.Rlimit{}
   141  
   142  	err := syscall.Getrlimit(unix.RLIMIT_NOFILE, &rlim)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	if rlim.Cur < nr {
   148  		rlim.Cur += nr
   149  
   150  		err = syscall.Setrlimit(unix.RLIMIT_NOFILE, &rlim)
   151  		if err != nil {
   152  			return err
   153  		}
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  // liburing: io_uring_register_files_sparse - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_files_sparse.3.en.html
   160  func (ring *Ring) RegisterFilesSparse(nr uint32) (uint, error) {
   161  	reg := &RsrcRegister{
   162  		Flags: RsrcRegisterSparse,
   163  		Nr:    nr,
   164  	}
   165  
   166  	var (
   167  		ret         uint
   168  		err         error
   169  		errno       syscall.Errno
   170  		didIncrease bool
   171  	)
   172  
   173  	for {
   174  		ret, errno = ring.doRegisterErrno(RegisterFiles2, unsafe.Pointer(reg), uint32(unsafe.Sizeof(*reg)))
   175  		if errno != 0 {
   176  			break
   177  		}
   178  
   179  		if errno == syscall.EMFILE && !didIncrease {
   180  			didIncrease = true
   181  
   182  			err = increaseRlimitNofile(uint64(nr))
   183  			if err != nil {
   184  				break
   185  			}
   186  
   187  			continue
   188  		}
   189  
   190  		break
   191  	}
   192  
   193  	return ret, err
   194  }
   195  
   196  // liburing: io_uring_register_files_tags - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_files_tags.3.en.html
   197  func (ring *Ring) RegisterFilesTags(files []int, tags []uint64) (uint, error) {
   198  	nr := len(files)
   199  	reg := &RsrcRegister{
   200  		Nr:   uint32(nr),
   201  		Data: uint64(uintptr(unsafe.Pointer(&files[0]))),
   202  		Tags: uint64(uintptr(unsafe.Pointer(&tags[0]))),
   203  	}
   204  
   205  	var (
   206  		ret         uint
   207  		err         error
   208  		errno       syscall.Errno
   209  		didIncrease bool
   210  	)
   211  
   212  	for {
   213  		ret, errno = ring.doRegisterErrno(RegisterFiles2, unsafe.Pointer(reg), uint32(unsafe.Sizeof(*reg)))
   214  		if ret > 0 {
   215  			break
   216  		}
   217  		if errno == syscall.EMFILE && !didIncrease {
   218  			didIncrease = true
   219  			err = increaseRlimitNofile(uint64(nr))
   220  			if err != nil {
   221  				break
   222  			}
   223  
   224  			continue
   225  		}
   226  
   227  		break
   228  	}
   229  
   230  	return ret, err
   231  }
   232  
   233  // liburing: io_uring_register_files - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_files.3.en.html
   234  func (ring *Ring) RegisterFiles(files []int) (uint, error) {
   235  	var (
   236  		ret         uint
   237  		err         error
   238  		errno       syscall.Errno
   239  		didIncrease bool
   240  	)
   241  
   242  	for {
   243  		ret, errno = ring.doRegisterErrno(RegisterFiles, unsafe.Pointer(&files[0]), uint32(len(files)))
   244  		if ret > 0 {
   245  			break
   246  		}
   247  		if errno == syscall.EMFILE && !didIncrease {
   248  			didIncrease = true
   249  			err = increaseRlimitNofile(uint64(len(files)))
   250  			if err != nil {
   251  				break
   252  			}
   253  
   254  			continue
   255  		}
   256  
   257  		break
   258  	}
   259  
   260  	return ret, err
   261  }
   262  
   263  // liburing: io_uring_unregister_files - https://manpages.debian.org/unstable/liburing-dev/io_uring_unregister_files.3.en.html
   264  func (ring *Ring) UnregisterFiles() (uint, error) {
   265  	return ring.doRegister(UnregisterFiles, unsafe.Pointer(nil), 0)
   266  }
   267  
   268  // liburing: io_uring_register_eventfd - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_eventfd.3.en.html
   269  func (ring *Ring) RegisterEventFd(fd int) (uint, error) {
   270  	return ring.doRegister(RegisterEventFD, unsafe.Pointer(&fd), 1)
   271  }
   272  
   273  // liburing: io_uring_unregister_eventfd - https://manpages.debian.org/unstable/liburing-dev/io_uring_unregister_eventfd.3.en.html
   274  func (ring *Ring) UnregisterEventFd(fd int) (uint, error) {
   275  	return ring.doRegister(UnregisterEventFD, unsafe.Pointer(&fd), 1)
   276  }
   277  
   278  // liburing: io_uring_register_eventfd_async - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_eventfd_async.3.en.html
   279  func (ring *Ring) RegisterEventFdAsync(fd int) (uint, error) {
   280  	return ring.doRegister(RegisterEventFDAsync, unsafe.Pointer(&fd), 1)
   281  }
   282  
   283  // liburing: io_uring_register_probe
   284  func (ring *Ring) RegisterProbe(probe *Probe, nrOps int) (uint, error) {
   285  	result, err := ring.doRegister(RegisterProbe, unsafe.Pointer(probe), uint32(nrOps))
   286  	runtime.KeepAlive(probe)
   287  
   288  	return result, err
   289  }
   290  
   291  // liburing: io_uring_register_personality
   292  func (ring *Ring) RegisterPersonality() (uint, error) {
   293  	return ring.doRegister(RegisterPersonality, unsafe.Pointer(nil), 0)
   294  }
   295  
   296  // liburing: io_uring_unregister_personality
   297  func (ring *Ring) UnregisterPersonality() (uint, error) {
   298  	return ring.doRegister(UnregisterPersonality, unsafe.Pointer(nil), 0)
   299  }
   300  
   301  // liburing: io_uring_register_restrictions
   302  func (ring *Ring) RegisterRestrictions(res []Restriction) (uint, error) {
   303  	return ring.doRegister(RegisterRestrictions, unsafe.Pointer(&res[0]), uint32(len(res)))
   304  }
   305  
   306  // liburing: io_uring_enable_rings
   307  func (ring *Ring) EnableRings() (uint, error) {
   308  	return ring.doRegister(RegisterEnableRings, unsafe.Pointer(nil), 0)
   309  }
   310  
   311  // liburing: io_uring_register_iowq_aff - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_iowq_aff.3.en.html
   312  func (ring *Ring) RegisterIOWQAff(cpusz uint64, mask *unix.CPUSet) error {
   313  	if cpusz >= 1<<31 {
   314  		return syscall.EINVAL
   315  	}
   316  	_, err := ring.doRegister(RegisterIOWQAff, unsafe.Pointer(mask), uint32(cpusz))
   317  
   318  	runtime.KeepAlive(mask)
   319  
   320  	return err
   321  }
   322  
   323  // liburing: io_uring_unregister_iowq_aff - https://manpages.debian.org/unstable/liburing-dev/io_uring_unregister_iowq_aff.3.en.html
   324  func (ring *Ring) UnregisterIOWQAff() (uint, error) {
   325  	return ring.doRegister(UnregisterIOWQAff, unsafe.Pointer(nil), 0)
   326  }
   327  
   328  const iowqMaxWorkersNrArgs = 2
   329  
   330  // liburing: io_uring_register_iowq_max_workers - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_iowq_max_workers.3.en.html
   331  func (ring *Ring) RegisterIOWQMaxWorkers(val []uint) (uint, error) {
   332  	return ring.doRegister(RegisterIOWQMaxWorkers, unsafe.Pointer(&val[0]), iowqMaxWorkersNrArgs)
   333  }
   334  
   335  // liburing: io_uring_register_ring_fd - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_ring_fd.3.en.html
   336  func (ring *Ring) RegisterRingFd() (uint, error) {
   337  	if (ring.intFlags & IntFlagRegRing) != 0 {
   338  		return 0, syscall.EEXIST
   339  	}
   340  
   341  	rsrcUpdate := &RsrcUpdate{
   342  		Data:   uint64(ring.ringFd),
   343  		Offset: registerRingFdOffset,
   344  	}
   345  
   346  	ret, err := ring.doRegister(RegisterRingFDs, unsafe.Pointer(rsrcUpdate), 1)
   347  	if err != nil {
   348  		return ret, err
   349  	}
   350  
   351  	if ret == 1 {
   352  		ring.enterRingFd = int(rsrcUpdate.Offset)
   353  		ring.intFlags |= IntFlagRegRing
   354  
   355  		if ring.features&FeatRegRegRing != 0 {
   356  			ring.intFlags |= IntFlagRegRegRing
   357  		}
   358  	} else {
   359  		return ret, fmt.Errorf("unexpected return from ring.Register: %d", ret)
   360  	}
   361  
   362  	return ret, nil
   363  }
   364  
   365  // liburing: io_uring_unregister_ring_fd - https://manpages.debian.org/unstable/liburing-dev/io_uring_unregister_ring_fd.3.en.html
   366  func (ring *Ring) UnregisterRingFd() (uint, error) {
   367  	rsrcUpdate := &RsrcUpdate{
   368  		Offset: uint32(ring.enterRingFd),
   369  	}
   370  
   371  	if (ring.intFlags & IntFlagRegRing) != 0 {
   372  		return 0, syscall.EINVAL
   373  	}
   374  
   375  	ret, err := ring.doRegister(UnregisterRingFDs, unsafe.Pointer(rsrcUpdate), 1)
   376  	if err != nil {
   377  		return ret, err
   378  	}
   379  
   380  	if ret == 1 {
   381  		ring.enterRingFd = ring.ringFd
   382  		ring.intFlags &= ^(IntFlagRegRing | IntFlagRegRegRing)
   383  	}
   384  
   385  	return ret, nil
   386  }
   387  
   388  // liburing: io_uring_close_ring_fd - https://manpages.debian.org/unstable/liburing-dev/io_uring_close_ring_fd.3.en.html
   389  func (ring *Ring) CloseRingFd() (uint, error) {
   390  	if ring.features&FeatRegRegRing == 0 {
   391  		return 0, syscall.EOPNOTSUPP
   392  	}
   393  
   394  	if (ring.intFlags & IntFlagRegRing) == 0 {
   395  		return 0, syscall.EINVAL
   396  	}
   397  
   398  	if ring.ringFd == -1 {
   399  		return 0, syscall.EBADF
   400  	}
   401  
   402  	syscall.Close(ring.ringFd)
   403  	ring.ringFd = -1
   404  
   405  	return 1, nil
   406  }
   407  
   408  // liburing: io_uring_register_buf_ring - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_buf_ring.3.en.html
   409  func (ring *Ring) RegisterBufferRing(reg *BufReg, _ uint32) (uint, error) {
   410  	result, err := ring.doRegister(RegisterPbufRing, unsafe.Pointer(reg), 1)
   411  	runtime.KeepAlive(reg)
   412  
   413  	return result, err
   414  }
   415  
   416  // liburing: io_uring_unregister_buf_ring - https://manpages.debian.org/unstable/liburing-dev/io_uring_unregister_buf_ring.3.en.html
   417  func (ring *Ring) UnregisterBufferRing(bgid int) (uint, error) {
   418  	reg := &BufReg{
   419  		Bgid: uint16(bgid),
   420  	}
   421  	result, err := ring.doRegister(UnregisterPbufRing, unsafe.Pointer(reg), 1)
   422  	runtime.KeepAlive(reg)
   423  
   424  	return result, err
   425  }
   426  
   427  // liburing: io_uring_register_sync_cancel - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_sync_cancel.3.en.html
   428  func (ring *Ring) RegisterSyncCancel(reg *SyncCancelReg) (uint, error) {
   429  	return ring.doRegister(RegisterSyncCancel, unsafe.Pointer(reg), 1)
   430  }
   431  
   432  // liburing: io_uring_register_file_alloc_range - https://manpages.debian.org/unstable/liburing-dev/io_uring_register_file_alloc_range.3.en.html
   433  func (ring *Ring) RegisterFileAllocRange(off, length uint32) (uint, error) {
   434  	fileRange := &FileIndexRange{
   435  		Off: off,
   436  		Len: length,
   437  	}
   438  
   439  	result, err := ring.doRegister(RegisterFileAllocRange, unsafe.Pointer(fileRange), 0)
   440  	runtime.KeepAlive(fileRange)
   441  
   442  	return result, err
   443  }