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  }