]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc: fix kernel panic caused by vio handshake
authorThomas Tai <thomas.tai@oracle.com>
Mon, 13 Feb 2017 14:50:05 +0000 (06:50 -0800)
committerChuck Anderson <chuck.anderson@oracle.com>
Thu, 9 Mar 2017 03:30:17 +0000 (19:30 -0800)
During hours long reboot test, the primary prints out multiple TX trigger
errors followed by a VIO handshake panic. The TX trigger error happens
because the primary ldmvsw detects that the ldc channel is down. In this
situation, the ldc operation is aborted, the tx and rx queue are then
flushed. The problem is that the rx queue may contain a LDC_EVENT_RESET
sent by the guest. It causes the primary to think that the ldc channel
is not in reset state. When the guest comes up again, the handshake is
out of sequence and thus causes handshake panic.

The TX trigger error would not have happened if the LDC_EVENT_RESET was
received before the TX checked the ldc link state. This is the reason
why the panic happens intermittently.

This patch checks for the connection reset and changes the ldc state to
reset. The reset logic is taken from existing vnet_event_napi() ldc_ctrl:
code path.

Orabug: 23476613
Orabug: 25064864

Signed-off-by: Thomas Tai <thomas.tai@oracle.com>
Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com>
drivers/net/ethernet/sun/sunvnet_common.c

index a2da2b4d0f1180e8fee2a1dd1c9dfc060c257e8c..bceaf2851b76e0b76adbcc60e5571118b691b784 100644 (file)
@@ -1423,11 +1423,19 @@ int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev,
 
        err = __vnet_tx_trigger(port, dr->cons);
        if (unlikely(err < 0)) {
-               netdev_info(dev, "TX trigger error %d\n", err);
                d->hdr.state = VIO_DESC_FREE;
                skb = port->tx_bufs[txi].skb;
                port->tx_bufs[txi].skb = NULL;
                dev->stats.tx_carrier_errors++;
+               if (err == -ECONNRESET) {
+                       /* change ldc state and reset vnet port */
+                       vio_link_state_change(&port->vio, LDC_EVENT_RESET);
+                       vnet_port_reset(port);
+                       vio_port_up(&port->vio);
+                       port->rx_event = 0;
+               } else {
+                       netdev_info(dev, "TX trigger error %d\n", err);
+               }
                goto out_dropped;
        }