diff options
author | Mitch Williams <mitch.a.williams@intel.com> | 2015-06-19 11:56:30 -0400 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2015-06-26 05:51:31 -0400 |
commit | 67c818a1d58c7897b8a6f531684516f9c236fe1b (patch) | |
tree | 49083ecd685152bd7774a8ba95d155ac076997ad /drivers/net | |
parent | 352f8ead753402d6c0496cb83b902128925459eb (diff) |
i40evf: fix panic during MTU change
Down was requesting queue disables, but then exited immediately
without waiting for the queues to actually disable. This could
allow any function called after i40evf_down to run immediately,
including i40evf_up, and causes a memory leak.
Removing the whole reinit_locked function is the best way
to go about this, and allows for the driver to handle the
state changes by requesting reset from the periodic timer.
Also, add a couple WARN_ONs in slow path to help us recognize
if we re-introduce this issue or missed any cases.
Signed-off-by: Mitch Williams <mitch.a.williams@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40evf/i40evf.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40evf/i40evf_main.c | 108 |
4 files changed, 54 insertions, 65 deletions
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index f54996f19629..395f32f226c0 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c | |||
@@ -484,6 +484,8 @@ int i40evf_setup_tx_descriptors(struct i40e_ring *tx_ring) | |||
484 | if (!dev) | 484 | if (!dev) |
485 | return -ENOMEM; | 485 | return -ENOMEM; |
486 | 486 | ||
487 | /* warn if we are about to overwrite the pointer */ | ||
488 | WARN_ON(tx_ring->tx_bi); | ||
487 | bi_size = sizeof(struct i40e_tx_buffer) * tx_ring->count; | 489 | bi_size = sizeof(struct i40e_tx_buffer) * tx_ring->count; |
488 | tx_ring->tx_bi = kzalloc(bi_size, GFP_KERNEL); | 490 | tx_ring->tx_bi = kzalloc(bi_size, GFP_KERNEL); |
489 | if (!tx_ring->tx_bi) | 491 | if (!tx_ring->tx_bi) |
@@ -644,6 +646,8 @@ int i40evf_setup_rx_descriptors(struct i40e_ring *rx_ring) | |||
644 | struct device *dev = rx_ring->dev; | 646 | struct device *dev = rx_ring->dev; |
645 | int bi_size; | 647 | int bi_size; |
646 | 648 | ||
649 | /* warn if we are about to overwrite the pointer */ | ||
650 | WARN_ON(rx_ring->rx_bi); | ||
647 | bi_size = sizeof(struct i40e_rx_buffer) * rx_ring->count; | 651 | bi_size = sizeof(struct i40e_rx_buffer) * rx_ring->count; |
648 | rx_ring->rx_bi = kzalloc(bi_size, GFP_KERNEL); | 652 | rx_ring->rx_bi = kzalloc(bi_size, GFP_KERNEL); |
649 | if (!rx_ring->rx_bi) | 653 | if (!rx_ring->rx_bi) |
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 1b98c25b3092..fea3b75a9a35 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h | |||
@@ -264,7 +264,6 @@ extern const char i40evf_driver_version[]; | |||
264 | 264 | ||
265 | int i40evf_up(struct i40evf_adapter *adapter); | 265 | int i40evf_up(struct i40evf_adapter *adapter); |
266 | void i40evf_down(struct i40evf_adapter *adapter); | 266 | void i40evf_down(struct i40evf_adapter *adapter); |
267 | void i40evf_reinit_locked(struct i40evf_adapter *adapter); | ||
268 | void i40evf_reset(struct i40evf_adapter *adapter); | 267 | void i40evf_reset(struct i40evf_adapter *adapter); |
269 | void i40evf_set_ethtool_ops(struct net_device *netdev); | 268 | void i40evf_set_ethtool_ops(struct net_device *netdev); |
270 | void i40evf_update_stats(struct i40evf_adapter *adapter); | 269 | void i40evf_update_stats(struct i40evf_adapter *adapter); |
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index f4e77665bc54..2b53c870e7f1 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | |||
@@ -267,8 +267,10 @@ static int i40evf_set_ringparam(struct net_device *netdev, | |||
267 | adapter->tx_desc_count = new_tx_count; | 267 | adapter->tx_desc_count = new_tx_count; |
268 | adapter->rx_desc_count = new_rx_count; | 268 | adapter->rx_desc_count = new_rx_count; |
269 | 269 | ||
270 | if (netif_running(netdev)) | 270 | if (netif_running(netdev)) { |
271 | i40evf_reinit_locked(adapter); | 271 | adapter->flags |= I40EVF_FLAG_RESET_NEEDED; |
272 | schedule_work(&adapter->reset_task); | ||
273 | } | ||
272 | 274 | ||
273 | return 0; | 275 | return 0; |
274 | } | 276 | } |
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 7c53aca4b5a6..5c73374c7f22 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c | |||
@@ -170,7 +170,8 @@ static void i40evf_tx_timeout(struct net_device *netdev) | |||
170 | struct i40evf_adapter *adapter = netdev_priv(netdev); | 170 | struct i40evf_adapter *adapter = netdev_priv(netdev); |
171 | 171 | ||
172 | adapter->tx_timeout_count++; | 172 | adapter->tx_timeout_count++; |
173 | if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING)) { | 173 | if (!(adapter->flags & (I40EVF_FLAG_RESET_PENDING | |
174 | I40EVF_FLAG_RESET_NEEDED))) { | ||
174 | adapter->flags |= I40EVF_FLAG_RESET_NEEDED; | 175 | adapter->flags |= I40EVF_FLAG_RESET_NEEDED; |
175 | schedule_work(&adapter->reset_task); | 176 | schedule_work(&adapter->reset_task); |
176 | } | 177 | } |
@@ -1470,8 +1471,8 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter) | |||
1470 | i40e_flush(hw); | 1471 | i40e_flush(hw); |
1471 | } | 1472 | } |
1472 | 1473 | ||
1473 | #define I40EVF_RESET_WAIT_MS 100 | 1474 | #define I40EVF_RESET_WAIT_MS 10 |
1474 | #define I40EVF_RESET_WAIT_COUNT 200 | 1475 | #define I40EVF_RESET_WAIT_COUNT 500 |
1475 | /** | 1476 | /** |
1476 | * i40evf_reset_task - Call-back task to handle hardware reset | 1477 | * i40evf_reset_task - Call-back task to handle hardware reset |
1477 | * @work: pointer to work_struct | 1478 | * @work: pointer to work_struct |
@@ -1495,10 +1496,17 @@ static void i40evf_reset_task(struct work_struct *work) | |||
1495 | &adapter->crit_section)) | 1496 | &adapter->crit_section)) |
1496 | usleep_range(500, 1000); | 1497 | usleep_range(500, 1000); |
1497 | 1498 | ||
1499 | i40evf_misc_irq_disable(adapter); | ||
1498 | if (adapter->flags & I40EVF_FLAG_RESET_NEEDED) { | 1500 | if (adapter->flags & I40EVF_FLAG_RESET_NEEDED) { |
1499 | dev_info(&adapter->pdev->dev, "Requesting reset from PF\n"); | 1501 | adapter->flags &= ~I40EVF_FLAG_RESET_NEEDED; |
1502 | /* Restart the AQ here. If we have been reset but didn't | ||
1503 | * detect it, or if the PF had to reinit, our AQ will be hosed. | ||
1504 | */ | ||
1505 | i40evf_shutdown_adminq(hw); | ||
1506 | i40evf_init_adminq(hw); | ||
1500 | i40evf_request_reset(adapter); | 1507 | i40evf_request_reset(adapter); |
1501 | } | 1508 | } |
1509 | adapter->flags |= I40EVF_FLAG_RESET_PENDING; | ||
1502 | 1510 | ||
1503 | /* poll until we see the reset actually happen */ | 1511 | /* poll until we see the reset actually happen */ |
1504 | for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { | 1512 | for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { |
@@ -1507,10 +1515,10 @@ static void i40evf_reset_task(struct work_struct *work) | |||
1507 | if ((rstat_val != I40E_VFR_VFACTIVE) && | 1515 | if ((rstat_val != I40E_VFR_VFACTIVE) && |
1508 | (rstat_val != I40E_VFR_COMPLETED)) | 1516 | (rstat_val != I40E_VFR_COMPLETED)) |
1509 | break; | 1517 | break; |
1510 | msleep(I40EVF_RESET_WAIT_MS); | 1518 | usleep_range(500, 1000); |
1511 | } | 1519 | } |
1512 | if (i == I40EVF_RESET_WAIT_COUNT) { | 1520 | if (i == I40EVF_RESET_WAIT_COUNT) { |
1513 | adapter->flags &= ~I40EVF_FLAG_RESET_PENDING; | 1521 | dev_info(&adapter->pdev->dev, "Never saw reset\n"); |
1514 | goto continue_reset; /* act like the reset happened */ | 1522 | goto continue_reset; /* act like the reset happened */ |
1515 | } | 1523 | } |
1516 | 1524 | ||
@@ -1518,11 +1526,12 @@ static void i40evf_reset_task(struct work_struct *work) | |||
1518 | for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { | 1526 | for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { |
1519 | rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & | 1527 | rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & |
1520 | I40E_VFGEN_RSTAT_VFR_STATE_MASK; | 1528 | I40E_VFGEN_RSTAT_VFR_STATE_MASK; |
1521 | if ((rstat_val == I40E_VFR_VFACTIVE) || | 1529 | if (rstat_val == I40E_VFR_VFACTIVE) |
1522 | (rstat_val == I40E_VFR_COMPLETED)) | ||
1523 | break; | 1530 | break; |
1524 | msleep(I40EVF_RESET_WAIT_MS); | 1531 | msleep(I40EVF_RESET_WAIT_MS); |
1525 | } | 1532 | } |
1533 | /* extra wait to make sure minimum wait is met */ | ||
1534 | msleep(I40EVF_RESET_WAIT_MS); | ||
1526 | if (i == I40EVF_RESET_WAIT_COUNT) { | 1535 | if (i == I40EVF_RESET_WAIT_COUNT) { |
1527 | struct i40evf_mac_filter *f, *ftmp; | 1536 | struct i40evf_mac_filter *f, *ftmp; |
1528 | struct i40evf_vlan_filter *fv, *fvtmp; | 1537 | struct i40evf_vlan_filter *fv, *fvtmp; |
@@ -1534,11 +1543,10 @@ static void i40evf_reset_task(struct work_struct *work) | |||
1534 | 1543 | ||
1535 | if (netif_running(adapter->netdev)) { | 1544 | if (netif_running(adapter->netdev)) { |
1536 | set_bit(__I40E_DOWN, &adapter->vsi.state); | 1545 | set_bit(__I40E_DOWN, &adapter->vsi.state); |
1537 | i40evf_irq_disable(adapter); | ||
1538 | i40evf_napi_disable_all(adapter); | ||
1539 | netif_tx_disable(netdev); | ||
1540 | netif_tx_stop_all_queues(netdev); | ||
1541 | netif_carrier_off(netdev); | 1546 | netif_carrier_off(netdev); |
1547 | netif_tx_disable(netdev); | ||
1548 | i40evf_napi_disable_all(adapter); | ||
1549 | i40evf_irq_disable(adapter); | ||
1542 | i40evf_free_traffic_irqs(adapter); | 1550 | i40evf_free_traffic_irqs(adapter); |
1543 | i40evf_free_all_tx_resources(adapter); | 1551 | i40evf_free_all_tx_resources(adapter); |
1544 | i40evf_free_all_rx_resources(adapter); | 1552 | i40evf_free_all_rx_resources(adapter); |
@@ -1550,6 +1558,7 @@ static void i40evf_reset_task(struct work_struct *work) | |||
1550 | list_del(&f->list); | 1558 | list_del(&f->list); |
1551 | kfree(f); | 1559 | kfree(f); |
1552 | } | 1560 | } |
1561 | |||
1553 | list_for_each_entry_safe(fv, fvtmp, &adapter->vlan_filter_list, | 1562 | list_for_each_entry_safe(fv, fvtmp, &adapter->vlan_filter_list, |
1554 | list) { | 1563 | list) { |
1555 | list_del(&fv->list); | 1564 | list_del(&fv->list); |
@@ -1564,22 +1573,27 @@ static void i40evf_reset_task(struct work_struct *work) | |||
1564 | i40evf_shutdown_adminq(hw); | 1573 | i40evf_shutdown_adminq(hw); |
1565 | adapter->netdev->flags &= ~IFF_UP; | 1574 | adapter->netdev->flags &= ~IFF_UP; |
1566 | clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); | 1575 | clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); |
1576 | adapter->flags &= ~I40EVF_FLAG_RESET_PENDING; | ||
1577 | dev_info(&adapter->pdev->dev, "Reset task did not complete, VF disabled\n"); | ||
1567 | return; /* Do not attempt to reinit. It's dead, Jim. */ | 1578 | return; /* Do not attempt to reinit. It's dead, Jim. */ |
1568 | } | 1579 | } |
1569 | 1580 | ||
1570 | continue_reset: | 1581 | continue_reset: |
1571 | adapter->flags &= ~I40EVF_FLAG_RESET_PENDING; | ||
1572 | |||
1573 | i40evf_irq_disable(adapter); | ||
1574 | |||
1575 | if (netif_running(adapter->netdev)) { | 1582 | if (netif_running(adapter->netdev)) { |
1576 | i40evf_napi_disable_all(adapter); | ||
1577 | netif_tx_disable(netdev); | ||
1578 | netif_tx_stop_all_queues(netdev); | ||
1579 | netif_carrier_off(netdev); | 1583 | netif_carrier_off(netdev); |
1584 | netif_tx_stop_all_queues(netdev); | ||
1585 | i40evf_napi_disable_all(adapter); | ||
1580 | } | 1586 | } |
1587 | i40evf_irq_disable(adapter); | ||
1581 | 1588 | ||
1582 | adapter->state = __I40EVF_RESETTING; | 1589 | adapter->state = __I40EVF_RESETTING; |
1590 | adapter->flags &= ~I40EVF_FLAG_RESET_PENDING; | ||
1591 | |||
1592 | /* free the Tx/Rx rings and descriptors, might be better to just | ||
1593 | * re-use them sometime in the future | ||
1594 | */ | ||
1595 | i40evf_free_all_rx_resources(adapter); | ||
1596 | i40evf_free_all_tx_resources(adapter); | ||
1583 | 1597 | ||
1584 | /* kill and reinit the admin queue */ | 1598 | /* kill and reinit the admin queue */ |
1585 | if (i40evf_shutdown_adminq(hw)) | 1599 | if (i40evf_shutdown_adminq(hw)) |
@@ -1603,6 +1617,7 @@ continue_reset: | |||
1603 | adapter->aq_required = I40EVF_FLAG_AQ_ADD_MAC_FILTER; | 1617 | adapter->aq_required = I40EVF_FLAG_AQ_ADD_MAC_FILTER; |
1604 | adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER; | 1618 | adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER; |
1605 | clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); | 1619 | clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); |
1620 | i40evf_misc_irq_enable(adapter); | ||
1606 | 1621 | ||
1607 | mod_timer(&adapter->watchdog_timer, jiffies + 2); | 1622 | mod_timer(&adapter->watchdog_timer, jiffies + 2); |
1608 | 1623 | ||
@@ -1624,7 +1639,10 @@ continue_reset: | |||
1624 | goto reset_err; | 1639 | goto reset_err; |
1625 | 1640 | ||
1626 | i40evf_irq_enable(adapter, true); | 1641 | i40evf_irq_enable(adapter, true); |
1642 | } else { | ||
1643 | adapter->state = __I40EVF_DOWN; | ||
1627 | } | 1644 | } |
1645 | |||
1628 | return; | 1646 | return; |
1629 | reset_err: | 1647 | reset_err: |
1630 | dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n"); | 1648 | dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n"); |
@@ -1667,6 +1685,11 @@ static void i40evf_adminq_task(struct work_struct *work) | |||
1667 | memset(event.msg_buf, 0, I40EVF_MAX_AQ_BUF_SIZE); | 1685 | memset(event.msg_buf, 0, I40EVF_MAX_AQ_BUF_SIZE); |
1668 | } while (pending); | 1686 | } while (pending); |
1669 | 1687 | ||
1688 | if ((adapter->flags & | ||
1689 | (I40EVF_FLAG_RESET_PENDING | I40EVF_FLAG_RESET_NEEDED)) || | ||
1690 | adapter->state == __I40EVF_RESETTING) | ||
1691 | goto freedom; | ||
1692 | |||
1670 | /* check for error indications */ | 1693 | /* check for error indications */ |
1671 | val = rd32(hw, hw->aq.arq.len); | 1694 | val = rd32(hw, hw->aq.arq.len); |
1672 | oldval = val; | 1695 | oldval = val; |
@@ -1702,6 +1725,7 @@ static void i40evf_adminq_task(struct work_struct *work) | |||
1702 | if (oldval != val) | 1725 | if (oldval != val) |
1703 | wr32(hw, hw->aq.asq.len, val); | 1726 | wr32(hw, hw->aq.asq.len, val); |
1704 | 1727 | ||
1728 | freedom: | ||
1705 | kfree(event.msg_buf); | 1729 | kfree(event.msg_buf); |
1706 | out: | 1730 | out: |
1707 | /* re-enable Admin queue interrupt cause */ | 1731 | /* re-enable Admin queue interrupt cause */ |
@@ -1897,47 +1921,6 @@ static struct net_device_stats *i40evf_get_stats(struct net_device *netdev) | |||
1897 | } | 1921 | } |
1898 | 1922 | ||
1899 | /** | 1923 | /** |
1900 | * i40evf_reinit_locked - Software reinit | ||
1901 | * @adapter: board private structure | ||
1902 | * | ||
1903 | * Reinititalizes the ring structures in response to a software configuration | ||
1904 | * change. Roughly the same as close followed by open, but skips releasing | ||
1905 | * and reallocating the interrupts. | ||
1906 | **/ | ||
1907 | void i40evf_reinit_locked(struct i40evf_adapter *adapter) | ||
1908 | { | ||
1909 | struct net_device *netdev = adapter->netdev; | ||
1910 | int err; | ||
1911 | |||
1912 | WARN_ON(in_interrupt()); | ||
1913 | |||
1914 | i40evf_down(adapter); | ||
1915 | |||
1916 | /* allocate transmit descriptors */ | ||
1917 | err = i40evf_setup_all_tx_resources(adapter); | ||
1918 | if (err) | ||
1919 | goto err_reinit; | ||
1920 | |||
1921 | /* allocate receive descriptors */ | ||
1922 | err = i40evf_setup_all_rx_resources(adapter); | ||
1923 | if (err) | ||
1924 | goto err_reinit; | ||
1925 | |||
1926 | i40evf_configure(adapter); | ||
1927 | |||
1928 | err = i40evf_up_complete(adapter); | ||
1929 | if (err) | ||
1930 | goto err_reinit; | ||
1931 | |||
1932 | i40evf_irq_enable(adapter, true); | ||
1933 | return; | ||
1934 | |||
1935 | err_reinit: | ||
1936 | dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n"); | ||
1937 | i40evf_close(netdev); | ||
1938 | } | ||
1939 | |||
1940 | /** | ||
1941 | * i40evf_change_mtu - Change the Maximum Transfer Unit | 1924 | * i40evf_change_mtu - Change the Maximum Transfer Unit |
1942 | * @netdev: network interface device structure | 1925 | * @netdev: network interface device structure |
1943 | * @new_mtu: new value for maximum frame size | 1926 | * @new_mtu: new value for maximum frame size |
@@ -1952,9 +1935,10 @@ static int i40evf_change_mtu(struct net_device *netdev, int new_mtu) | |||
1952 | if ((new_mtu < 68) || (max_frame > I40E_MAX_RXBUFFER)) | 1935 | if ((new_mtu < 68) || (max_frame > I40E_MAX_RXBUFFER)) |
1953 | return -EINVAL; | 1936 | return -EINVAL; |
1954 | 1937 | ||
1955 | /* must set new MTU before calling down or up */ | ||
1956 | netdev->mtu = new_mtu; | 1938 | netdev->mtu = new_mtu; |
1957 | i40evf_reinit_locked(adapter); | 1939 | adapter->flags |= I40EVF_FLAG_RESET_NEEDED; |
1940 | schedule_work(&adapter->reset_task); | ||
1941 | |||
1958 | return 0; | 1942 | return 0; |
1959 | } | 1943 | } |
1960 | 1944 | ||