aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid L Stevens <david.stevens@oracle.com>2015-01-26 15:54:27 -0500
committerDavid S. Miller <davem@davemloft.net>2015-01-27 03:25:21 -0500
commit8e2b60cd18381a2f102dc6157ea2481a9ddd0001 (patch)
tree08a04d624d6b289fa51570c462c7b5ae7a102a6c
parent07ac3e7099b5788ee4b78233c96532c6209a304b (diff)
sunvnet: improve error handling when a remote crashes
If a remote machine crashes while there are pending transmit buffers, the sunvnet driver reallocates the ring descriptors giving us enries that have state VIO_DESC_FREE but also an allocated skb. This results in a BUG_ON() call when the remote reboots and we reach that point in the ring. This patch: 1) clears pending tx packets in the ring on port reset 2) changes a BUG_ON() to a pr_warn() when a remote host has given us an invalid descriptor state 3) collapses multiple active buffer frees in a ring to a single message per ring and adds the device name and remote MAC address This fixes the particular problem of not cleaning up pending buffers on a reset, but also prevents us from crashing if the remote handles descriptors out of order or sets an unexpected state for a descriptor. Signed-off-by: David L Stevens <david.stevens@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c62
1 files changed, 34 insertions, 28 deletions
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index fe044f31708e..2b719ccd6e7c 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -50,6 +50,7 @@ MODULE_VERSION(DRV_MODULE_VERSION);
50#define VNET_MAX_RETRIES 10 50#define VNET_MAX_RETRIES 10
51 51
52static int __vnet_tx_trigger(struct vnet_port *port, u32 start); 52static int __vnet_tx_trigger(struct vnet_port *port, u32 start);
53static void vnet_port_reset(struct vnet_port *port);
53 54
54/* Ordered from largest major to lowest */ 55/* Ordered from largest major to lowest */
55static struct vio_version vnet_versions[] = { 56static struct vio_version vnet_versions[] = {
@@ -736,9 +737,7 @@ ldc_ctrl:
736 vio_link_state_change(vio, event); 737 vio_link_state_change(vio, event);
737 738
738 if (event == LDC_EVENT_RESET) { 739 if (event == LDC_EVENT_RESET) {
739 port->rmtu = 0; 740 vnet_port_reset(port);
740 port->tso = true;
741 port->tsolen = 0;
742 vio_port_up(vio); 741 vio_port_up(vio);
743 } 742 }
744 port->rx_event = 0; 743 port->rx_event = 0;
@@ -934,36 +933,36 @@ static struct sk_buff *vnet_clean_tx_ring(struct vnet_port *port,
934 933
935 *pending = 0; 934 *pending = 0;
936 935
937 txi = dr->prod-1; 936 txi = dr->prod;
938 if (txi < 0)
939 txi = VNET_TX_RING_SIZE-1;
940
941 for (i = 0; i < VNET_TX_RING_SIZE; ++i) { 937 for (i = 0; i < VNET_TX_RING_SIZE; ++i) {
942 struct vio_net_desc *d; 938 struct vio_net_desc *d;
943 939
944 d = vio_dring_entry(dr, txi); 940 --txi;
945 941 if (txi < 0)
946 if (d->hdr.state == VIO_DESC_DONE) { 942 txi = VNET_TX_RING_SIZE-1;
947 if (port->tx_bufs[txi].skb) {
948 BUG_ON(port->tx_bufs[txi].skb->next);
949 943
950 port->tx_bufs[txi].skb->next = skb; 944 d = vio_dring_entry(dr, txi);
951 skb = port->tx_bufs[txi].skb;
952 port->tx_bufs[txi].skb = NULL;
953 945
954 ldc_unmap(port->vio.lp, 946 if (d->hdr.state == VIO_DESC_READY) {
955 port->tx_bufs[txi].cookies,
956 port->tx_bufs[txi].ncookies);
957 }
958 d->hdr.state = VIO_DESC_FREE;
959 } else if (d->hdr.state == VIO_DESC_READY) {
960 (*pending)++; 947 (*pending)++;
961 } else if (d->hdr.state == VIO_DESC_FREE) { 948 continue;
962 break;
963 } 949 }
964 --txi; 950 if (port->tx_bufs[txi].skb) {
965 if (txi < 0) 951 if (d->hdr.state != VIO_DESC_DONE)
966 txi = VNET_TX_RING_SIZE-1; 952 pr_notice("invalid ring buffer state %d\n",
953 d->hdr.state);
954 BUG_ON(port->tx_bufs[txi].skb->next);
955
956 port->tx_bufs[txi].skb->next = skb;
957 skb = port->tx_bufs[txi].skb;
958 port->tx_bufs[txi].skb = NULL;
959
960 ldc_unmap(port->vio.lp,
961 port->tx_bufs[txi].cookies,
962 port->tx_bufs[txi].ncookies);
963 } else if (d->hdr.state == VIO_DESC_FREE)
964 break;
965 d->hdr.state = VIO_DESC_FREE;
967 } 966 }
968 return skb; 967 return skb;
969} 968}
@@ -1649,8 +1648,6 @@ static void vnet_port_free_tx_bufs(struct vnet_port *port)
1649 continue; 1648 continue;
1650 1649
1651 d = vio_dring_entry(dr, i); 1650 d = vio_dring_entry(dr, i);
1652 if (d->hdr.state == VIO_DESC_READY)
1653 pr_warn("active transmit buffers freed\n");
1654 1651
1655 ldc_unmap(port->vio.lp, 1652 ldc_unmap(port->vio.lp,
1656 port->tx_bufs[i].cookies, 1653 port->tx_bufs[i].cookies,
@@ -1669,6 +1666,15 @@ static void vnet_port_free_tx_bufs(struct vnet_port *port)
1669 dr->ncookies = 0; 1666 dr->ncookies = 0;
1670} 1667}
1671 1668
1669static void vnet_port_reset(struct vnet_port *port)
1670{
1671 del_timer(&port->clean_timer);
1672 vnet_port_free_tx_bufs(port);
1673 port->rmtu = 0;
1674 port->tso = true;
1675 port->tsolen = 0;
1676}
1677
1672static int vnet_port_alloc_tx_ring(struct vnet_port *port) 1678static int vnet_port_alloc_tx_ring(struct vnet_port *port)
1673{ 1679{
1674 struct vio_dring_state *dr; 1680 struct vio_dring_state *dr;