Synchronously send packets over pipe link endpoint

Before this change, packets were delivered asynchronously to the remote
end of a pipe. This was to avoid a deadlock during link resolution where
the stack would attempt to double-lock a mutex (see removed comments in
the parent commit for details).

As of https://github.com/google/gvisor/commit/4943347137, we do not hold
locks while sending link resolution probes so the deadlock will no
longer occur.

PiperOrigin-RevId: 356066224
This commit is contained in:
Ghanan Gowripalan
2021-02-06 16:58:25 -08:00
committed by gVisor bot
parent 11ce8ba992
commit 554c405e87
+15 -29
View File
@@ -46,6 +46,10 @@ type Endpoint struct {
}
func (e *Endpoint) deliverPackets(r stack.RouteInfo, proto tcpip.NetworkProtocolNumber, pkts stack.PacketBufferList) {
if !e.linked.IsAttached() {
return
}
// Note that the local address from the perspective of this endpoint is the
// remote address from the perspective of the other end of the pipe
// (e.linked). Similarly, the remote address from the perspective of this
@@ -54,44 +58,26 @@ func (e *Endpoint) deliverPackets(r stack.RouteInfo, proto tcpip.NetworkProtocol
// Deliver the packet in a new goroutine to escape this goroutine's stack and
// avoid a deadlock when a packet triggers a response which leads the stack to
// try and take a lock it already holds.
//
// As of writing, a deadlock may occur when performing link resolution as the
// neighbor table will send a solicitation while holding a lock and the
// response advertisement will be sent in the same stack that sent the
// solictation. When the response is received, the stack attempts to take the
// same lock it already took before sending the solicitation, leading to a
// deadlock. Basically, we attempt to lock the same lock twice in the same
// call stack.
//
// TODO(gvisor.dev/issue/5289): don't use a new goroutine once we support send
// and receive queues.
go func() {
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
e.linked.dispatcher.DeliverNetworkPacket(r.LocalLinkAddress /* remote */, r.RemoteLinkAddress /* local */, proto, stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.NewVectorisedView(pkt.Size(), pkt.Views()),
}))
}
}()
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
e.linked.dispatcher.DeliverNetworkPacket(r.LocalLinkAddress /* remote */, r.RemoteLinkAddress /* local */, proto, stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.NewVectorisedView(pkt.Size(), pkt.Views()),
}))
}
}
// WritePacket implements stack.LinkEndpoint.
func (e *Endpoint) WritePacket(r stack.RouteInfo, _ *stack.GSO, proto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
if e.linked.IsAttached() {
var pkts stack.PacketBufferList
pkts.PushBack(pkt)
e.deliverPackets(r, proto, pkts)
}
var pkts stack.PacketBufferList
pkts.PushBack(pkt)
e.deliverPackets(r, proto, pkts)
return nil
}
// WritePackets implements stack.LinkEndpoint.
func (e *Endpoint) WritePackets(r stack.RouteInfo, _ *stack.GSO, pkts stack.PacketBufferList, proto tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
if e.linked.IsAttached() {
e.deliverPackets(r, proto, pkts)
}
return pkts.Len(), nil
n := pkts.Len()
e.deliverPackets(r, proto, pkts)
return n, nil
}
// Attach implements stack.LinkEndpoint.