github.com/flowerwrong/netstack@v0.0.0-20191009141956-e5848263af28/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  // +build linux
    16  
    17  package sharedmem
    18  
    19  import (
    20  	"sync/atomic"
    21  	"syscall"
    22  
    23  	"github.com/FlowerWrong/netstack/tcpip/link/rawfile"
    24  	"github.com/FlowerWrong/netstack/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    int
    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  		syscall.Munmap(txPipe)
    50  		return err
    51  	}
    52  
    53  	data, err := getBuffer(c.DataFD)
    54  	if err != nil {
    55  		syscall.Munmap(txPipe)
    56  		syscall.Munmap(rxPipe)
    57  		return err
    58  	}
    59  
    60  	sharedData, err := getBuffer(c.SharedDataFD)
    61  	if err != nil {
    62  		syscall.Munmap(txPipe)
    63  		syscall.Munmap(rxPipe)
    64  		syscall.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 := syscall.Dup(c.EventFD)
    71  	if err != nil {
    72  		syscall.Munmap(txPipe)
    73  		syscall.Munmap(rxPipe)
    74  		syscall.Munmap(data)
    75  		syscall.Munmap(sharedData)
    76  		return err
    77  	}
    78  
    79  	// Set the eventfd as non-blocking.
    80  	if err := syscall.SetNonblock(efd, true); err != nil {
    81  		syscall.Munmap(txPipe)
    82  		syscall.Munmap(rxPipe)
    83  		syscall.Munmap(data)
    84  		syscall.Munmap(sharedData)
    85  		syscall.Close(efd)
    86  		return err
    87  	}
    88  
    89  	// Initialize state based on buffers.
    90  	r.q.Init(txPipe, rxPipe, sharedDataPointer(sharedData))
    91  	r.data = data
    92  	r.eventFD = efd
    93  	r.sharedData = sharedData
    94  
    95  	return nil
    96  }
    97  
    98  // cleanup releases all resources allocated during init(). It must only be
    99  // called if init() has previously succeeded.
   100  func (r *rx) cleanup() {
   101  	a, b := r.q.Bytes()
   102  	syscall.Munmap(a)
   103  	syscall.Munmap(b)
   104  
   105  	syscall.Munmap(r.data)
   106  	syscall.Munmap(r.sharedData)
   107  	syscall.Close(r.eventFD)
   108  }
   109  
   110  // postAndReceive posts the provided buffers (if any), and then tries to read
   111  // from the receive queue.
   112  //
   113  // Capacity permitting, it reuses the posted buffer slice to store the buffers
   114  // that were read as well.
   115  //
   116  // This function will block if there aren't any available packets.
   117  func (r *rx) postAndReceive(b []queue.RxBuffer, stopRequested *uint32) ([]queue.RxBuffer, uint32) {
   118  	// Post the buffers first. If we cannot post, sleep until we can. We
   119  	// never post more than will fit concurrently, so it's safe to wait
   120  	// until enough room is available.
   121  	if len(b) != 0 && !r.q.PostBuffers(b) {
   122  		r.q.EnableNotification()
   123  		for !r.q.PostBuffers(b) {
   124  			var tmp [8]byte
   125  			rawfile.BlockingRead(r.eventFD, tmp[:])
   126  			if atomic.LoadUint32(stopRequested) != 0 {
   127  				r.q.DisableNotification()
   128  				return nil, 0
   129  			}
   130  		}
   131  		r.q.DisableNotification()
   132  	}
   133  
   134  	// Read the next set of descriptors.
   135  	b, n := r.q.Dequeue(b[:0])
   136  	if len(b) != 0 {
   137  		return b, n
   138  	}
   139  
   140  	// Data isn't immediately available. Enable eventfd notifications.
   141  	r.q.EnableNotification()
   142  	for {
   143  		b, n = r.q.Dequeue(b)
   144  		if len(b) != 0 {
   145  			break
   146  		}
   147  
   148  		// Wait for notification.
   149  		var tmp [8]byte
   150  		rawfile.BlockingRead(r.eventFD, tmp[:])
   151  		if atomic.LoadUint32(stopRequested) != 0 {
   152  			r.q.DisableNotification()
   153  			return nil, 0
   154  		}
   155  	}
   156  	r.q.DisableNotification()
   157  
   158  	return b, n
   159  }