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 }