diff options
author | Anish Bhatt <anish@chelsio.com> | 2014-08-20 16:44:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-08-22 00:45:36 -0400 |
commit | 29aaee65bc28cc75281dc9fe0998cc5e10ac37f9 (patch) | |
tree | 7cfffd8e08c218490a8ac8ba48ee68a6906e44eb | |
parent | 061079ac0b9be7a578dcd09f7865c2c0d6ac894a (diff) |
cxgb4: Fix race condition in cleanup
There is a possible race condition when we unregister the PCI Driver and then
flush/destroy the global "workq". This could lead to situations where there
are tasks on the Work Queue with references to now deleted adapter data
structures. Instead, have per-adapter Work Queues which were instantiated and
torn down in init_one() and remove_one(), respectively.
v2: Remove unnecessary call to flush_workqueue() before destroy_workqueue()
Signed-off-by: Anish Bhatt <anish@chelsio.com>
Signed-off-by: Casey Leedom <leedom@chelsio.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 32 |
2 files changed, 19 insertions, 14 deletions
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index d57282172ea5..c067b7888ac4 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | |||
@@ -652,6 +652,7 @@ struct adapter { | |||
652 | struct tid_info tids; | 652 | struct tid_info tids; |
653 | void **tid_release_head; | 653 | void **tid_release_head; |
654 | spinlock_t tid_release_lock; | 654 | spinlock_t tid_release_lock; |
655 | struct workqueue_struct *workq; | ||
655 | struct work_struct tid_release_task; | 656 | struct work_struct tid_release_task; |
656 | struct work_struct db_full_task; | 657 | struct work_struct db_full_task; |
657 | struct work_struct db_drop_task; | 658 | struct work_struct db_drop_task; |
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 1afee70ce856..18fb9c61d7ba 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | |||
@@ -643,8 +643,6 @@ static int set_rxmode(struct net_device *dev, int mtu, bool sleep_ok) | |||
643 | return ret; | 643 | return ret; |
644 | } | 644 | } |
645 | 645 | ||
646 | static struct workqueue_struct *workq; | ||
647 | |||
648 | /** | 646 | /** |
649 | * link_start - enable a port | 647 | * link_start - enable a port |
650 | * @dev: the port to enable | 648 | * @dev: the port to enable |
@@ -3340,7 +3338,7 @@ static void cxgb4_queue_tid_release(struct tid_info *t, unsigned int chan, | |||
3340 | adap->tid_release_head = (void **)((uintptr_t)p | chan); | 3338 | adap->tid_release_head = (void **)((uintptr_t)p | chan); |
3341 | if (!adap->tid_release_task_busy) { | 3339 | if (!adap->tid_release_task_busy) { |
3342 | adap->tid_release_task_busy = true; | 3340 | adap->tid_release_task_busy = true; |
3343 | queue_work(workq, &adap->tid_release_task); | 3341 | queue_work(adap->workq, &adap->tid_release_task); |
3344 | } | 3342 | } |
3345 | spin_unlock_bh(&adap->tid_release_lock); | 3343 | spin_unlock_bh(&adap->tid_release_lock); |
3346 | } | 3344 | } |
@@ -4140,7 +4138,7 @@ void t4_db_full(struct adapter *adap) | |||
4140 | notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL); | 4138 | notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL); |
4141 | t4_set_reg_field(adap, SGE_INT_ENABLE3, | 4139 | t4_set_reg_field(adap, SGE_INT_ENABLE3, |
4142 | DBFIFO_HP_INT | DBFIFO_LP_INT, 0); | 4140 | DBFIFO_HP_INT | DBFIFO_LP_INT, 0); |
4143 | queue_work(workq, &adap->db_full_task); | 4141 | queue_work(adap->workq, &adap->db_full_task); |
4144 | } | 4142 | } |
4145 | } | 4143 | } |
4146 | 4144 | ||
@@ -4150,7 +4148,7 @@ void t4_db_dropped(struct adapter *adap) | |||
4150 | disable_dbs(adap); | 4148 | disable_dbs(adap); |
4151 | notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL); | 4149 | notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL); |
4152 | } | 4150 | } |
4153 | queue_work(workq, &adap->db_drop_task); | 4151 | queue_work(adap->workq, &adap->db_drop_task); |
4154 | } | 4152 | } |
4155 | 4153 | ||
4156 | static void uld_attach(struct adapter *adap, unsigned int uld) | 4154 | static void uld_attach(struct adapter *adap, unsigned int uld) |
@@ -6517,6 +6515,12 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
6517 | goto out_disable_device; | 6515 | goto out_disable_device; |
6518 | } | 6516 | } |
6519 | 6517 | ||
6518 | adapter->workq = create_singlethread_workqueue("cxgb4"); | ||
6519 | if (!adapter->workq) { | ||
6520 | err = -ENOMEM; | ||
6521 | goto out_free_adapter; | ||
6522 | } | ||
6523 | |||
6520 | /* PCI device has been enabled */ | 6524 | /* PCI device has been enabled */ |
6521 | adapter->flags |= DEV_ENABLED; | 6525 | adapter->flags |= DEV_ENABLED; |
6522 | 6526 | ||
@@ -6715,6 +6719,9 @@ sriov: | |||
6715 | out_unmap_bar0: | 6719 | out_unmap_bar0: |
6716 | iounmap(adapter->regs); | 6720 | iounmap(adapter->regs); |
6717 | out_free_adapter: | 6721 | out_free_adapter: |
6722 | if (adapter->workq) | ||
6723 | destroy_workqueue(adapter->workq); | ||
6724 | |||
6718 | kfree(adapter); | 6725 | kfree(adapter); |
6719 | out_disable_device: | 6726 | out_disable_device: |
6720 | pci_disable_pcie_error_reporting(pdev); | 6727 | pci_disable_pcie_error_reporting(pdev); |
@@ -6736,6 +6743,11 @@ static void remove_one(struct pci_dev *pdev) | |||
6736 | if (adapter) { | 6743 | if (adapter) { |
6737 | int i; | 6744 | int i; |
6738 | 6745 | ||
6746 | /* Tear down per-adapter Work Queue first since it can contain | ||
6747 | * references to our adapter data structure. | ||
6748 | */ | ||
6749 | destroy_workqueue(adapter->workq); | ||
6750 | |||
6739 | if (is_offload(adapter)) | 6751 | if (is_offload(adapter)) |
6740 | detach_ulds(adapter); | 6752 | detach_ulds(adapter); |
6741 | 6753 | ||
@@ -6788,20 +6800,14 @@ static int __init cxgb4_init_module(void) | |||
6788 | { | 6800 | { |
6789 | int ret; | 6801 | int ret; |
6790 | 6802 | ||
6791 | workq = create_singlethread_workqueue("cxgb4"); | ||
6792 | if (!workq) | ||
6793 | return -ENOMEM; | ||
6794 | |||
6795 | /* Debugfs support is optional, just warn if this fails */ | 6803 | /* Debugfs support is optional, just warn if this fails */ |
6796 | cxgb4_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); | 6804 | cxgb4_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); |
6797 | if (!cxgb4_debugfs_root) | 6805 | if (!cxgb4_debugfs_root) |
6798 | pr_warn("could not create debugfs entry, continuing\n"); | 6806 | pr_warn("could not create debugfs entry, continuing\n"); |
6799 | 6807 | ||
6800 | ret = pci_register_driver(&cxgb4_driver); | 6808 | ret = pci_register_driver(&cxgb4_driver); |
6801 | if (ret < 0) { | 6809 | if (ret < 0) |
6802 | debugfs_remove(cxgb4_debugfs_root); | 6810 | debugfs_remove(cxgb4_debugfs_root); |
6803 | destroy_workqueue(workq); | ||
6804 | } | ||
6805 | 6811 | ||
6806 | register_inet6addr_notifier(&cxgb4_inet6addr_notifier); | 6812 | register_inet6addr_notifier(&cxgb4_inet6addr_notifier); |
6807 | 6813 | ||
@@ -6813,8 +6819,6 @@ static void __exit cxgb4_cleanup_module(void) | |||
6813 | unregister_inet6addr_notifier(&cxgb4_inet6addr_notifier); | 6819 | unregister_inet6addr_notifier(&cxgb4_inet6addr_notifier); |
6814 | pci_unregister_driver(&cxgb4_driver); | 6820 | pci_unregister_driver(&cxgb4_driver); |
6815 | debugfs_remove(cxgb4_debugfs_root); /* NULL ok */ | 6821 | debugfs_remove(cxgb4_debugfs_root); /* NULL ok */ |
6816 | flush_workqueue(workq); | ||
6817 | destroy_workqueue(workq); | ||
6818 | } | 6822 | } |
6819 | 6823 | ||
6820 | module_init(cxgb4_init_module); | 6824 | module_init(cxgb4_init_module); |