diff options
author | Tejun Heo <tj@kernel.org> | 2011-01-24 08:57:29 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2011-02-12 11:31:03 -0500 |
commit | 429305e4650c5d3395c21ca183455a3f3e3568af (patch) | |
tree | 22f80762c0a2d3dcca39bde7aec19e5401a3d292 /drivers/scsi/pm8001 | |
parent | a684b8da35a429a246ec2a91e2742bdff5209709 (diff) |
[SCSI] pm8001: simplify workqueue usage
pm8001 manages its own list of pending works and cancel them on device
free. It is unnecessarily complex and has a race condition - the
works are canceled but not synced, so the work could still be running
during and after the data structures are freed.
This patch simplifies workqueue usage.
* A driver specific workqueue pm8001_wq is created to serve these
work items.
* To avoid confusion, the "queue" suffixes are dropped from work items
and functions.
* Delayed queueing was never used. pm8001_work now uses work_struct
instead.
* The driver no longer keeps track of pending works. All pm8001_works
are queued to pm8001_wq and the workqueue is flushed as necessary.
flush_scheduled_work() usage is removed during conversion.
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Jack Wang <jack_wang@usish.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/pm8001')
-rw-r--r-- | drivers/scsi/pm8001/pm8001_hwi.c | 37 | ||||
-rw-r--r-- | drivers/scsi/pm8001/pm8001_init.c | 27 | ||||
-rw-r--r-- | drivers/scsi/pm8001/pm8001_sas.h | 10 |
3 files changed, 41 insertions, 33 deletions
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index d8db0137c0c7..18b6c55cd08c 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c | |||
@@ -1382,53 +1382,50 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha, | |||
1382 | return MPI_IO_STATUS_BUSY; | 1382 | return MPI_IO_STATUS_BUSY; |
1383 | } | 1383 | } |
1384 | 1384 | ||
1385 | static void pm8001_work_queue(struct work_struct *work) | 1385 | static void pm8001_work_fn(struct work_struct *work) |
1386 | { | 1386 | { |
1387 | struct delayed_work *dw = container_of(work, struct delayed_work, work); | 1387 | struct pm8001_work *pw = container_of(work, struct pm8001_work, work); |
1388 | struct pm8001_wq *wq = container_of(dw, struct pm8001_wq, work_q); | ||
1389 | struct pm8001_device *pm8001_dev; | 1388 | struct pm8001_device *pm8001_dev; |
1390 | struct domain_device *dev; | 1389 | struct domain_device *dev; |
1391 | 1390 | ||
1392 | switch (wq->handler) { | 1391 | switch (pw->handler) { |
1393 | case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: | 1392 | case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: |
1394 | pm8001_dev = wq->data; | 1393 | pm8001_dev = pw->data; |
1395 | dev = pm8001_dev->sas_device; | 1394 | dev = pm8001_dev->sas_device; |
1396 | pm8001_I_T_nexus_reset(dev); | 1395 | pm8001_I_T_nexus_reset(dev); |
1397 | break; | 1396 | break; |
1398 | case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: | 1397 | case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: |
1399 | pm8001_dev = wq->data; | 1398 | pm8001_dev = pw->data; |
1400 | dev = pm8001_dev->sas_device; | 1399 | dev = pm8001_dev->sas_device; |
1401 | pm8001_I_T_nexus_reset(dev); | 1400 | pm8001_I_T_nexus_reset(dev); |
1402 | break; | 1401 | break; |
1403 | case IO_DS_IN_ERROR: | 1402 | case IO_DS_IN_ERROR: |
1404 | pm8001_dev = wq->data; | 1403 | pm8001_dev = pw->data; |
1405 | dev = pm8001_dev->sas_device; | 1404 | dev = pm8001_dev->sas_device; |
1406 | pm8001_I_T_nexus_reset(dev); | 1405 | pm8001_I_T_nexus_reset(dev); |
1407 | break; | 1406 | break; |
1408 | case IO_DS_NON_OPERATIONAL: | 1407 | case IO_DS_NON_OPERATIONAL: |
1409 | pm8001_dev = wq->data; | 1408 | pm8001_dev = pw->data; |
1410 | dev = pm8001_dev->sas_device; | 1409 | dev = pm8001_dev->sas_device; |
1411 | pm8001_I_T_nexus_reset(dev); | 1410 | pm8001_I_T_nexus_reset(dev); |
1412 | break; | 1411 | break; |
1413 | } | 1412 | } |
1414 | list_del(&wq->entry); | 1413 | kfree(pw); |
1415 | kfree(wq); | ||
1416 | } | 1414 | } |
1417 | 1415 | ||
1418 | static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data, | 1416 | static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data, |
1419 | int handler) | 1417 | int handler) |
1420 | { | 1418 | { |
1421 | struct pm8001_wq *wq; | 1419 | struct pm8001_work *pw; |
1422 | int ret = 0; | 1420 | int ret = 0; |
1423 | 1421 | ||
1424 | wq = kmalloc(sizeof(struct pm8001_wq), GFP_ATOMIC); | 1422 | pw = kmalloc(sizeof(struct pm8001_work), GFP_ATOMIC); |
1425 | if (wq) { | 1423 | if (pw) { |
1426 | wq->pm8001_ha = pm8001_ha; | 1424 | pw->pm8001_ha = pm8001_ha; |
1427 | wq->data = data; | 1425 | pw->data = data; |
1428 | wq->handler = handler; | 1426 | pw->handler = handler; |
1429 | INIT_DELAYED_WORK(&wq->work_q, pm8001_work_queue); | 1427 | INIT_WORK(&pw->work, pm8001_work_fn); |
1430 | list_add_tail(&wq->entry, &pm8001_ha->wq_list); | 1428 | queue_work(pm8001_wq, &pw->work); |
1431 | schedule_delayed_work(&wq->work_q, 0); | ||
1432 | } else | 1429 | } else |
1433 | ret = -ENOMEM; | 1430 | ret = -ENOMEM; |
1434 | 1431 | ||
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index b95285f3383f..002360da01e3 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c | |||
@@ -51,6 +51,8 @@ static int pm8001_id; | |||
51 | 51 | ||
52 | LIST_HEAD(hba_list); | 52 | LIST_HEAD(hba_list); |
53 | 53 | ||
54 | struct workqueue_struct *pm8001_wq; | ||
55 | |||
54 | /** | 56 | /** |
55 | * The main structure which LLDD must register for scsi core. | 57 | * The main structure which LLDD must register for scsi core. |
56 | */ | 58 | */ |
@@ -134,7 +136,6 @@ static void __devinit pm8001_phy_init(struct pm8001_hba_info *pm8001_ha, | |||
134 | static void pm8001_free(struct pm8001_hba_info *pm8001_ha) | 136 | static void pm8001_free(struct pm8001_hba_info *pm8001_ha) |
135 | { | 137 | { |
136 | int i; | 138 | int i; |
137 | struct pm8001_wq *wq; | ||
138 | 139 | ||
139 | if (!pm8001_ha) | 140 | if (!pm8001_ha) |
140 | return; | 141 | return; |
@@ -150,8 +151,7 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha) | |||
150 | PM8001_CHIP_DISP->chip_iounmap(pm8001_ha); | 151 | PM8001_CHIP_DISP->chip_iounmap(pm8001_ha); |
151 | if (pm8001_ha->shost) | 152 | if (pm8001_ha->shost) |
152 | scsi_host_put(pm8001_ha->shost); | 153 | scsi_host_put(pm8001_ha->shost); |
153 | list_for_each_entry(wq, &pm8001_ha->wq_list, entry) | 154 | flush_workqueue(pm8001_wq); |
154 | cancel_delayed_work(&wq->work_q); | ||
155 | kfree(pm8001_ha->tags); | 155 | kfree(pm8001_ha->tags); |
156 | kfree(pm8001_ha); | 156 | kfree(pm8001_ha); |
157 | } | 157 | } |
@@ -381,7 +381,6 @@ pm8001_pci_alloc(struct pci_dev *pdev, u32 chip_id, struct Scsi_Host *shost) | |||
381 | pm8001_ha->sas = sha; | 381 | pm8001_ha->sas = sha; |
382 | pm8001_ha->shost = shost; | 382 | pm8001_ha->shost = shost; |
383 | pm8001_ha->id = pm8001_id++; | 383 | pm8001_ha->id = pm8001_id++; |
384 | INIT_LIST_HEAD(&pm8001_ha->wq_list); | ||
385 | pm8001_ha->logging_level = 0x01; | 384 | pm8001_ha->logging_level = 0x01; |
386 | sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id); | 385 | sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id); |
387 | #ifdef PM8001_USE_TASKLET | 386 | #ifdef PM8001_USE_TASKLET |
@@ -758,7 +757,7 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
758 | int i , pos; | 757 | int i , pos; |
759 | u32 device_state; | 758 | u32 device_state; |
760 | pm8001_ha = sha->lldd_ha; | 759 | pm8001_ha = sha->lldd_ha; |
761 | flush_scheduled_work(); | 760 | flush_workqueue(pm8001_wq); |
762 | scsi_block_requests(pm8001_ha->shost); | 761 | scsi_block_requests(pm8001_ha->shost); |
763 | pos = pci_find_capability(pdev, PCI_CAP_ID_PM); | 762 | pos = pci_find_capability(pdev, PCI_CAP_ID_PM); |
764 | if (pos == 0) { | 763 | if (pos == 0) { |
@@ -870,17 +869,26 @@ static struct pci_driver pm8001_pci_driver = { | |||
870 | */ | 869 | */ |
871 | static int __init pm8001_init(void) | 870 | static int __init pm8001_init(void) |
872 | { | 871 | { |
873 | int rc; | 872 | int rc = -ENOMEM; |
873 | |||
874 | pm8001_wq = alloc_workqueue("pm8001", 0, 0); | ||
875 | if (!pm8001_wq) | ||
876 | goto err; | ||
877 | |||
874 | pm8001_id = 0; | 878 | pm8001_id = 0; |
875 | pm8001_stt = sas_domain_attach_transport(&pm8001_transport_ops); | 879 | pm8001_stt = sas_domain_attach_transport(&pm8001_transport_ops); |
876 | if (!pm8001_stt) | 880 | if (!pm8001_stt) |
877 | return -ENOMEM; | 881 | goto err_wq; |
878 | rc = pci_register_driver(&pm8001_pci_driver); | 882 | rc = pci_register_driver(&pm8001_pci_driver); |
879 | if (rc) | 883 | if (rc) |
880 | goto err_out; | 884 | goto err_tp; |
881 | return 0; | 885 | return 0; |
882 | err_out: | 886 | |
887 | err_tp: | ||
883 | sas_release_transport(pm8001_stt); | 888 | sas_release_transport(pm8001_stt); |
889 | err_wq: | ||
890 | destroy_workqueue(pm8001_wq); | ||
891 | err: | ||
884 | return rc; | 892 | return rc; |
885 | } | 893 | } |
886 | 894 | ||
@@ -888,6 +896,7 @@ static void __exit pm8001_exit(void) | |||
888 | { | 896 | { |
889 | pci_unregister_driver(&pm8001_pci_driver); | 897 | pci_unregister_driver(&pm8001_pci_driver); |
890 | sas_release_transport(pm8001_stt); | 898 | sas_release_transport(pm8001_stt); |
899 | destroy_workqueue(pm8001_wq); | ||
891 | } | 900 | } |
892 | 901 | ||
893 | module_init(pm8001_init); | 902 | module_init(pm8001_init); |
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 7f064f9ca828..bdb6b27dedd6 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <linux/dma-mapping.h> | 50 | #include <linux/dma-mapping.h> |
51 | #include <linux/pci.h> | 51 | #include <linux/pci.h> |
52 | #include <linux/interrupt.h> | 52 | #include <linux/interrupt.h> |
53 | #include <linux/workqueue.h> | ||
53 | #include <scsi/libsas.h> | 54 | #include <scsi/libsas.h> |
54 | #include <scsi/scsi_tcq.h> | 55 | #include <scsi/scsi_tcq.h> |
55 | #include <scsi/sas_ata.h> | 56 | #include <scsi/sas_ata.h> |
@@ -379,18 +380,16 @@ struct pm8001_hba_info { | |||
379 | #ifdef PM8001_USE_TASKLET | 380 | #ifdef PM8001_USE_TASKLET |
380 | struct tasklet_struct tasklet; | 381 | struct tasklet_struct tasklet; |
381 | #endif | 382 | #endif |
382 | struct list_head wq_list; | ||
383 | u32 logging_level; | 383 | u32 logging_level; |
384 | u32 fw_status; | 384 | u32 fw_status; |
385 | const struct firmware *fw_image; | 385 | const struct firmware *fw_image; |
386 | }; | 386 | }; |
387 | 387 | ||
388 | struct pm8001_wq { | 388 | struct pm8001_work { |
389 | struct delayed_work work_q; | 389 | struct work_struct work; |
390 | struct pm8001_hba_info *pm8001_ha; | 390 | struct pm8001_hba_info *pm8001_ha; |
391 | void *data; | 391 | void *data; |
392 | int handler; | 392 | int handler; |
393 | struct list_head entry; | ||
394 | }; | 393 | }; |
395 | 394 | ||
396 | struct pm8001_fw_image_header { | 395 | struct pm8001_fw_image_header { |
@@ -460,6 +459,9 @@ struct fw_control_ex { | |||
460 | void *param3; | 459 | void *param3; |
461 | }; | 460 | }; |
462 | 461 | ||
462 | /* pm8001 workqueue */ | ||
463 | extern struct workqueue_struct *pm8001_wq; | ||
464 | |||
463 | /******************** function prototype *********************/ | 465 | /******************** function prototype *********************/ |
464 | int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out); | 466 | int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out); |
465 | void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha); | 467 | void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha); |