inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/tcpip/link/qdisc/fifo/endpoint.go (about)

     1  // Copyright 2020 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 fifo provides the implementation of data-link layer endpoints that
    16  // wrap another endpoint and queues all outbound packets and asynchronously
    17  // dispatches them to the lower endpoint.
    18  package fifo
    19  
    20  import (
    21  	"inet.af/netstack/sleep"
    22  	"inet.af/netstack/sync"
    23  	"inet.af/netstack/tcpip"
    24  	"inet.af/netstack/tcpip/header"
    25  	"inet.af/netstack/tcpip/stack"
    26  )
    27  
    28  var _ stack.LinkEndpoint = (*endpoint)(nil)
    29  var _ stack.GSOEndpoint = (*endpoint)(nil)
    30  
    31  // endpoint represents a LinkEndpoint which implements a FIFO queue for all
    32  // outgoing packets. endpoint can have 1 or more underlying queueDispatchers.
    33  // All outgoing packets are consistenly hashed to a single underlying queue
    34  // using the PacketBuffer.Hash if set, otherwise all packets are queued to the
    35  // first queue to avoid reordering in case of missing hash.
    36  type endpoint struct {
    37  	dispatcher  stack.NetworkDispatcher
    38  	lower       stack.LinkEndpoint
    39  	wg          sync.WaitGroup
    40  	dispatchers []*queueDispatcher
    41  }
    42  
    43  // queueDispatcher is responsible for dispatching all outbound packets in its
    44  // queue. It will also smartly batch packets when possible and write them
    45  // through the lower LinkEndpoint.
    46  type queueDispatcher struct {
    47  	lower          stack.LinkEndpoint
    48  	q              *packetBufferQueue
    49  	newPacketWaker sleep.Waker
    50  	closeWaker     sleep.Waker
    51  }
    52  
    53  // New creates a new fifo link endpoint with the n queues with maximum
    54  // capacity of queueLen.
    55  func New(lower stack.LinkEndpoint, n int, queueLen int) stack.LinkEndpoint {
    56  	e := &endpoint{
    57  		lower: lower,
    58  	}
    59  	// Create the required dispatchers
    60  	for i := 0; i < n; i++ {
    61  		qd := &queueDispatcher{
    62  			q:     &packetBufferQueue{limit: queueLen},
    63  			lower: lower,
    64  		}
    65  		e.dispatchers = append(e.dispatchers, qd)
    66  		e.wg.Add(1)
    67  		go func() {
    68  			defer e.wg.Done()
    69  			qd.dispatchLoop()
    70  		}()
    71  	}
    72  	return e
    73  }
    74  
    75  func (q *queueDispatcher) dispatchLoop() {
    76  	s := sleep.Sleeper{}
    77  	s.AddWaker(&q.newPacketWaker)
    78  	s.AddWaker(&q.closeWaker)
    79  	defer s.Done()
    80  
    81  	const batchSize = 32
    82  	var batch stack.PacketBufferList
    83  	for {
    84  		w := s.Fetch(true)
    85  		if w == &q.closeWaker {
    86  			return
    87  		}
    88  		// Must otherwise be the newPacketWaker.
    89  		for pkt := q.q.dequeue(); pkt != nil; pkt = q.q.dequeue() {
    90  			batch.PushBack(pkt)
    91  			if batch.Len() < batchSize && !q.q.empty() {
    92  				continue
    93  			}
    94  			// We pass a protocol of zero here because each packet carries its
    95  			// NetworkProtocol.
    96  			q.lower.WritePackets(stack.RouteInfo{}, batch, 0 /* protocol */)
    97  			batch.DecRef()
    98  			batch.Reset()
    99  		}
   100  	}
   101  }
   102  
   103  // DeliverNetworkPacket implements stack.NetworkDispatcher.DeliverNetworkPacket.
   104  func (e *endpoint) DeliverNetworkPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
   105  	e.dispatcher.DeliverNetworkPacket(remote, local, protocol, pkt)
   106  }
   107  
   108  // Attach implements stack.LinkEndpoint.Attach.
   109  func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
   110  	// nil means the NIC is being removed.
   111  	if dispatcher == nil {
   112  		e.lower.Attach(nil)
   113  		e.Wait()
   114  		e.dispatcher = nil
   115  		return
   116  	}
   117  	e.dispatcher = dispatcher
   118  	e.lower.Attach(e)
   119  }
   120  
   121  // IsAttached implements stack.LinkEndpoint.IsAttached.
   122  func (e *endpoint) IsAttached() bool {
   123  	return e.dispatcher != nil
   124  }
   125  
   126  // MTU implements stack.LinkEndpoint.MTU.
   127  func (e *endpoint) MTU() uint32 {
   128  	return e.lower.MTU()
   129  }
   130  
   131  // Capabilities implements stack.LinkEndpoint.Capabilities.
   132  func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
   133  	return e.lower.Capabilities()
   134  }
   135  
   136  // MaxHeaderLength implements stack.LinkEndpoint.MaxHeaderLength.
   137  func (e *endpoint) MaxHeaderLength() uint16 {
   138  	return e.lower.MaxHeaderLength()
   139  }
   140  
   141  // LinkAddress implements stack.LinkEndpoint.LinkAddress.
   142  func (e *endpoint) LinkAddress() tcpip.LinkAddress {
   143  	return e.lower.LinkAddress()
   144  }
   145  
   146  // GSOMaxSize implements stack.GSOEndpoint.
   147  func (e *endpoint) GSOMaxSize() uint32 {
   148  	if gso, ok := e.lower.(stack.GSOEndpoint); ok {
   149  		return gso.GSOMaxSize()
   150  	}
   151  	return 0
   152  }
   153  
   154  // SupportedGSO implements stack.GSOEndpoint.
   155  func (e *endpoint) SupportedGSO() stack.SupportedGSO {
   156  	if gso, ok := e.lower.(stack.GSOEndpoint); ok {
   157  		return gso.SupportedGSO()
   158  	}
   159  	return stack.GSONotSupported
   160  }
   161  
   162  // WritePacket implements stack.LinkEndpoint.WritePacket.
   163  //
   164  // The packet must have the following fields populated:
   165  //  - pkt.EgressRoute
   166  //  - pkt.GSOOptions
   167  //  - pkt.NetworkProtocolNumber
   168  func (e *endpoint) WritePacket(r stack.RouteInfo, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
   169  	d := e.dispatchers[int(pkt.Hash)%len(e.dispatchers)]
   170  	if !d.q.enqueue(pkt) {
   171  		return &tcpip.ErrNoBufferSpace{}
   172  	}
   173  	d.newPacketWaker.Assert()
   174  	return nil
   175  }
   176  
   177  // WritePackets implements stack.LinkEndpoint.WritePackets.
   178  //
   179  // Each packet in the packet buffer list must have the following fields
   180  // populated:
   181  //  - pkt.EgressRoute
   182  //  - pkt.GSOOptions
   183  //  - pkt.NetworkProtocolNumber
   184  func (e *endpoint) WritePackets(r stack.RouteInfo, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
   185  	enqueued := 0
   186  	for pkt := pkts.Front(); pkt != nil; {
   187  		d := e.dispatchers[int(pkt.Hash)%len(e.dispatchers)]
   188  		nxt := pkt.Next()
   189  		if !d.q.enqueue(pkt) {
   190  			if enqueued > 0 {
   191  				d.newPacketWaker.Assert()
   192  			}
   193  			return enqueued, &tcpip.ErrNoBufferSpace{}
   194  		}
   195  		pkt = nxt
   196  		enqueued++
   197  		d.newPacketWaker.Assert()
   198  	}
   199  	return enqueued, nil
   200  }
   201  
   202  // Wait implements stack.LinkEndpoint.Wait.
   203  func (e *endpoint) Wait() {
   204  	e.lower.Wait()
   205  
   206  	// The linkEP is gone. Teardown the outbound dispatcher goroutines.
   207  	for i := range e.dispatchers {
   208  		e.dispatchers[i].closeWaker.Assert()
   209  	}
   210  
   211  	e.wg.Wait()
   212  }
   213  
   214  // ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType
   215  func (e *endpoint) ARPHardwareType() header.ARPHardwareType {
   216  	return e.lower.ARPHardwareType()
   217  }
   218  
   219  // AddHeader implements stack.LinkEndpoint.AddHeader.
   220  func (e *endpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
   221  	e.lower.AddHeader(local, remote, protocol, pkt)
   222  }
   223  
   224  // WriteRawPacket implements stack.LinkEndpoint.
   225  func (e *endpoint) WriteRawPacket(pkt *stack.PacketBuffer) tcpip.Error {
   226  	return e.lower.WriteRawPacket(pkt)
   227  }