inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/tcpip/transport/tcp/dispatcher.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 package tcp 16 17 import ( 18 "encoding/binary" 19 "math/rand" 20 21 "inet.af/netstack/sleep" 22 "inet.af/netstack/sync" 23 "inet.af/netstack/tcpip" 24 "inet.af/netstack/tcpip/hash/jenkins" 25 "inet.af/netstack/tcpip/header" 26 "inet.af/netstack/tcpip/stack" 27 ) 28 29 // epQueue is a queue of endpoints. 30 type epQueue struct { 31 mu sync.Mutex 32 list endpointList 33 } 34 35 // enqueue adds e to the queue if the endpoint is not already on the queue. 36 func (q *epQueue) enqueue(e *endpoint) { 37 q.mu.Lock() 38 if e.pendingProcessing { 39 q.mu.Unlock() 40 return 41 } 42 q.list.PushBack(e) 43 e.pendingProcessing = true 44 q.mu.Unlock() 45 } 46 47 // dequeue removes and returns the first element from the queue if available, 48 // returns nil otherwise. 49 func (q *epQueue) dequeue() *endpoint { 50 q.mu.Lock() 51 if e := q.list.Front(); e != nil { 52 q.list.Remove(e) 53 e.pendingProcessing = false 54 q.mu.Unlock() 55 return e 56 } 57 q.mu.Unlock() 58 return nil 59 } 60 61 // empty returns true if the queue is empty, false otherwise. 62 func (q *epQueue) empty() bool { 63 q.mu.Lock() 64 v := q.list.Empty() 65 q.mu.Unlock() 66 return v 67 } 68 69 // processor is responsible for processing packets queued to a tcp endpoint. 70 type processor struct { 71 epQ epQueue 72 sleeper sleep.Sleeper 73 newEndpointWaker sleep.Waker 74 closeWaker sleep.Waker 75 } 76 77 func (p *processor) close() { 78 p.closeWaker.Assert() 79 } 80 81 func (p *processor) queueEndpoint(ep *endpoint) { 82 // Queue an endpoint for processing by the processor goroutine. 83 p.epQ.enqueue(ep) 84 p.newEndpointWaker.Assert() 85 } 86 87 const ( 88 newEndpointWaker = 1 89 closeWaker = 2 90 ) 91 92 func (p *processor) start(wg *sync.WaitGroup) { 93 defer wg.Done() 94 defer p.sleeper.Done() 95 96 for { 97 if w := p.sleeper.Fetch(true); w == &p.closeWaker { 98 break 99 } 100 // If not the closeWaker, it must be &p.newEndpointWaker. 101 for { 102 ep := p.epQ.dequeue() 103 if ep == nil { 104 break 105 } 106 if ep.segmentQueue.empty() { 107 continue 108 } 109 110 // If socket has transitioned out of connected state then just let the 111 // worker handle the packet. 112 // 113 // NOTE: We read this outside of e.mu lock which means that by the time 114 // we get to handleSegments the endpoint may not be in ESTABLISHED. But 115 // this should be fine as all normal shutdown states are handled by 116 // handleSegments and if the endpoint moves to a CLOSED/ERROR state 117 // then handleSegments is a noop. 118 if ep.EndpointState() == StateEstablished && ep.mu.TryLock() { 119 // If the endpoint is in a connected state then we do direct delivery 120 // to ensure low latency and avoid scheduler interactions. 121 switch err := ep.handleSegmentsLocked(true /* fastPath */); { 122 case err != nil: 123 // Send any active resets if required. 124 ep.resetConnectionLocked(err) 125 fallthrough 126 case ep.EndpointState() == StateClose: 127 ep.notifyProtocolGoroutine(notifyTickleWorker) 128 case !ep.segmentQueue.empty(): 129 p.epQ.enqueue(ep) 130 } 131 ep.mu.Unlock() // +checklocksforce 132 } else { 133 ep.newSegmentWaker.Assert() 134 } 135 } 136 } 137 } 138 139 // dispatcher manages a pool of TCP endpoint processors which are responsible 140 // for the processing of inbound segments. This fixed pool of processor 141 // goroutines do full tcp processing. The processor is selected based on the 142 // hash of the endpoint id to ensure that delivery for the same endpoint happens 143 // in-order. 144 type dispatcher struct { 145 processors []processor 146 // seed is a random secret for a jenkins hash. 147 seed uint32 148 wg sync.WaitGroup 149 } 150 151 func (d *dispatcher) init(rng *rand.Rand, nProcessors int) { 152 d.close() 153 d.wait() 154 d.processors = make([]processor, nProcessors) 155 d.seed = rng.Uint32() 156 for i := range d.processors { 157 p := &d.processors[i] 158 p.sleeper.AddWaker(&p.newEndpointWaker) 159 p.sleeper.AddWaker(&p.closeWaker) 160 d.wg.Add(1) 161 // NB: sleeper-waker registration must happen synchronously to avoid races 162 // with `close`. It's possible to pull all this logic into `start`, but 163 // that results in a heap-allocated function literal. 164 go p.start(&d.wg) 165 } 166 } 167 168 func (d *dispatcher) close() { 169 for i := range d.processors { 170 d.processors[i].close() 171 } 172 } 173 174 func (d *dispatcher) wait() { 175 d.wg.Wait() 176 } 177 178 func (d *dispatcher) queuePacket(stackEP stack.TransportEndpoint, id stack.TransportEndpointID, clock tcpip.Clock, pkt *stack.PacketBuffer) { 179 ep := stackEP.(*endpoint) 180 181 s := newIncomingSegment(id, clock, pkt) 182 if !s.parse(pkt.RXTransportChecksumValidated) { 183 ep.stack.Stats().TCP.InvalidSegmentsReceived.Increment() 184 ep.stats.ReceiveErrors.MalformedPacketsReceived.Increment() 185 s.decRef() 186 return 187 } 188 189 if !s.csumValid { 190 ep.stack.Stats().TCP.ChecksumErrors.Increment() 191 ep.stats.ReceiveErrors.ChecksumErrors.Increment() 192 s.decRef() 193 return 194 } 195 196 ep.stack.Stats().TCP.ValidSegmentsReceived.Increment() 197 ep.stats.SegmentsReceived.Increment() 198 if (s.flags & header.TCPFlagRst) != 0 { 199 ep.stack.Stats().TCP.ResetsReceived.Increment() 200 } 201 202 if !ep.enqueueSegment(s) { 203 s.decRef() 204 return 205 } 206 207 // For sockets not in established state let the worker goroutine 208 // handle the packets. 209 if ep.EndpointState() != StateEstablished { 210 ep.newSegmentWaker.Assert() 211 return 212 } 213 214 d.selectProcessor(id).queueEndpoint(ep) 215 } 216 217 func (d *dispatcher) selectProcessor(id stack.TransportEndpointID) *processor { 218 var payload [4]byte 219 binary.LittleEndian.PutUint16(payload[0:], id.LocalPort) 220 binary.LittleEndian.PutUint16(payload[2:], id.RemotePort) 221 222 h := jenkins.Sum32(d.seed) 223 h.Write(payload[:]) 224 h.Write([]byte(id.LocalAddress)) 225 h.Write([]byte(id.RemoteAddress)) 226 227 return &d.processors[h.Sum32()%uint32(len(d.processors))] 228 }