github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/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/nicocha30/gvisor-ligolo/pkg/sync"
    25  )
    26  
    27  // TODO(b/240191988): There's some kind of memory corruption bug that occurs
    28  // occasionally. This occured 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  }