aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-01-27 03:25:27 -0500
committerDavid S. Miller <davem@davemloft.net>2015-01-27 03:26:04 -0500
commit28f323b3bcad4f18106153d177960292e8b2b240 (patch)
tree08a04d624d6b289fa51570c462c7b5ae7a102a6c
parent971f49dee2639badd70bea6cf92e4eaa357ffecf (diff)
parent8e2b60cd18381a2f102dc6157ea2481a9ddd0001 (diff)
Merge branch 'sunvnet-next'
David L Stevens says: ==================== sunvnet: fix null pointer deref and crash recovery These patches fix an incorrect ordering in releasing ring data, clear pending tx buffers on a reset, and make the sunvnet driver more reliable when remote systems crash during active transmits. ==================== Acked-by: Sowmini Varadhan <sowmini.varadhan@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c83
1 files changed, 45 insertions, 38 deletions
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index b5a1d3d7b0bf..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}
@@ -1637,16 +1636,9 @@ static void vnet_port_free_tx_bufs(struct vnet_port *port)
1637 int i; 1636 int i;
1638 1637
1639 dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 1638 dr = &port->vio.drings[VIO_DRIVER_TX_RING];
1640 if (dr->base) { 1639
1641 ldc_free_exp_dring(port->vio.lp, dr->base, 1640 if (dr->base == NULL)
1642 (dr->entry_size * dr->num_entries), 1641 return;
1643 dr->cookies, dr->ncookies);
1644 dr->base = NULL;
1645 dr->entry_size = 0;
1646 dr->num_entries = 0;
1647 dr->pending = 0;
1648 dr->ncookies = 0;
1649 }
1650 1642
1651 for (i = 0; i < VNET_TX_RING_SIZE; i++) { 1643 for (i = 0; i < VNET_TX_RING_SIZE; i++) {
1652 struct vio_net_desc *d; 1644 struct vio_net_desc *d;
@@ -1656,8 +1648,6 @@ static void vnet_port_free_tx_bufs(struct vnet_port *port)
1656 continue; 1648 continue;
1657 1649
1658 d = vio_dring_entry(dr, i); 1650 d = vio_dring_entry(dr, i);
1659 if (d->hdr.state == VIO_DESC_READY)
1660 pr_warn("active transmit buffers freed\n");
1661 1651
1662 ldc_unmap(port->vio.lp, 1652 ldc_unmap(port->vio.lp,
1663 port->tx_bufs[i].cookies, 1653 port->tx_bufs[i].cookies,
@@ -1666,6 +1656,23 @@ static void vnet_port_free_tx_bufs(struct vnet_port *port)
1666 port->tx_bufs[i].skb = NULL; 1656 port->tx_bufs[i].skb = NULL;
1667 d->hdr.state = VIO_DESC_FREE; 1657 d->hdr.state = VIO_DESC_FREE;
1668 } 1658 }
1659 ldc_free_exp_dring(port->vio.lp, dr->base,
1660 (dr->entry_size * dr->num_entries),
1661 dr->cookies, dr->ncookies);
1662 dr->base = NULL;
1663 dr->entry_size = 0;
1664 dr->num_entries = 0;
1665 dr->pending = 0;
1666 dr->ncookies = 0;
1667}
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;
1669} 1676}
1670 1677
1671static int vnet_port_alloc_tx_ring(struct vnet_port *port) 1678static int vnet_port_alloc_tx_ring(struct vnet_port *port)