mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Refactor the tcp segment implementation to use refsvfs.
This change also fixes existing refcounting correctness issues. PiperOrigin-RevId: 437118979
This commit is contained in:
committed by
gVisor bot
parent
04a94d647d
commit
28cf71cc61
@@ -78,6 +78,8 @@ func (f *fakeTransportEndpoint) Abort() {
|
||||
f.Close()
|
||||
}
|
||||
|
||||
func (*fakeTransportEndpoint) Release() {}
|
||||
|
||||
func (f *fakeTransportEndpoint) Close() {
|
||||
// TODO(gvisor.dev/issue/5153): Consider retaining the route.
|
||||
f.route.Release()
|
||||
|
||||
@@ -674,6 +674,9 @@ type Endpoint interface {
|
||||
// SocketOptions returns the structure which contains all the socket
|
||||
// level options.
|
||||
SocketOptions() *SocketOptions
|
||||
|
||||
// Release releases all reference counted objects held by the endpoint.
|
||||
Release()
|
||||
}
|
||||
|
||||
// LinkPacketInfo holds Link layer information for a received packet.
|
||||
@@ -2490,6 +2493,18 @@ func GetDanglingEndpoints() []Endpoint {
|
||||
return es
|
||||
}
|
||||
|
||||
// ReleaseDanglingEndpoints clears out all all reference counted objects held by
|
||||
// dangling endpoints.
|
||||
func ReleaseDanglingEndpoints() {
|
||||
// Get the dangling endpoints first to avoid locking around Release(), which
|
||||
// can cause a lock inversion with endpoint.mu and danglingEndpointsMu.
|
||||
// Calling Release on a dangling endpoint that has been deleted is a noop.
|
||||
eps := GetDanglingEndpoints()
|
||||
for _, ep := range eps {
|
||||
ep.Release()
|
||||
}
|
||||
}
|
||||
|
||||
// AddDanglingEndpoint adds a dangling endpoint.
|
||||
func AddDanglingEndpoint(e Endpoint) {
|
||||
danglingEndpointsMu.Lock()
|
||||
|
||||
@@ -767,6 +767,9 @@ func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketB
|
||||
// HandleError implements stack.TransportEndpoint.
|
||||
func (*endpoint) HandleError(stack.TransportError, *stack.PacketBuffer) {}
|
||||
|
||||
// Release implements stack.TransportEndpoint.
|
||||
func (*endpoint) Release() {}
|
||||
|
||||
// State implements tcpip.Endpoint.State. The ICMP endpoint currently doesn't
|
||||
// expose internal socket state.
|
||||
func (e *endpoint) State() uint32 {
|
||||
|
||||
@@ -151,6 +151,11 @@ func (*endpoint) Wait() {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// Release implements stack.TransportEndpoint.Release.
|
||||
func (*endpoint) Release() {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// LastError implements tcpip.Endpoint.LastError.
|
||||
func (*endpoint) LastError() tcpip.Error {
|
||||
return nil
|
||||
|
||||
@@ -492,6 +492,9 @@ func (ep *endpoint) Stats() tcpip.EndpointStats {
|
||||
// SetOwner implements tcpip.Endpoint.SetOwner.
|
||||
func (*endpoint) SetOwner(tcpip.PacketOwner) {}
|
||||
|
||||
// Release implements tcpip.Release.
|
||||
func (*endpoint) Release() {}
|
||||
|
||||
// SocketOptions implements tcpip.Endpoint.SocketOptions.
|
||||
func (ep *endpoint) SocketOptions() *tcpip.SocketOptions {
|
||||
return &ep.ops
|
||||
|
||||
@@ -735,6 +735,9 @@ func (*endpoint) LastError() tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Release implements stack.TransportEndpoint.Release.
|
||||
func (*endpoint) Release() {}
|
||||
|
||||
// SocketOptions implements tcpip.Endpoint.SocketOptions.
|
||||
func (e *endpoint) SocketOptions() *tcpip.SocketOptions {
|
||||
return &e.ops
|
||||
|
||||
@@ -15,6 +15,17 @@ go_template_instance(
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "tcp_segment_refs",
|
||||
out = "tcp_segment_refs.go",
|
||||
package = "tcp",
|
||||
prefix = "segment",
|
||||
template = "//pkg/refsvfs2:refs_template",
|
||||
types = {
|
||||
"T": "segment",
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "tcp_endpoint_list",
|
||||
out = "tcp_endpoint_list.go",
|
||||
@@ -54,6 +65,7 @@ go_library(
|
||||
"snd.go",
|
||||
"tcp_endpoint_list.go",
|
||||
"tcp_segment_list.go",
|
||||
"tcp_segment_refs.go",
|
||||
"timer.go",
|
||||
],
|
||||
imports = ["gvisor.dev/gvisor/pkg/tcpip/buffer"],
|
||||
@@ -61,6 +73,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/log",
|
||||
"//pkg/rand",
|
||||
"//pkg/refsvfs2",
|
||||
"//pkg/sleep",
|
||||
"//pkg/sync",
|
||||
"//pkg/tcpip",
|
||||
|
||||
@@ -715,7 +715,6 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Err
|
||||
// Requeue the segment if the ACK completing the handshake has more info
|
||||
// to be procesed by the newly established endpoint.
|
||||
if (s.flags.Contains(header.TCPFlagFin) || s.data.Size() > 0) && n.enqueueSegment(s) {
|
||||
s.incRef()
|
||||
n.newSegmentWaker.Assert()
|
||||
}
|
||||
|
||||
@@ -780,7 +779,7 @@ func (e *endpoint) protocolListenLoop(rcvWnd seqnum.Size) {
|
||||
// TODO(gvisor.dev/issue/4690): Better handle errors instead of
|
||||
// silently dropping.
|
||||
_ = e.handleListenSegment(ctx, s)
|
||||
s.decRef()
|
||||
s.DecRef()
|
||||
}
|
||||
close(e.drainDone)
|
||||
e.mu.Unlock()
|
||||
@@ -801,7 +800,7 @@ func (e *endpoint) protocolListenLoop(rcvWnd seqnum.Size) {
|
||||
// TODO(gvisor.dev/issue/4690): Better handle errors instead of
|
||||
// silently dropping.
|
||||
_ = e.handleListenSegment(ctx, s)
|
||||
s.decRef()
|
||||
s.DecRef()
|
||||
}
|
||||
|
||||
// If the queue is not empty, make sure we'll wake up
|
||||
|
||||
@@ -423,7 +423,6 @@ func (h *handshake) synRcvdState(s *segment) tcpip.Error {
|
||||
// Requeue the segment if the ACK completing the handshake has more info
|
||||
// to be procesed by the newly established endpoint.
|
||||
if (s.flags.Contains(header.TCPFlagFin) || s.data.Size() > 0) && h.ep.enqueueSegment(s) {
|
||||
s.incRef()
|
||||
h.ep.newSegmentWaker.Assert()
|
||||
}
|
||||
return nil
|
||||
@@ -459,7 +458,7 @@ func (h *handshake) processSegments() tcpip.Error {
|
||||
}
|
||||
|
||||
err := h.handleSegment(s)
|
||||
s.decRef()
|
||||
s.DecRef()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -590,7 +589,7 @@ func (h *handshake) complete() tcpip.Error {
|
||||
for !h.ep.segmentQueue.empty() {
|
||||
s := h.ep.segmentQueue.dequeue()
|
||||
err := h.handleSegment(s)
|
||||
s.decRef()
|
||||
s.DecRef()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -969,7 +968,7 @@ func (e *endpoint) sendData(next *segment) {
|
||||
if next == nil {
|
||||
return
|
||||
}
|
||||
e.snd.writeNext = next
|
||||
e.snd.updateWriteNext(next)
|
||||
}
|
||||
|
||||
// Push out any new packets.
|
||||
@@ -1003,6 +1002,10 @@ func (e *endpoint) resetConnectionLocked(err tcpip.Error) {
|
||||
}
|
||||
e.sendRaw(buffer.VectorisedView{}, header.TCPFlagAck|header.TCPFlagRst, resetSeqNum, e.rcv.RcvNxt, 0)
|
||||
}
|
||||
// Don't purge read queues here. If there's buffered data, it's still allowed
|
||||
// to be read.
|
||||
e.purgeWriteQueue()
|
||||
e.purgePendingRcvQueue()
|
||||
}
|
||||
|
||||
// completeWorkerLocked is called by the worker goroutine when it's about to
|
||||
@@ -1058,7 +1061,6 @@ func (e *endpoint) tryDeliverSegmentFromClosedEndpoint(s *segment) {
|
||||
}
|
||||
if ep == nil {
|
||||
replyWithReset(e.stack, s, stack.DefaultTOS, tcpip.UseDefaultIPv4TTL, tcpip.UseDefaultIPv6HopLimit)
|
||||
s.decRef()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1082,6 +1084,7 @@ func (e *endpoint) drainClosingSegmentQueue() {
|
||||
}
|
||||
|
||||
e.tryDeliverSegmentFromClosedEndpoint(s)
|
||||
s.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1150,7 +1153,7 @@ func (e *endpoint) handleSegmentsLocked(fastPath bool) tcpip.Error {
|
||||
}
|
||||
|
||||
cont, err := e.handleSegmentLocked(s)
|
||||
s.decRef()
|
||||
s.DecRef()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1553,6 +1556,7 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
|
||||
e.workerCleanup = true
|
||||
if err != nil {
|
||||
e.resetConnectionLocked(err)
|
||||
e.releaseLocked()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1656,15 +1660,13 @@ func (e *endpoint) handleTimeWaitSegments() (extendTimeWait bool, reuseTW func()
|
||||
if EndpointState(tcpEP.State()) == StateListen {
|
||||
reuseTW = func() {
|
||||
if !tcpEP.enqueueSegment(s) {
|
||||
s.decRef()
|
||||
return
|
||||
}
|
||||
tcpEP.newSegmentWaker.Assert()
|
||||
s.DecRef()
|
||||
}
|
||||
// We explicitly do not decRef
|
||||
// the segment as it's still
|
||||
// valid and being reflected to
|
||||
// a listening endpoint.
|
||||
// We explicitly do not DecRef the segment as it's still valid and
|
||||
// being reflected to a listening endpoint.
|
||||
return false, reuseTW
|
||||
}
|
||||
}
|
||||
@@ -1673,7 +1675,7 @@ func (e *endpoint) handleTimeWaitSegments() (extendTimeWait bool, reuseTW func()
|
||||
if extTW {
|
||||
extendTimeWait = true
|
||||
}
|
||||
s.decRef()
|
||||
s.DecRef()
|
||||
}
|
||||
if checkRequeue && !e.segmentQueue.empty() {
|
||||
e.newSegmentWaker.Assert()
|
||||
|
||||
@@ -179,17 +179,16 @@ func (d *dispatcher) queuePacket(stackEP stack.TransportEndpoint, id stack.Trans
|
||||
ep := stackEP.(*endpoint)
|
||||
|
||||
s := newIncomingSegment(id, clock, pkt)
|
||||
defer s.DecRef()
|
||||
if !s.parse(pkt.RXTransportChecksumValidated) {
|
||||
ep.stack.Stats().TCP.InvalidSegmentsReceived.Increment()
|
||||
ep.stats.ReceiveErrors.MalformedPacketsReceived.Increment()
|
||||
s.decRef()
|
||||
return
|
||||
}
|
||||
|
||||
if !s.csumValid {
|
||||
ep.stack.Stats().TCP.ChecksumErrors.Increment()
|
||||
ep.stats.ReceiveErrors.ChecksumErrors.Increment()
|
||||
s.decRef()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -200,7 +199,6 @@ func (d *dispatcher) queuePacket(stackEP stack.TransportEndpoint, id stack.Trans
|
||||
}
|
||||
|
||||
if !ep.enqueueSegment(s) {
|
||||
s.decRef()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -999,6 +1000,71 @@ func (e *endpoint) notifyProtocolGoroutine(n uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) Release() {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
e.releaseLocked()
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
func (e *endpoint) releaseLocked() {
|
||||
e.purgeReadQueue()
|
||||
e.purgeWriteQueue()
|
||||
for {
|
||||
s := e.segmentQueue.dequeue()
|
||||
if s == nil {
|
||||
break
|
||||
}
|
||||
s.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
// Purging pending rcv segments is only necessary on RST.
|
||||
func (e *endpoint) purgePendingRcvQueue() {
|
||||
if e.rcv != nil {
|
||||
for e.rcv.pendingRcvdSegments.Len() > 0 {
|
||||
s := heap.Pop(&e.rcv.pendingRcvdSegments).(*segment)
|
||||
s.DecRef()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
func (e *endpoint) purgeReadQueue() {
|
||||
if e.rcv != nil {
|
||||
e.rcvQueueInfo.rcvQueueMu.Lock()
|
||||
defer e.rcvQueueInfo.rcvQueueMu.Unlock()
|
||||
for {
|
||||
s := e.rcvQueueInfo.rcvQueue.Front()
|
||||
if s == nil {
|
||||
break
|
||||
}
|
||||
e.rcvQueueInfo.rcvQueue.Remove(s)
|
||||
s.DecRef()
|
||||
}
|
||||
e.rcvQueueInfo.RcvBufUsed = 0
|
||||
}
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
func (e *endpoint) purgeWriteQueue() {
|
||||
if e.snd != nil {
|
||||
e.sndQueueInfo.sndQueueMu.Lock()
|
||||
defer e.sndQueueInfo.sndQueueMu.Unlock()
|
||||
e.snd.updateWriteNext(nil)
|
||||
for {
|
||||
s := e.snd.writeList.Front()
|
||||
if s == nil {
|
||||
break
|
||||
}
|
||||
e.snd.writeList.Remove(s)
|
||||
s.DecRef()
|
||||
}
|
||||
e.sndQueueInfo.SndBufUsed = 0
|
||||
e.sndQueueInfo.SndClosed = true
|
||||
}
|
||||
}
|
||||
|
||||
// Abort implements stack.TransportEndpoint.Abort.
|
||||
func (e *endpoint) Abort() {
|
||||
// The abort notification is not processed synchronously, so no
|
||||
@@ -1041,6 +1107,9 @@ func (e *endpoint) Close() {
|
||||
return
|
||||
}
|
||||
|
||||
// We always want to purge the read queue, but do so after the checks in
|
||||
// shutdownLocked.
|
||||
defer e.purgeReadQueue()
|
||||
linger := e.SocketOptions().GetLinger()
|
||||
if linger.Enabled && linger.Timeout == 0 {
|
||||
s := e.EndpointState()
|
||||
@@ -1066,6 +1135,10 @@ func (e *endpoint) Close() {
|
||||
// if we're connected, or stop accepting if we're listening.
|
||||
e.shutdownLocked(tcpip.ShutdownWrite | tcpip.ShutdownRead)
|
||||
e.closeNoShutdownLocked()
|
||||
switch e.EndpointState() {
|
||||
case StateClose, StateError:
|
||||
e.releaseLocked()
|
||||
}
|
||||
}
|
||||
|
||||
// closeNoShutdown closes the endpoint without doing a full shutdown.
|
||||
@@ -1187,6 +1260,8 @@ func (e *endpoint) cleanupLocked() {
|
||||
e.route = nil
|
||||
}
|
||||
|
||||
// It's not safe to purge the read queues yet, there could be unread data.
|
||||
e.purgeWriteQueue()
|
||||
e.stack.CompleteTransportEndpointCleanup(e)
|
||||
tcpip.DeleteDanglingEndpoint(e)
|
||||
}
|
||||
@@ -1470,10 +1545,16 @@ func (e *endpoint) commitRead(done int) *segment {
|
||||
// Memory is only considered released when the whole segment has been
|
||||
// read.
|
||||
memDelta += s.segMemSize()
|
||||
s.decRef()
|
||||
s.DecRef()
|
||||
s = e.rcvQueueInfo.rcvQueue.Front()
|
||||
}
|
||||
e.rcvQueueInfo.RcvBufUsed -= done
|
||||
// Concurrent calls to Close() and Read() could cause RcvBufUsed to be
|
||||
// negative because Read() unlocks between startRead() and commitRead(). In
|
||||
// this case the read is allowed, but we refrain from subtracting from
|
||||
// RcvBufUsed since it should already be zero.
|
||||
if e.rcvQueueInfo.RcvBufUsed != 0 {
|
||||
e.rcvQueueInfo.RcvBufUsed -= done
|
||||
}
|
||||
|
||||
if memDelta > 0 {
|
||||
// If the window was small before this read and if the read freed up
|
||||
@@ -1599,6 +1680,7 @@ func (e *endpoint) queueSegment(p tcpip.Payloader, opts tcpip.WriteOptions) (*se
|
||||
// Add data to the send queue.
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), v)
|
||||
e.sndQueueInfo.SndBufUsed += len(v)
|
||||
s.IncRef()
|
||||
e.snd.writeList.PushBack(s)
|
||||
|
||||
return s, len(v), nil
|
||||
@@ -1616,6 +1698,9 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcp
|
||||
// Return if either we didn't queue anything or if an error occurred while
|
||||
// attempting to queue data.
|
||||
nextSeg, n, err := e.queueSegment(p, opts)
|
||||
if nextSeg != nil {
|
||||
defer nextSeg.DecRef()
|
||||
}
|
||||
if n == 0 || err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -2892,7 +2977,7 @@ func (e *endpoint) readyToRead(s *segment) {
|
||||
e.rcvQueueInfo.rcvQueueMu.Lock()
|
||||
if s != nil {
|
||||
e.rcvQueueInfo.RcvBufUsed += s.payloadSize()
|
||||
s.incRef()
|
||||
s.IncRef()
|
||||
e.rcvQueueInfo.rcvQueue.PushBack(s)
|
||||
} else {
|
||||
e.rcvQueueInfo.RcvClosed = true
|
||||
|
||||
@@ -66,7 +66,7 @@ func NewForwarder(s *stack.Stack, rcvWnd, maxInFlight int, handler func(*Forward
|
||||
// stack.SetTransportProtocolHandler function.
|
||||
func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||
s := newIncomingSegment(id, f.stack.Clock(), pkt)
|
||||
defer s.decRef()
|
||||
defer s.DecRef()
|
||||
|
||||
// We only care about well-formed SYN packets (not SYN-ACK) packets.
|
||||
if !s.parse(pkt.RXTransportChecksumValidated) || !s.csumValid || !s.flags.Contains(header.TCPFlagSyn) || s.flags.Contains(header.TCPFlagAck) {
|
||||
@@ -90,7 +90,7 @@ func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.Packet
|
||||
|
||||
// Launch a new goroutine to handle the request.
|
||||
f.inFlight[id] = struct{}{}
|
||||
s.incRef()
|
||||
s.IncRef()
|
||||
go f.handler(&ForwarderRequest{ // S/R-SAFE: not used by Sentry.
|
||||
forwarder: f,
|
||||
segment: s,
|
||||
@@ -136,7 +136,7 @@ func (r *ForwarderRequest) Complete(sendReset bool) {
|
||||
}
|
||||
|
||||
// Release all resources.
|
||||
r.segment.decRef()
|
||||
r.segment.DecRef()
|
||||
r.segment = nil
|
||||
r.forwarder = nil
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ func (p *protocol) QueuePacket(ep stack.TransportEndpoint, id stack.TransportEnd
|
||||
// means."
|
||||
func (p *protocol) HandleUnknownDestinationPacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) stack.UnknownDestinationPacketDisposition {
|
||||
s := newIncomingSegment(id, p.stack.Clock(), pkt)
|
||||
defer s.decRef()
|
||||
defer s.DecRef()
|
||||
|
||||
if !s.parse(pkt.RXTransportChecksumValidated) || !s.csumValid {
|
||||
return stack.UnknownDestinationPacketMalformed
|
||||
|
||||
@@ -197,7 +197,7 @@ func (s *sender) probeTimerExpired() tcpip.Error {
|
||||
dataSent = s.maybeSendSegment(s.writeNext, int(s.ep.scoreboard.SMSS()), s.SndUna.Add(s.SndWnd))
|
||||
if dataSent {
|
||||
s.Outstanding += s.pCount(s.writeNext, s.MaxPayloadSize)
|
||||
s.writeNext = s.writeNext.Next()
|
||||
s.updateWriteNext(s.writeNext.Next())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -286,8 +286,7 @@ func (r *receiver) consumeSegment(s *segment, segSeq seqnum.Value, segLen seqnum
|
||||
|
||||
for i := first; i < len(r.pendingRcvdSegments); i++ {
|
||||
r.PendingBufUsed -= r.pendingRcvdSegments[i].segMemSize()
|
||||
r.pendingRcvdSegments[i].decRef()
|
||||
|
||||
r.pendingRcvdSegments[i].DecRef()
|
||||
// Note that slice truncation does not allow garbage collection of
|
||||
// truncated items, thus truncated items must be set to nil to avoid
|
||||
// memory leaks.
|
||||
@@ -490,7 +489,7 @@ func (r *receiver) handleRcvdSegment(s *segment) (drop bool, err tcpip.Error) {
|
||||
r.ep.rcvQueueInfo.rcvQueueMu.Lock()
|
||||
r.PendingBufUsed += s.segMemSize()
|
||||
r.ep.rcvQueueInfo.rcvQueueMu.Unlock()
|
||||
s.incRef()
|
||||
s.IncRef()
|
||||
heap.Push(&r.pendingRcvdSegments, s)
|
||||
UpdateSACKBlocks(&r.ep.sack, segSeq, segSeq.Add(segLen), r.RcvNxt)
|
||||
}
|
||||
@@ -526,7 +525,7 @@ func (r *receiver) handleRcvdSegment(s *segment) (drop bool, err tcpip.Error) {
|
||||
r.ep.rcvQueueInfo.rcvQueueMu.Lock()
|
||||
r.PendingBufUsed -= s.segMemSize()
|
||||
r.ep.rcvQueueInfo.rcvQueueMu.Unlock()
|
||||
s.decRef()
|
||||
s.DecRef()
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func (sr *sackRecovery) handleSACKRecovery(limit int, end seqnum.Value) (dataSen
|
||||
}
|
||||
dataSent = true
|
||||
snd.Outstanding++
|
||||
snd.writeNext = nextSeg.Next()
|
||||
snd.updateWriteNext(nextSeg.Next())
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ package tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
@@ -41,7 +40,8 @@ const (
|
||||
// +stateify savable
|
||||
type segment struct {
|
||||
segmentEntry
|
||||
refCnt int32
|
||||
segmentRefs
|
||||
|
||||
ep *endpoint
|
||||
qFlags queueFlags
|
||||
id stack.TransportEndpointID `state:"manual"`
|
||||
@@ -90,13 +90,13 @@ type segment struct {
|
||||
func newIncomingSegment(id stack.TransportEndpointID, clock tcpip.Clock, pkt *stack.PacketBuffer) *segment {
|
||||
netHdr := pkt.Network()
|
||||
s := &segment{
|
||||
refCnt: 1,
|
||||
id: id,
|
||||
srcAddr: netHdr.SourceAddress(),
|
||||
dstAddr: netHdr.DestinationAddress(),
|
||||
netProto: pkt.NetworkProtocolNumber,
|
||||
nicID: pkt.NICID,
|
||||
}
|
||||
s.InitRefs()
|
||||
s.data = pkt.Data().ExtractVV().Clone(s.views[:])
|
||||
s.hdr = header.TCP(pkt.TransportHeader().View())
|
||||
s.rcvdTime = clock.NowMonotonic()
|
||||
@@ -106,9 +106,9 @@ func newIncomingSegment(id stack.TransportEndpointID, clock tcpip.Clock, pkt *st
|
||||
|
||||
func newOutgoingSegment(id stack.TransportEndpointID, clock tcpip.Clock, v buffer.View) *segment {
|
||||
s := &segment{
|
||||
refCnt: 1,
|
||||
id: id,
|
||||
id: id,
|
||||
}
|
||||
s.InitRefs()
|
||||
s.rcvdTime = clock.NowMonotonic()
|
||||
if len(v) != 0 {
|
||||
s.views[0] = v
|
||||
@@ -120,7 +120,6 @@ func newOutgoingSegment(id stack.TransportEndpointID, clock tcpip.Clock, v buffe
|
||||
|
||||
func (s *segment) clone() *segment {
|
||||
t := &segment{
|
||||
refCnt: 1,
|
||||
id: s.id,
|
||||
sequenceNumber: s.sequenceNumber,
|
||||
ackNumber: s.ackNumber,
|
||||
@@ -135,6 +134,7 @@ func (s *segment) clone() *segment {
|
||||
qFlags: s.qFlags,
|
||||
dataMemSize: s.dataMemSize,
|
||||
}
|
||||
t.InitRefs()
|
||||
t.data = s.data.Clone(t.views[:])
|
||||
return t
|
||||
}
|
||||
@@ -164,8 +164,8 @@ func (s *segment) setOwner(ep *endpoint, qFlags queueFlags) {
|
||||
s.qFlags = qFlags
|
||||
}
|
||||
|
||||
func (s *segment) decRef() {
|
||||
if atomic.AddInt32(&s.refCnt, -1) == 0 {
|
||||
func (s *segment) DecRef() {
|
||||
s.segmentRefs.DecRef(func() {
|
||||
if s.ep != nil {
|
||||
switch s.qFlags {
|
||||
case recvQ:
|
||||
@@ -176,11 +176,7 @@ func (s *segment) decRef() {
|
||||
panic(fmt.Sprintf("unexpected queue flag %b set for segment", s.qFlags))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *segment) incRef() {
|
||||
atomic.AddInt32(&s.refCnt, 1)
|
||||
})
|
||||
}
|
||||
|
||||
// logicalLen is the segment length in the sequence number space. It's defined
|
||||
|
||||
@@ -60,6 +60,7 @@ func (q *segmentQueue) enqueue(s *segment) bool {
|
||||
allow := (used <= int(bufSz) || s.payloadSize() == 0) && !q.frozen
|
||||
|
||||
if allow {
|
||||
s.IncRef()
|
||||
q.list.PushBack(s)
|
||||
// Set the owner now that the endpoint owns the segment.
|
||||
s.setOwner(q.ep, recvQ)
|
||||
|
||||
@@ -43,9 +43,9 @@ func TestSegmentMerge(t *testing.T) {
|
||||
var clock faketime.NullClock
|
||||
id := stack.TransportEndpointID{}
|
||||
seg1 := newOutgoingSegment(id, &clock, buffer.NewView(10))
|
||||
defer seg1.decRef()
|
||||
defer seg1.DecRef()
|
||||
seg2 := newOutgoingSegment(id, &clock, buffer.NewView(20))
|
||||
defer seg2.decRef()
|
||||
defer seg2.DecRef()
|
||||
|
||||
checkSegmentSize(t, "seg1", seg1, segmentSizeWants{
|
||||
DataSize: 10,
|
||||
|
||||
@@ -333,7 +333,7 @@ func (s *sender) updateMaxPayloadSize(mtu, count int) {
|
||||
|
||||
// Since we likely reduced the number of outstanding packets, we may be
|
||||
// ready to send some more.
|
||||
s.writeNext = nextSeg
|
||||
s.updateWriteNext(nextSeg)
|
||||
s.sendData()
|
||||
}
|
||||
|
||||
@@ -545,7 +545,7 @@ func (s *sender) retransmitTimerExpired() bool {
|
||||
// information as we lack more rigorous checks to validate if the SACK
|
||||
// information is usable after an RTO.
|
||||
s.ep.scoreboard.Reset()
|
||||
s.writeNext = s.writeList.Front()
|
||||
s.updateWriteNext(s.writeList.Front())
|
||||
|
||||
// RFC 1122 4.2.2.17: Start sending zero window probes when we still see a
|
||||
// zero receive window after retransmission interval and we have data to
|
||||
@@ -752,7 +752,7 @@ func (s *sender) maybeSendSegment(seg *segment, limit int, end seqnum.Value) (se
|
||||
}
|
||||
seg.merge(nSeg)
|
||||
s.writeList.Remove(nSeg)
|
||||
nSeg.decRef()
|
||||
nSeg.DecRef()
|
||||
}
|
||||
if !nextTooBig && seg.data.Size() < available {
|
||||
// Segment is not full.
|
||||
@@ -972,7 +972,7 @@ func (s *sender) sendData() {
|
||||
if s.isAssignedSequenceNumber(seg) && s.ep.SACKPermitted && s.ep.scoreboard.IsSACKED(seg.sackBlock()) {
|
||||
// Move writeNext along so that we don't try and scan data that
|
||||
// has already been SACKED.
|
||||
s.writeNext = seg.Next()
|
||||
s.updateWriteNext(seg.Next())
|
||||
continue
|
||||
}
|
||||
if sent := s.maybeSendSegment(seg, limit, end); !sent {
|
||||
@@ -980,7 +980,7 @@ func (s *sender) sendData() {
|
||||
}
|
||||
dataSent = true
|
||||
s.Outstanding += s.pCount(seg, s.MaxPayloadSize)
|
||||
s.writeNext = seg.Next()
|
||||
s.updateWriteNext(seg.Next())
|
||||
}
|
||||
|
||||
s.postXmit(dataSent, true /* shouldScheduleProbe */)
|
||||
@@ -1526,7 +1526,7 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
|
||||
}
|
||||
|
||||
if s.writeNext == seg {
|
||||
s.writeNext = seg.Next()
|
||||
s.updateWriteNext(seg.Next())
|
||||
}
|
||||
|
||||
// Update the RACK fields if SACK is enabled.
|
||||
@@ -1545,7 +1545,7 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
|
||||
} else {
|
||||
s.SackedOut -= s.pCount(seg, s.MaxPayloadSize)
|
||||
}
|
||||
seg.decRef()
|
||||
seg.DecRef()
|
||||
ackLeft -= datalen
|
||||
}
|
||||
|
||||
@@ -1699,3 +1699,13 @@ func (s *sender) maybeSendOutOfWindowAck(seg *segment) {
|
||||
s.sendAck()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sender) updateWriteNext(seg *segment) {
|
||||
if s.writeNext != nil {
|
||||
s.writeNext.DecRef()
|
||||
}
|
||||
if seg != nil {
|
||||
seg.IncRef()
|
||||
}
|
||||
s.writeNext = seg
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user