diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-11-16 22:26:42 -0500 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2010-11-16 22:26:42 -0500 |
commit | 1a51502bddca7ac1e921d918b741ffd2bec149ed (patch) | |
tree | ee41f3f5fdba243eb2e68b0fd8ee4c8c84d72c2b /drivers/net/ixgbe | |
parent | b178bb3dfc30d9555bdd2401e95af98e23e83e10 (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/net/ixgbe')
-rw-r--r-- | drivers/net/ixgbe/ixgbe.h | 1 | ||||
-rw-r--r-- | drivers/net/ixgbe/ixgbe_main.c | 34 |
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 | ||
197 | enum ixgbe_ring_f_enum { | 198 | enum 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 | ||
4754 | static 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 |