diff options
| author | David S. Miller <davem@davemloft.net> | 2015-01-27 03:25:27 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2015-01-27 03:26:04 -0500 |
| commit | 28f323b3bcad4f18106153d177960292e8b2b240 (patch) | |
| tree | 08a04d624d6b289fa51570c462c7b5ae7a102a6c | |
| parent | 971f49dee2639badd70bea6cf92e4eaa357ffecf (diff) | |
| parent | 8e2b60cd18381a2f102dc6157ea2481a9ddd0001 (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.c | 83 |
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 | ||
| 52 | static int __vnet_tx_trigger(struct vnet_port *port, u32 start); | 52 | static int __vnet_tx_trigger(struct vnet_port *port, u32 start); |
| 53 | static void vnet_port_reset(struct vnet_port *port); | ||
| 53 | 54 | ||
| 54 | /* Ordered from largest major to lowest */ | 55 | /* Ordered from largest major to lowest */ |
| 55 | static struct vio_version vnet_versions[] = { | 56 | static 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 | |||
| 1669 | static 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 | ||
| 1671 | static int vnet_port_alloc_tx_ring(struct vnet_port *port) | 1678 | static int vnet_port_alloc_tx_ring(struct vnet_port *port) |
