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