github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/xdp/umem.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 //go:build amd64 || arm64 16 // +build amd64 arm64 17 18 package xdp 19 20 import ( 21 "fmt" 22 23 "golang.org/x/sys/unix" 24 "github.com/sagernet/gvisor/pkg/sync" 25 ) 26 27 // TODO(b/240191988): There's some kind of memory corruption bug that occurs 28 // occasionally. This occurred even before TX was supported. 29 30 // TODO(b/240191988): We can hold locks for less time if we accept a more 31 // obtuse API. For example, CompletionQueue.FreeAll doesn't need to hold a 32 // mutex for its entire duration. 33 34 // UMEM is the shared memory area that the kernel and userspace put packets in. 35 type UMEM struct { 36 // mem is the mmap'd area shared with the kernel. 37 mem []byte 38 39 // sockfd is the underlying AF_XDP socket. 40 sockfd uint32 41 42 // frameMask masks the lower bits of an adderess to get the frame's 43 // address. 44 frameMask uint64 45 46 // mu protects frameAddresses and nFreeFrames. 47 mu sync.Mutex 48 49 // frameAddresses is a stack of available frame addresses. 50 // +checklocks:mu 51 frameAddresses []uint64 52 53 // nFreeFrames is the number of frames available and is used to index 54 // into frameAddresses. 55 // +checklocks:mu 56 nFreeFrames uint32 57 } 58 59 // SockFD returns the underlying AF_XDP socket FD. 60 func (um *UMEM) SockFD() uint32 { 61 return um.sockfd 62 } 63 64 // Lock locks the UMEM. 65 // 66 // +checklocksacquire:um.mu 67 func (um *UMEM) Lock() { 68 um.mu.Lock() 69 } 70 71 // Unlock unlocks the UMEM. 72 // 73 // +checklocksrelease:um.mu 74 func (um *UMEM) Unlock() { 75 um.mu.Unlock() 76 } 77 78 // FreeFrame returns the frame containing addr to the set of free frames. 79 // 80 // The UMEM must be locked during the call to FreeFrame. 81 // 82 // +checklocks:um.mu 83 func (um *UMEM) FreeFrame(addr uint64) { 84 um.frameAddresses[um.nFreeFrames] = addr 85 um.nFreeFrames++ 86 } 87 88 // AllocFrame returns the address of a frame that can be enqueued to the fill 89 // or TX queue. It will panic if there are no frames left, so callers must call 90 // it no more than the number of buffers reserved via TXQueue.Reserve(). 91 // 92 // The UMEM must be locked during the call to AllocFrame. 93 // 94 // +checklocks:um.mu 95 func (um *UMEM) AllocFrame() uint64 { 96 um.nFreeFrames-- 97 return um.frameAddresses[um.nFreeFrames] & um.frameMask 98 } 99 100 // Get gets the bytes of the packet pointed to by desc. 101 func (um *UMEM) Get(desc unix.XDPDesc) []byte { 102 end := desc.Addr + uint64(desc.Len) 103 if desc.Addr&um.frameMask != (end-1)&um.frameMask { 104 panic(fmt.Sprintf("UMEM (%+v) access crosses frame boundaries: %+v", um, desc)) 105 } 106 return um.mem[desc.Addr:end] 107 }