aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-11-16 22:26:42 -0500
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>2010-11-16 22:26:42 -0500
commit1a51502bddca7ac1e921d918b741ffd2bec149ed (patch)
treeee41f3f5fdba243eb2e68b0fd8ee4c8c84d72c2b /drivers
parentb178bb3dfc30d9555bdd2401e95af98e23e83e10 (diff)
ixgbe: delay rx_ring freeing
"cat /proc/net/dev" uses RCU protection only. Its quite possible we call a driver get_stats() method while device is dismantling and freeing its data structures. So get_stats() methods must be very careful not accessing driver private data without appropriate locking. In ixgbe case, we access rx_ring pointers. These pointers are freed in ixgbe_clear_interrupt_scheme() and set to NULL, this can trigger NULL dereference in ixgbe_get_stats64() A possible fix is to use RCU locking in ixgbe_get_stats64() and defer rx_ring freeing after a grace period in ixgbe_clear_interrupt_scheme() Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Reported-by: Tantilov, Emil S <emil.s.tantilov@intel.com> Tested-by: Ross Brattain <ross.b.brattain@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/ixgbe/ixgbe.h1
-rw-r--r--drivers/net/ixgbe/ixgbe_main.c34
2 files changed, 25 insertions, 10 deletions
diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h
index ed8703cfffb7..018e143612b2 100644
--- a/drivers/net/ixgbe/ixgbe.h
+++ b/drivers/net/ixgbe/ixgbe.h
@@ -192,6 +192,7 @@ struct ixgbe_ring {
192 192
193 unsigned int size; /* length in bytes */ 193 unsigned int size; /* length in bytes */
194 dma_addr_t dma; /* phys. address of descriptor ring */ 194 dma_addr_t dma; /* phys. address of descriptor ring */
195 struct rcu_head rcu;
195} ____cacheline_internodealigned_in_smp; 196} ____cacheline_internodealigned_in_smp;
196 197
197enum ixgbe_ring_f_enum { 198enum ixgbe_ring_f_enum {
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index fbad4d819608..a137f9dbaacd 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -4751,6 +4751,11 @@ err_set_interrupt:
4751 return err; 4751 return err;
4752} 4752}
4753 4753
4754static void ring_free_rcu(struct rcu_head *head)
4755{
4756 kfree(container_of(head, struct ixgbe_ring, rcu));
4757}
4758
4754/** 4759/**
4755 * ixgbe_clear_interrupt_scheme - Clear the current interrupt scheme settings 4760 * ixgbe_clear_interrupt_scheme - Clear the current interrupt scheme settings
4756 * @adapter: board private structure to clear interrupt scheme on 4761 * @adapter: board private structure to clear interrupt scheme on
@@ -4767,7 +4772,12 @@ void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter)
4767 adapter->tx_ring[i] = NULL; 4772 adapter->tx_ring[i] = NULL;
4768 } 4773 }
4769 for (i = 0; i < adapter->num_rx_queues; i++) { 4774 for (i = 0; i < adapter->num_rx_queues; i++) {
4770 kfree(adapter->rx_ring[i]); 4775 struct ixgbe_ring *ring = adapter->rx_ring[i];
4776
4777 /* ixgbe_get_stats64() might access this ring, we must wait
4778 * a grace period before freeing it.
4779 */
4780 call_rcu(&ring->rcu, ring_free_rcu);
4771 adapter->rx_ring[i] = NULL; 4781 adapter->rx_ring[i] = NULL;
4772 } 4782 }
4773 4783
@@ -6563,20 +6573,23 @@ static struct rtnl_link_stats64 *ixgbe_get_stats64(struct net_device *netdev,
6563 6573
6564 /* accurate rx/tx bytes/packets stats */ 6574 /* accurate rx/tx bytes/packets stats */
6565 dev_txq_stats_fold(netdev, stats); 6575 dev_txq_stats_fold(netdev, stats);
6576 rcu_read_lock();
6566 for (i = 0; i < adapter->num_rx_queues; i++) { 6577 for (i = 0; i < adapter->num_rx_queues; i++) {
6567 struct ixgbe_ring *ring = adapter->rx_ring[i]; 6578 struct ixgbe_ring *ring = ACCESS_ONCE(adapter->rx_ring[i]);
6568 u64 bytes, packets; 6579 u64 bytes, packets;
6569 unsigned int start; 6580 unsigned int start;
6570 6581
6571 do { 6582 if (ring) {
6572 start = u64_stats_fetch_begin_bh(&ring->syncp); 6583 do {
6573 packets = ring->stats.packets; 6584 start = u64_stats_fetch_begin_bh(&ring->syncp);
6574 bytes = ring->stats.bytes; 6585 packets = ring->stats.packets;
6575 } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); 6586 bytes = ring->stats.bytes;
6576 stats->rx_packets += packets; 6587 } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
6577 stats->rx_bytes += bytes; 6588 stats->rx_packets += packets;
6589 stats->rx_bytes += bytes;
6590 }
6578 } 6591 }
6579 6592 rcu_read_unlock();
6580 /* following stats updated by ixgbe_watchdog_task() */ 6593 /* following stats updated by ixgbe_watchdog_task() */
6581 stats->multicast = netdev->stats.multicast; 6594 stats->multicast = netdev->stats.multicast;
6582 stats->rx_errors = netdev->stats.rx_errors; 6595 stats->rx_errors = netdev->stats.rx_errors;
@@ -7282,6 +7295,7 @@ static void __exit ixgbe_exit_module(void)
7282 dca_unregister_notify(&dca_notifier); 7295 dca_unregister_notify(&dca_notifier);
7283#endif 7296#endif
7284 pci_unregister_driver(&ixgbe_driver); 7297 pci_unregister_driver(&ixgbe_driver);
7298 rcu_barrier(); /* Wait for completion of call_rcu()'s */
7285} 7299}
7286 7300
7287#ifdef CONFIG_IXGBE_DCA 7301#ifdef CONFIG_IXGBE_DCA