Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i40evf: fix crash when changing ring sizes

i40evf_set_ringparam was broken in several ways. First, it only changed
the size of the first ring, and second, changing the ring size would
often result in a panic because it would change the count before
deallocating resources, causing the driver to either free nonexistent
buffers, or leak leftover buffers.

Fix this by storing the descriptor count in the adapter structure, and
updating the count for each ring each time we allocate them. This
ensures that we always free the right size ring, and always end up with
the requested count when the device is (re)opened.

Change-ID: I298396cd3d452ba8509d9f2d33a93f25868a9a55
Signed-off-by: Mitch Williams <mitch.a.williams@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>

authored by

Mitch Williams and committed by
Jeff Kirsher
d732a184 337eb08e

+15 -13
+2
drivers/net/ethernet/intel/i40evf/i40evf.h
··· 196 196 struct i40e_ring *tx_rings[I40E_MAX_VSI_QP]; 197 197 u32 tx_timeout_count; 198 198 struct list_head mac_filter_list; 199 + u32 tx_desc_count; 199 200 200 201 /* RX */ 201 202 struct i40e_ring *rx_rings[I40E_MAX_VSI_QP]; 202 203 u64 hw_csum_rx_error; 204 + u32 rx_desc_count; 203 205 int num_msix_vectors; 204 206 struct msix_entry *msix_entries; 205 207
+7 -11
drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
··· 224 224 struct ethtool_ringparam *ring) 225 225 { 226 226 struct i40evf_adapter *adapter = netdev_priv(netdev); 227 - struct i40e_ring *tx_ring = adapter->tx_rings[0]; 228 - struct i40e_ring *rx_ring = adapter->rx_rings[0]; 229 227 230 228 ring->rx_max_pending = I40EVF_MAX_RXD; 231 229 ring->tx_max_pending = I40EVF_MAX_TXD; 232 - ring->rx_pending = rx_ring->count; 233 - ring->tx_pending = tx_ring->count; 230 + ring->rx_pending = adapter->rx_desc_count; 231 + ring->tx_pending = adapter->tx_desc_count; 234 232 } 235 233 236 234 /** ··· 244 246 { 245 247 struct i40evf_adapter *adapter = netdev_priv(netdev); 246 248 u32 new_rx_count, new_tx_count; 247 - int i; 248 249 249 250 if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) 250 251 return -EINVAL; ··· 259 262 new_rx_count = ALIGN(new_rx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE); 260 263 261 264 /* if nothing to do return success */ 262 - if ((new_tx_count == adapter->tx_rings[0]->count) && 263 - (new_rx_count == adapter->rx_rings[0]->count)) 265 + if ((new_tx_count == adapter->tx_desc_count) && 266 + (new_rx_count == adapter->rx_desc_count)) 264 267 return 0; 265 268 266 - for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) { 267 - adapter->tx_rings[0]->count = new_tx_count; 268 - adapter->rx_rings[0]->count = new_rx_count; 269 - } 269 + adapter->tx_desc_count = new_tx_count; 270 + adapter->rx_desc_count = new_rx_count; 270 271 271 272 if (netif_running(netdev)) 272 273 i40evf_reinit_locked(adapter); 274 + 273 275 return 0; 274 276 } 275 277
+6 -2
drivers/net/ethernet/intel/i40evf/i40evf_main.c
··· 1091 1091 tx_ring->queue_index = i; 1092 1092 tx_ring->netdev = adapter->netdev; 1093 1093 tx_ring->dev = &adapter->pdev->dev; 1094 - tx_ring->count = I40EVF_DEFAULT_TXD; 1094 + tx_ring->count = adapter->tx_desc_count; 1095 1095 adapter->tx_rings[i] = tx_ring; 1096 1096 1097 1097 rx_ring = &tx_ring[1]; 1098 1098 rx_ring->queue_index = i; 1099 1099 rx_ring->netdev = adapter->netdev; 1100 1100 rx_ring->dev = &adapter->pdev->dev; 1101 - rx_ring->count = I40EVF_DEFAULT_RXD; 1101 + rx_ring->count = adapter->rx_desc_count; 1102 1102 adapter->rx_rings[i] = rx_ring; 1103 1103 } 1104 1104 ··· 1669 1669 int i, err = 0; 1670 1670 1671 1671 for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) { 1672 + adapter->tx_rings[i]->count = adapter->tx_desc_count; 1672 1673 err = i40evf_setup_tx_descriptors(adapter->tx_rings[i]); 1673 1674 if (!err) 1674 1675 continue; ··· 1697 1696 int i, err = 0; 1698 1697 1699 1698 for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) { 1699 + adapter->rx_rings[i]->count = adapter->rx_desc_count; 1700 1700 err = i40evf_setup_rx_descriptors(adapter->rx_rings[i]); 1701 1701 if (!err) 1702 1702 continue; ··· 2094 2092 adapter->watchdog_timer.data = (unsigned long)adapter; 2095 2093 mod_timer(&adapter->watchdog_timer, jiffies + 1); 2096 2094 2095 + adapter->tx_desc_count = I40EVF_DEFAULT_TXD; 2096 + adapter->rx_desc_count = I40EVF_DEFAULT_RXD; 2097 2097 err = i40evf_init_interrupt_scheme(adapter); 2098 2098 if (err) 2099 2099 goto err_sw_init;