summaryrefslogtreecommitdiff
path: root/drivers/net/thunderbolt/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/thunderbolt/main.c')
-rw-r--r--drivers/net/thunderbolt/main.c33
1 files changed, 22 insertions, 11 deletions
diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
index 02a91650561a..98893732bc6e 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -386,11 +386,16 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout)
break;
}
- tb_ring_stop(net->rx_ring.ring);
- tb_ring_stop(net->tx_ring.ring);
- tbnet_free_buffers(&net->rx_ring);
- tbnet_free_buffers(&net->tx_ring);
-
+ /* Tear the paths down before stopping the rings. This mirrors
+ * tbnet_connected_work(), which enables the paths last so the
+ * Rx ring is primed before packets can arrive. Stopping a
+ * ring zeroes its descriptor base and tbnet_free_buffers()
+ * unmaps and frees the frame buffers, leaving anything still
+ * in flight with nowhere to drain to;
+ * __tb_path_deactivate_hop() then waits for the hop's
+ * 'pending' bit, which on some host routers never clears in
+ * that state.
+ */
ret = tb_xdomain_disable_paths(net->xd,
net->local_transmit_path,
net->tx_ring.ring->hop,
@@ -399,6 +404,11 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout)
if (ret)
netdev_warn(net->dev, "failed to disable DMA paths\n");
+ tb_ring_stop(net->rx_ring.ring);
+ tb_ring_stop(net->tx_ring.ring);
+ tbnet_free_buffers(&net->rx_ring);
+ tbnet_free_buffers(&net->tx_ring);
+
tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path);
net->remote_transmit_path = 0;
}
@@ -925,12 +935,8 @@ static int tbnet_open(struct net_device *dev)
netif_carrier_off(dev);
- flags = RING_FLAG_FRAME;
- /* Only enable full E2E if the other end supports it too */
- if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E)
- flags |= RING_FLAG_E2E;
-
- ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags);
+ ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE,
+ RING_FLAG_FRAME);
if (!ring) {
netdev_err(dev, "failed to allocate Tx ring\n");
return -ENOMEM;
@@ -949,6 +955,11 @@ static int tbnet_open(struct net_device *dev)
sof_mask = BIT(TBIP_PDF_FRAME_START);
eof_mask = BIT(TBIP_PDF_FRAME_END);
+ flags = RING_FLAG_FRAME;
+ /* Only enable full E2E if the other end supports it too */
+ if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E)
+ flags |= RING_FLAG_E2E;
+
ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags,
net->tx_ring.ring->hop, sof_mask,
eof_mask, tbnet_start_poll, net);