github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/tcpip/link/sharedmem/rx.go (about)

     1  // Copyright 2018 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 linux
    16  // +build linux
    17  
    18  package sharedmem
    19  
    20  import (
    21  	"golang.org/x/sys/unix"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/atomicbitops"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/eventfd"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/link/sharedmem/queue"
    25  )
    26  
    27  // rx holds all state associated with an rx queue.
    28  type rx struct {
    29  	data       []byte
    30  	sharedData []byte
    31  	q          queue.Rx
    32  	eventFD    eventfd.Eventfd
    33  }
    34  
    35  // init initializes all state needed by the rx queue based on the information
    36  // provided.
    37  //
    38  // The caller always retains ownership of all file descriptors passed in. The
    39  // queue implementation will duplicate any that it may need in the future.
    40  func (r *rx) init(mtu uint32, c *QueueConfig) error {
    41  	// Map in all buffers.
    42  	txPipe, err := getBuffer(c.TxPipeFD)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	rxPipe, err := getBuffer(c.RxPipeFD)
    48  	if err != nil {
    49  		unix.Munmap(txPipe)
    50  		return err
    51  	}
    52  
    53  	data, err := getBuffer(c.DataFD)
    54  	if err != nil {
    55  		unix.Munmap(txPipe)
    56  		unix.Munmap(rxPipe)
    57  		return err
    58  	}
    59  
    60  	sharedData, err := getBuffer(c.SharedDataFD)
    61  	if err != nil {
    62  		unix.Munmap(txPipe)
    63  		unix.Munmap(rxPipe)
    64  		unix.Munmap(data)
    65  		return err
    66  	}
    67  
    68  	// Duplicate the eventFD so that caller can close it but we can still
    69  	// use it.
    70  	efd, err := c.EventFD.Dup()
    71  	if err != nil {
    72  		unix.Munmap(txPipe)
    73  		unix.Munmap(rxPipe)
    74  		unix.Munmap(data)
    75  		unix.Munmap(sharedData)
    76  		return err
    77  	}
    78  
    79  	// Initialize state based on buffers.
    80  	r.q.Init(txPipe, rxPipe, sharedDataPointer(sharedData))
    81  	r.data = data
    82  	r.eventFD = efd
    83  	r.sharedData = sharedData
    84  
    85  	return nil
    86  }
    87  
    88  // cleanup releases all resources allocated during init() except r.eventFD. It
    89  // must only be called if init() has previously succeeded.
    90  func (r *rx) cleanup() {
    91  	a, b := r.q.Bytes()
    92  	unix.Munmap(a)
    93  	unix.Munmap(b)
    94  
    95  	unix.Munmap(r.data)
    96  	unix.Munmap(r.sharedData)
    97  }
    98  
    99  // notify writes to the tx.eventFD to indicate to the peer that there is data to
   100  // be read.
   101  func (r *rx) notify() {
   102  	r.eventFD.Notify()
   103  }
   104  
   105  // postAndReceive posts the provided buffers (if any), and then tries to read
   106  // from the receive queue.
   107  //
   108  // Capacity permitting, it reuses the posted buffer slice to store the buffers
   109  // that were read as well.
   110  //
   111  // This function will block if there aren't any available packets.
   112  func (r *rx) postAndReceive(b []queue.RxBuffer, stopRequested *atomicbitops.Uint32) ([]queue.RxBuffer, uint32) {
   113  	// Post the buffers first. If we cannot post, sleep until we can. We
   114  	// never post more than will fit concurrently, so it's safe to wait
   115  	// until enough room is available.
   116  	if len(b) != 0 && !r.q.PostBuffers(b) {
   117  		r.q.EnableNotification()
   118  		for !r.q.PostBuffers(b) {
   119  			r.eventFD.Wait()
   120  			if stopRequested.Load() != 0 {
   121  				r.q.DisableNotification()
   122  				return nil, 0
   123  			}
   124  		}
   125  		r.q.DisableNotification()
   126  	}
   127  
   128  	// Read the next set of descriptors.
   129  	b, n := r.q.Dequeue(b[:0])
   130  	if len(b) != 0 {
   131  		return b, n
   132  	}
   133  
   134  	// Data isn't immediately available. Enable eventfd notifications.
   135  	r.q.EnableNotification()
   136  	for {
   137  		b, n = r.q.Dequeue(b)
   138  		if len(b) != 0 {
   139  			break
   140  		}
   141  
   142  		// Wait for notification.
   143  		r.eventFD.Wait()
   144  		if stopRequested.Load() != 0 {
   145  			r.q.DisableNotification()
   146  			return nil, 0
   147  		}
   148  	}
   149  	r.q.DisableNotification()
   150  
   151  	return b, n
   152  }