github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/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/metacubex/gvisor/pkg/atomicbitops" 23 "github.com/metacubex/gvisor/pkg/eventfd" 24 "github.com/metacubex/gvisor/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 }