github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/iouringfs/iouringfs.go (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package iouringfs provides a filesystem implementation for IO_URING basing
    16  // it on anonfs. Currently, we don't support neither IOPOLL nor SQPOLL modes.
    17  // Thus, user needs to set up IO_URING first with io_uring_setup(2) syscall and
    18  // then issue submission request using io_uring_enter(2).
    19  //
    20  // Another important note, as of now, we don't support deferred CQE. In other
    21  // words, the size of the backlogged set of CQE is zero. Whenever, completion
    22  // queue ring buffer is full, we drop the subsequent completion queue entries.
    23  package iouringfs
    24  
    25  import (
    26  	"fmt"
    27  	"io"
    28  
    29  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    30  	"github.com/nicocha30/gvisor-ligolo/pkg/atomicbitops"
    31  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    32  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    33  	"github.com/nicocha30/gvisor-ligolo/pkg/hostarch"
    34  	"github.com/nicocha30/gvisor-ligolo/pkg/safemem"
    35  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel"
    36  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/memmap"
    37  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/pgalloc"
    38  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/usage"
    39  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs"
    40  	"github.com/nicocha30/gvisor-ligolo/pkg/usermem"
    41  )
    42  
    43  // FileDescription implements vfs.FileDescriptionImpl for file-based IO_URING.
    44  // It is based on io_rings struct. See io_uring/io_uring.c.
    45  //
    46  // +stateify savable
    47  type FileDescription struct {
    48  	vfsfd vfs.FileDescription
    49  	vfs.FileDescriptionDefaultImpl
    50  	vfs.DentryMetadataFileDescriptionImpl
    51  	vfs.NoLockFD
    52  
    53  	mfp pgalloc.MemoryFileProvider
    54  
    55  	rbmf  ringsBufferFile
    56  	sqemf sqEntriesFile
    57  
    58  	// running indicates whether the submission queue is currently being
    59  	// processed. This is either 0 for not running, or 1 for running.
    60  	running atomicbitops.Uint32
    61  	// runC is used to wake up serialized task goroutines waiting for any
    62  	// concurrent processors of the submisison queue.
    63  	runC chan struct{} `state:"nosave"`
    64  
    65  	ioRings linux.IORings
    66  
    67  	ioRingsBuf sharedBuffer `state:"nosave"`
    68  	sqesBuf    sharedBuffer `state:"nosave"`
    69  	cqesBuf    sharedBuffer `state:"nosave"`
    70  
    71  	// remap indicates whether the shared buffers need to be remapped
    72  	// due to a S/R. Protected by ProcessSubmissions critical section.
    73  	remap bool
    74  }
    75  
    76  var _ vfs.FileDescriptionImpl = (*FileDescription)(nil)
    77  
    78  func roundUpPowerOfTwo(n uint32) (uint32, bool) {
    79  	if n > (1 << 31) {
    80  		return 0, false
    81  	}
    82  	result := uint32(1)
    83  	for result < n {
    84  		result = result << 1
    85  	}
    86  	return result, true
    87  }
    88  
    89  // New creates a new iouring fd.
    90  func New(ctx context.Context, vfsObj *vfs.VirtualFilesystem, entries uint32, params *linux.IOUringParams) (*vfs.FileDescription, error) {
    91  	if entries > linux.IORING_MAX_ENTRIES {
    92  		return nil, linuxerr.EINVAL
    93  	}
    94  
    95  	vd := vfsObj.NewAnonVirtualDentry("[io_uring]")
    96  	defer vd.DecRef(ctx)
    97  
    98  	mfp := pgalloc.MemoryFileProviderFromContext(ctx)
    99  	if mfp == nil {
   100  		panic(fmt.Sprintf("context.Context %T lacks non-nil value for key %T", ctx, pgalloc.CtxMemoryFileProvider))
   101  	}
   102  
   103  	numSqEntries, ok := roundUpPowerOfTwo(entries)
   104  	if !ok {
   105  		return nil, linuxerr.EOVERFLOW
   106  	}
   107  	var numCqEntries uint32
   108  	if params.Flags&linux.IORING_SETUP_CQSIZE != 0 {
   109  		var ok bool
   110  		numCqEntries, ok = roundUpPowerOfTwo(params.CqEntries)
   111  		if !ok || numCqEntries < numSqEntries || numCqEntries > linux.IORING_MAX_CQ_ENTRIES {
   112  			return nil, linuxerr.EINVAL
   113  		}
   114  	} else {
   115  		numCqEntries = 2 * numSqEntries
   116  	}
   117  
   118  	// Allocate enough space to store the `struct io_rings` plus a given number of indexes
   119  	// corresponding to the number of SQEs.
   120  	ioRingsWithCqesSize := uint32((*linux.IORings)(nil).SizeBytes()) +
   121  		numCqEntries*uint32((*linux.IOUringCqe)(nil).SizeBytes())
   122  	ringsBufferSize := uint64(ioRingsWithCqesSize +
   123  		numSqEntries*uint32((*linux.IORingIndex)(nil).SizeBytes()))
   124  	ringsBufferSize = uint64(hostarch.Addr(ringsBufferSize).MustRoundUp())
   125  
   126  	mf := mfp.MemoryFile()
   127  	memCgID := pgalloc.MemoryCgroupIDFromContext(ctx)
   128  	rbfr, err := mf.Allocate(ringsBufferSize, pgalloc.AllocOpts{Kind: usage.Anonymous, MemCgID: memCgID})
   129  	if err != nil {
   130  		return nil, linuxerr.ENOMEM
   131  	}
   132  
   133  	// Allocate enough space to store the given number of submission queue entries.
   134  	sqEntriesSize := uint64(numSqEntries * uint32((*linux.IOUringSqe)(nil).SizeBytes()))
   135  	sqEntriesSize = uint64(hostarch.Addr(sqEntriesSize).MustRoundUp())
   136  	sqefr, err := mf.Allocate(sqEntriesSize, pgalloc.AllocOpts{Kind: usage.Anonymous, MemCgID: memCgID})
   137  	if err != nil {
   138  		return nil, linuxerr.ENOMEM
   139  	}
   140  
   141  	iouringfd := &FileDescription{
   142  		mfp: mfp,
   143  		rbmf: ringsBufferFile{
   144  			fr: rbfr,
   145  		},
   146  		sqemf: sqEntriesFile{
   147  			fr: sqefr,
   148  		},
   149  		// See ProcessSubmissions for why the capacity is 1.
   150  		runC: make(chan struct{}, 1),
   151  	}
   152  
   153  	// iouringfd is always set up with read/write mode.
   154  	// See io_uring/io_uring.c:io_uring_install_fd().
   155  	if err := iouringfd.vfsfd.Init(iouringfd, uint32(linux.O_RDWR), vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{
   156  		UseDentryMetadata: true,
   157  		DenyPRead:         true,
   158  		DenyPWrite:        true,
   159  		DenySpliceIn:      true,
   160  	}); err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	params.SqEntries = numSqEntries
   165  	params.CqEntries = numCqEntries
   166  
   167  	arrayOffset := uint64(hostarch.Addr(ioRingsWithCqesSize))
   168  	arrayOffset, ok = hostarch.CacheLineRoundUp(arrayOffset)
   169  	if !ok {
   170  		return nil, linuxerr.EOVERFLOW
   171  	}
   172  
   173  	params.SqOff = linux.PreComputedIOSqRingOffsets()
   174  	params.SqOff.Array = uint32(arrayOffset)
   175  
   176  	cqesOffset := uint64(hostarch.Addr((*linux.IORings)(nil).SizeBytes()))
   177  	cqesOffset, ok = hostarch.CacheLineRoundUp(cqesOffset)
   178  	if !ok {
   179  		return nil, linuxerr.EOVERFLOW
   180  	}
   181  
   182  	params.CqOff = linux.PreComputedIOCqRingOffsets()
   183  	params.CqOff.Cqes = uint32(cqesOffset)
   184  
   185  	// Set features supported by the current IO_URING implementation.
   186  	params.Features = linux.IORING_FEAT_SINGLE_MMAP
   187  
   188  	// Map all shared buffers.
   189  	if err := iouringfd.mapSharedBuffers(); err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	// Initialize IORings struct from params.
   194  	iouringfd.ioRings.SqRingMask = params.SqEntries - 1
   195  	iouringfd.ioRings.CqRingMask = params.CqEntries - 1
   196  	iouringfd.ioRings.SqRingEntries = params.SqEntries
   197  	iouringfd.ioRings.CqRingEntries = params.CqEntries
   198  
   199  	// Write IORings out to shared buffer.
   200  	view, err := iouringfd.ioRingsBuf.view(iouringfd.ioRings.SizeBytes())
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	iouringfd.ioRings.MarshalUnsafe(view)
   205  
   206  	buf := make([]byte, iouringfd.ioRings.SizeBytes())
   207  	iouringfd.ioRings.MarshalUnsafe(buf)
   208  
   209  	if _, err := iouringfd.ioRingsBuf.writeback(iouringfd.ioRings.SizeBytes()); err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	return &iouringfd.vfsfd, nil
   214  }
   215  
   216  // Release implements vfs.FileDescriptionImpl.Release.
   217  func (fd *FileDescription) Release(ctx context.Context) {
   218  	mf := pgalloc.MemoryFileProviderFromContext(ctx).MemoryFile()
   219  	mf.DecRef(fd.rbmf.fr)
   220  	mf.DecRef(fd.sqemf.fr)
   221  }
   222  
   223  // mapSharedBuffers caches internal mappings for the ring's shared memory
   224  // regions.
   225  func (fd *FileDescription) mapSharedBuffers() error {
   226  	mf := fd.mfp.MemoryFile()
   227  
   228  	// Mapping for the IORings header struct.
   229  	rb, err := mf.MapInternal(fd.rbmf.fr, hostarch.ReadWrite)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	fd.ioRingsBuf.init(rb)
   234  
   235  	// Mapping for the CQEs array. This is contiguous to the header struct.
   236  	cqesOffset := uint64(fd.ioRings.SizeBytes())
   237  	cqesOffset, ok := hostarch.CacheLineRoundUp(cqesOffset)
   238  	if !ok {
   239  		return linuxerr.EOVERFLOW
   240  	}
   241  	cqes := rb.DropFirst(int(cqesOffset))
   242  	fd.cqesBuf.init(cqes)
   243  
   244  	// Mapping for the SQEs array.
   245  	sqes, err := mf.MapInternal(fd.sqemf.fr, hostarch.ReadWrite)
   246  	if err != nil {
   247  		return err
   248  	}
   249  	fd.sqesBuf.init(sqes)
   250  
   251  	return nil
   252  
   253  }
   254  
   255  // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
   256  func (fd *FileDescription) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
   257  	var mf memmap.Mappable
   258  	switch opts.Offset {
   259  	case linux.IORING_OFF_SQ_RING, linux.IORING_OFF_CQ_RING:
   260  		mf = &fd.rbmf
   261  	case linux.IORING_OFF_SQES:
   262  		mf = &fd.sqemf
   263  	default:
   264  		return linuxerr.EINVAL
   265  	}
   266  
   267  	opts.Offset = 0
   268  
   269  	return vfs.GenericConfigureMMap(&fd.vfsfd, mf, opts)
   270  }
   271  
   272  // ProcessSubmissions processes the submission queue. Concurrent calls to
   273  // ProcessSubmissions serialize, yielding task goroutines with Task.Block since
   274  // processing can take a long time.
   275  func (fd *FileDescription) ProcessSubmissions(t *kernel.Task, toSubmit uint32, minComplete uint32, flags uint32) (int, error) {
   276  	// We use a combination of fd.running and fd.runC to serialize concurrent
   277  	// callers to ProcessSubmissions. runC has a capacity of 1. The protocol
   278  	// works as follows:
   279  	//
   280  	// * Becoming the active task
   281  	//
   282  	// On entry to ProcessSubmissions, we try to transition running from 0 to
   283  	// 1. If there is already an active task, this will fail and we'll go to
   284  	// sleep with Task.Block(). If we succeed, we're the active task.
   285  	//
   286  	// * Sleep, Wakeup
   287  	//
   288  	// If we had to sleep, on wakeup we try to transition running to 1 again as
   289  	// we could still be racing with other tasks. Note that if multiple tasks
   290  	// are sleeping, only one will wake up since only one will successfully
   291  	// receive from runC. However we could still race with a new caller of
   292  	// ProcessSubmissions that hasn't gone to sleep yet. Only one waiting task
   293  	// will succeed and become the active task, the rest will go to sleep.
   294  	//
   295  	// runC needs to be buffered to avoid a race between checking running and
   296  	// going back to sleep. With an unbuffered channel, we could miss a wakeup
   297  	// like this:
   298  	//
   299  	// Task B (entering, sleeping)                        | Task A (active, releasing)
   300  	// ---------------------------------------------------+-------------------------
   301  	//                                                    | fd.running.Store(0)
   302  	// for !fd.running.CompareAndSwap(0, 1) { // Succeess |
   303  	//                                                    | nonblockingSend(runC) // Missed!
   304  	//     t.Block(fd.runC) // Will block forever         |
   305  	// }
   306  	//
   307  	// Task A's send would have to be non-blocking, as there may not be a
   308  	// concurrent Task B.
   309  	//
   310  	// A side-effect of using a buffered channel is the first task that needs to
   311  	// sleep may wake up once immediately due to a previously queued
   312  	// wakeup. This isn't a problem, as it'll immediately try to transition
   313  	// running to 1, likely fail again and go back to sleep. Task.Block has a
   314  	// fast path if runC already has a queued message so this won't result in a
   315  	// task state change.
   316  	//
   317  	// * Release
   318  	//
   319  	// When the active task is done, it releases the critical section by setting
   320  	// running = 0, then doing a non-blocking send on runC. The send needs to be
   321  	// non-blocking, as there may not be a concurrent sleeper.
   322  	for !fd.running.CompareAndSwap(0, 1) {
   323  		t.Block(fd.runC)
   324  	}
   325  	// We successfully set fd.running, so we're the active task now.
   326  	defer func() {
   327  		// Unblock any potentially waiting tasks.
   328  		if !fd.running.CompareAndSwap(1, 0) {
   329  			panic(fmt.Sprintf("iouringfs.FileDescription.ProcessSubmissions: active task encountered invalid fd.running state %v", fd.running.Load()))
   330  		}
   331  		select {
   332  		case fd.runC <- struct{}{}:
   333  		default:
   334  		}
   335  	}()
   336  
   337  	// The rest of this function is a critical section with respect to
   338  	// concurrent callers.
   339  
   340  	if fd.remap {
   341  		fd.mapSharedBuffers()
   342  		fd.remap = false
   343  	}
   344  
   345  	var err error
   346  	var sqe linux.IOUringSqe
   347  
   348  	sqOff := linux.PreComputedIOSqRingOffsets()
   349  	cqOff := linux.PreComputedIOCqRingOffsets()
   350  	sqArraySize := sqe.SizeBytes() * int(fd.ioRings.SqRingEntries)
   351  	cqArraySize := (*linux.IOUringCqe)(nil).SizeBytes() * int(fd.ioRings.CqRingEntries)
   352  
   353  	// Fetch all buffers initially.
   354  	fetchRB := true
   355  	fetchSQA := true
   356  	fetchCQA := true
   357  
   358  	var view, sqaView, cqaView []byte
   359  	submitted := uint32(0)
   360  
   361  	for toSubmit > submitted {
   362  		// This loop can take a long time to process, so periodically check for
   363  		// interrupts. This also pets the watchdog.
   364  		if t.Interrupted() {
   365  			return -1, linuxerr.EINTR
   366  		}
   367  
   368  		if fetchRB {
   369  			view, err = fd.ioRingsBuf.view(fd.ioRings.SizeBytes())
   370  			if err != nil {
   371  				return -1, err
   372  			}
   373  		}
   374  
   375  		// Note: The kernel uses sqHead as a cursor and writes cqTail. Userspace
   376  		// uses cqHead as a cursor and writes sqTail.
   377  
   378  		sqHeadPtr := atomicUint32AtOffset(view, int(sqOff.Head))
   379  		sqTailPtr := atomicUint32AtOffset(view, int(sqOff.Tail))
   380  		cqHeadPtr := atomicUint32AtOffset(view, int(cqOff.Head))
   381  		cqTailPtr := atomicUint32AtOffset(view, int(cqOff.Tail))
   382  		overflowPtr := atomicUint32AtOffset(view, int(cqOff.Overflow))
   383  
   384  		// Load the pointers once, so we work with a stable value. Particularly,
   385  		// usersapce can update the SQ tail at any time.
   386  		sqHead := sqHeadPtr.Load()
   387  		sqTail := sqTailPtr.Load()
   388  
   389  		// Is the submission queue is empty?
   390  		if sqHead == sqTail {
   391  			return int(submitted), nil
   392  		}
   393  
   394  		// We have at least one pending sqe, unmarshal the first from the
   395  		// submission queue.
   396  		if fetchSQA {
   397  			sqaView, err = fd.sqesBuf.view(sqArraySize)
   398  			if err != nil {
   399  				return -1, err
   400  			}
   401  		}
   402  		sqaOff := int(sqHead&fd.ioRings.SqRingMask) * sqe.SizeBytes()
   403  		sqe.UnmarshalUnsafe(sqaView[sqaOff : sqaOff+sqe.SizeBytes()])
   404  		fetchSQA = fd.sqesBuf.drop()
   405  
   406  		// Dispatch request from unmarshalled entry.
   407  		cqe := fd.ProcessSubmission(t, &sqe, flags)
   408  
   409  		// Advance sq head.
   410  		sqHeadPtr.Add(1)
   411  
   412  		// Load once so we have stable values. Particularly, userspace can
   413  		// update the CQ head at any time.
   414  		cqHead := cqHeadPtr.Load()
   415  		cqTail := cqTailPtr.Load()
   416  
   417  		// Marshal response to completion queue.
   418  		if (cqTail - cqHead) >= fd.ioRings.CqRingEntries {
   419  			// CQ ring full.
   420  			fd.ioRings.CqOverflow++
   421  			overflowPtr.Store(fd.ioRings.CqOverflow)
   422  		} else {
   423  			// Have room in CQ, marshal CQE.
   424  			if fetchCQA {
   425  				cqaView, err = fd.cqesBuf.view(cqArraySize)
   426  				if err != nil {
   427  					return -1, err
   428  				}
   429  			}
   430  			cqaOff := int(cqTail&fd.ioRings.CqRingMask) * cqe.SizeBytes()
   431  			cqe.MarshalUnsafe(cqaView[cqaOff : cqaOff+cqe.SizeBytes()])
   432  			fetchCQA, err = fd.cqesBuf.writebackWindow(cqaOff, cqe.SizeBytes())
   433  			if err != nil {
   434  				return -1, err
   435  			}
   436  
   437  			// Advance cq tail.
   438  			cqTailPtr.Add(1)
   439  		}
   440  
   441  		fetchRB, err = fd.ioRingsBuf.writeback(fd.ioRings.SizeBytes())
   442  		if err != nil {
   443  			return -1, err
   444  		}
   445  
   446  		submitted++
   447  	}
   448  
   449  	return int(submitted), nil
   450  }
   451  
   452  // ProcessSubmission processes a single submission request.
   453  func (fd *FileDescription) ProcessSubmission(t *kernel.Task, sqe *linux.IOUringSqe, flags uint32) *linux.IOUringCqe {
   454  	var (
   455  		cqeErr   error
   456  		cqeFlags uint32
   457  		retValue int32
   458  	)
   459  
   460  	switch op := sqe.Opcode; op {
   461  	case linux.IORING_OP_NOP:
   462  		// For the NOP operation, we don't do anything special.
   463  	case linux.IORING_OP_READV:
   464  		retValue, cqeErr = fd.handleReadv(t, sqe, flags)
   465  		if cqeErr == io.EOF {
   466  			// Don't raise EOF as errno, error translation will fail. Short
   467  			// reads aren't failures.
   468  			cqeErr = nil
   469  		}
   470  	default: // Unsupported operation
   471  		retValue = -int32(linuxerr.EINVAL.Errno())
   472  	}
   473  
   474  	if cqeErr != nil {
   475  		retValue = -int32(kernel.ExtractErrno(cqeErr, -1))
   476  	}
   477  
   478  	return &linux.IOUringCqe{
   479  		UserData: sqe.UserData,
   480  		Res:      retValue,
   481  		Flags:    cqeFlags,
   482  	}
   483  }
   484  
   485  // handleReadv handles IORING_OP_READV.
   486  func (fd *FileDescription) handleReadv(t *kernel.Task, sqe *linux.IOUringSqe, flags uint32) (int32, error) {
   487  	// Check that a file descriptor is valid.
   488  	if sqe.Fd < 0 {
   489  		return 0, linuxerr.EBADF
   490  	}
   491  	// Currently we don't support any flags for the SQEs.
   492  	if sqe.Flags != 0 {
   493  		return 0, linuxerr.EINVAL
   494  	}
   495  	// If the file is not seekable then offset must be zero. And currently, we don't support them.
   496  	if sqe.OffOrAddrOrCmdOp != 0 {
   497  		return 0, linuxerr.EINVAL
   498  	}
   499  	// ioprio should not be set for the READV operation.
   500  	if sqe.IoPrio != 0 {
   501  		return 0, linuxerr.EINVAL
   502  	}
   503  
   504  	// AddressSpaceActive is set to true as we are doing this from the task goroutine.And this is a
   505  	// case as we currently don't support neither IOPOLL nor SQPOLL modes.
   506  	dst, err := t.IovecsIOSequence(hostarch.Addr(sqe.AddrOrSpliceOff), int(sqe.Len), usermem.IOOpts{
   507  		AddressSpaceActive: true,
   508  	})
   509  	if err != nil {
   510  		return 0, err
   511  	}
   512  	file := t.GetFile(sqe.Fd)
   513  	if file == nil {
   514  		return 0, linuxerr.EBADF
   515  	}
   516  	defer file.DecRef(t)
   517  	n, err := file.PRead(t, dst, 0, vfs.ReadOptions{})
   518  	if err != nil {
   519  		return 0, err
   520  	}
   521  
   522  	return int32(n), nil
   523  }
   524  
   525  // updateCq updates a completion queue by adding a given completion queue entry.
   526  func (fd *FileDescription) updateCq(cqes *safemem.BlockSeq, cqe *linux.IOUringCqe, cqTail uint32) error {
   527  	cqeSize := uint32((*linux.IOUringCqe)(nil).SizeBytes())
   528  	if cqes.NumBlocks() == 1 && !cqes.Head().NeedSafecopy() {
   529  		cqe.MarshalBytes(cqes.Head().ToSlice()[cqTail*cqeSize : (cqTail+1)*cqeSize])
   530  
   531  		return nil
   532  	}
   533  
   534  	buf := make([]byte, cqes.NumBytes())
   535  	cqe.MarshalBytes(buf)
   536  	cp, cperr := safemem.CopySeq(cqes.DropFirst64(uint64(cqTail*cqeSize)), safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf)))
   537  	if cp == 0 {
   538  		return cperr
   539  	}
   540  
   541  	return nil
   542  }
   543  
   544  // sqEntriesFile implements memmap.Mappable for SQ entries.
   545  //
   546  // +stateify savable
   547  type sqEntriesFile struct {
   548  	fr memmap.FileRange
   549  }
   550  
   551  // AddMapping implements memmap.Mappable.AddMapping.
   552  func (sqemf *sqEntriesFile) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
   553  	return nil
   554  }
   555  
   556  // RemoveMapping implements memmap.Mappable.RemoveMapping.
   557  func (sqemf *sqEntriesFile) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
   558  }
   559  
   560  // CopyMapping implements memmap.Mappable.CopyMapping.
   561  func (sqemf *sqEntriesFile) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
   562  	return nil
   563  }
   564  
   565  // Translate implements memmap.Mappable.Translate.
   566  func (sqemf *sqEntriesFile) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
   567  	if required.End > sqemf.fr.Length() {
   568  		return nil, &memmap.BusError{linuxerr.EFAULT}
   569  	}
   570  
   571  	if source := optional.Intersect(memmap.MappableRange{0, sqemf.fr.Length()}); source.Length() != 0 {
   572  		return []memmap.Translation{
   573  			{
   574  				Source: source,
   575  				File:   pgalloc.MemoryFileProviderFromContext(ctx).MemoryFile(),
   576  				Offset: sqemf.fr.Start + source.Start,
   577  				Perms:  at,
   578  			},
   579  		}, nil
   580  	}
   581  
   582  	return nil, linuxerr.EFAULT
   583  }
   584  
   585  // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
   586  func (sqemf *sqEntriesFile) InvalidateUnsavable(ctx context.Context) error {
   587  	return nil
   588  }
   589  
   590  // ringBuffersFile implements memmap.Mappable for SQ and CQ ring buffers.
   591  //
   592  // +stateify savable
   593  type ringsBufferFile struct {
   594  	fr memmap.FileRange
   595  }
   596  
   597  // AddMapping implements memmap.Mappable.AddMapping.
   598  func (rbmf *ringsBufferFile) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
   599  	return nil
   600  }
   601  
   602  // RemoveMapping implements memmap.Mappable.RemoveMapping.
   603  func (rbmf *ringsBufferFile) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
   604  }
   605  
   606  // CopyMapping implements memmap.Mappable.CopyMapping.
   607  func (rbmf *ringsBufferFile) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
   608  	return nil
   609  }
   610  
   611  // Translate implements memmap.Mappable.Translate.
   612  func (rbmf *ringsBufferFile) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
   613  	if required.End > rbmf.fr.Length() {
   614  		return nil, &memmap.BusError{linuxerr.EFAULT}
   615  	}
   616  
   617  	if source := optional.Intersect(memmap.MappableRange{0, rbmf.fr.Length()}); source.Length() != 0 {
   618  		return []memmap.Translation{
   619  			{
   620  				Source: source,
   621  				File:   pgalloc.MemoryFileProviderFromContext(ctx).MemoryFile(),
   622  				Offset: rbmf.fr.Start + source.Start,
   623  				Perms:  at,
   624  			},
   625  		}, nil
   626  	}
   627  
   628  	return nil, linuxerr.EFAULT
   629  }
   630  
   631  // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
   632  func (rbmf *ringsBufferFile) InvalidateUnsavable(ctx context.Context) error {
   633  	return nil
   634  }