diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2011-05-01 14:50:31 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2011-05-10 16:53:46 -0400 |
commit | 105e53f863c04e1d9e5bb34bf753c9fdbce6a60c (patch) | |
tree | 63500c7cb130348b70279cdbfdfb278de6fc83ad | |
parent | 81bf52d8622f05cfe89893fd5c1101efd85f855b (diff) |
firewire: sbp2: parallelize login, reconnect, logout
The struct sbp2_logical_unit.work items can all be executed in parallel
but are not reentrant. Furthermore, reconnect or re-login work must be
executed in a WQ_MEM_RECLAIM workqueue.
Hence replace the old single-threaded firewire-sbp2 workqueue by a
concurrency-managed but non-reentrant workqueue with rescuer.
firewire-core already maintains one, hence use this one.
In earlier versions of this change, I observed occasional failures of
parallel INQUIRY to an Initio INIC-2430 FireWire 800 to dual IDE bridge.
More testing indicates that parallel INQUIRY is not actually a problem,
but too quick successions of logout and login + INQUIRY, e.g. a quick
sequence of cable plugout and plugin, can result in failed INQUIRY.
This does not seem to be something that should or could be addressed by
serialization.
Another dual-LU device to which I currently have access to, an
OXUF924DSB FireWire 800 to dual SATA bridge with firmware from MacPower,
has been successfully tested with this too.
This change is beneficial to environments with two or more FireWire
storage devices, especially if they are located on the same bus.
Management tasks that should be performed as soon and as quickly as
possible, especially reconnect, are no longer held up by tasks on other
devices that may take a long time, especially login with INQUIRY and sd
or sr driver probe.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
-rw-r--r-- | drivers/firewire/core-card.c | 4 | ||||
-rw-r--r-- | drivers/firewire/core-cdev.c | 2 | ||||
-rw-r--r-- | drivers/firewire/core-device.c | 5 | ||||
-rw-r--r-- | drivers/firewire/core-transaction.c | 12 | ||||
-rw-r--r-- | drivers/firewire/core.h | 2 | ||||
-rw-r--r-- | drivers/firewire/sbp2.c | 9 | ||||
-rw-r--r-- | include/linux/firewire.h | 2 |
7 files changed, 15 insertions, 21 deletions
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index bb8c4d22b03e..29d2423fae6d 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c | |||
@@ -228,7 +228,7 @@ void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset) | |||
228 | 228 | ||
229 | /* Use an arbitrary short delay to combine multiple reset requests. */ | 229 | /* Use an arbitrary short delay to combine multiple reset requests. */ |
230 | fw_card_get(card); | 230 | fw_card_get(card); |
231 | if (!queue_delayed_work(fw_wq, &card->br_work, | 231 | if (!queue_delayed_work(fw_workqueue, &card->br_work, |
232 | delayed ? DIV_ROUND_UP(HZ, 100) : 0)) | 232 | delayed ? DIV_ROUND_UP(HZ, 100) : 0)) |
233 | fw_card_put(card); | 233 | fw_card_put(card); |
234 | } | 234 | } |
@@ -241,7 +241,7 @@ static void br_work(struct work_struct *work) | |||
241 | /* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */ | 241 | /* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */ |
242 | if (card->reset_jiffies != 0 && | 242 | if (card->reset_jiffies != 0 && |
243 | time_before64(get_jiffies_64(), card->reset_jiffies + 2 * HZ)) { | 243 | time_before64(get_jiffies_64(), card->reset_jiffies + 2 * HZ)) { |
244 | if (!queue_delayed_work(fw_wq, &card->br_work, 2 * HZ)) | 244 | if (!queue_delayed_work(fw_workqueue, &card->br_work, 2 * HZ)) |
245 | fw_card_put(card); | 245 | fw_card_put(card); |
246 | return; | 246 | return; |
247 | } | 247 | } |
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index aa1131d26e30..b1c11775839c 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c | |||
@@ -149,7 +149,7 @@ static void release_iso_resource(struct client *, struct client_resource *); | |||
149 | static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) | 149 | static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) |
150 | { | 150 | { |
151 | client_get(r->client); | 151 | client_get(r->client); |
152 | if (!queue_delayed_work(fw_wq, &r->work, delay)) | 152 | if (!queue_delayed_work(fw_workqueue, &r->work, delay)) |
153 | client_put(r->client); | 153 | client_put(r->client); |
154 | } | 154 | } |
155 | 155 | ||
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index ef900d923f15..95a471401892 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c | |||
@@ -725,12 +725,13 @@ struct fw_device *fw_device_get_by_devt(dev_t devt) | |||
725 | return device; | 725 | return device; |
726 | } | 726 | } |
727 | 727 | ||
728 | struct workqueue_struct *fw_wq; | 728 | struct workqueue_struct *fw_workqueue; |
729 | EXPORT_SYMBOL(fw_workqueue); | ||
729 | 730 | ||
730 | static void fw_schedule_device_work(struct fw_device *device, | 731 | static void fw_schedule_device_work(struct fw_device *device, |
731 | unsigned long delay) | 732 | unsigned long delay) |
732 | { | 733 | { |
733 | queue_delayed_work(fw_wq, &device->work, delay); | 734 | queue_delayed_work(fw_workqueue, &device->work, delay); |
734 | } | 735 | } |
735 | 736 | ||
736 | /* | 737 | /* |
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index d4c28a217b2c..334b82a3542c 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c | |||
@@ -1214,21 +1214,21 @@ static int __init fw_core_init(void) | |||
1214 | { | 1214 | { |
1215 | int ret; | 1215 | int ret; |
1216 | 1216 | ||
1217 | fw_wq = alloc_workqueue(KBUILD_MODNAME, | 1217 | fw_workqueue = alloc_workqueue("firewire", |
1218 | WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); | 1218 | WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); |
1219 | if (!fw_wq) | 1219 | if (!fw_workqueue) |
1220 | return -ENOMEM; | 1220 | return -ENOMEM; |
1221 | 1221 | ||
1222 | ret = bus_register(&fw_bus_type); | 1222 | ret = bus_register(&fw_bus_type); |
1223 | if (ret < 0) { | 1223 | if (ret < 0) { |
1224 | destroy_workqueue(fw_wq); | 1224 | destroy_workqueue(fw_workqueue); |
1225 | return ret; | 1225 | return ret; |
1226 | } | 1226 | } |
1227 | 1227 | ||
1228 | fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); | 1228 | fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); |
1229 | if (fw_cdev_major < 0) { | 1229 | if (fw_cdev_major < 0) { |
1230 | bus_unregister(&fw_bus_type); | 1230 | bus_unregister(&fw_bus_type); |
1231 | destroy_workqueue(fw_wq); | 1231 | destroy_workqueue(fw_workqueue); |
1232 | return fw_cdev_major; | 1232 | return fw_cdev_major; |
1233 | } | 1233 | } |
1234 | 1234 | ||
@@ -1244,7 +1244,7 @@ static void __exit fw_core_cleanup(void) | |||
1244 | { | 1244 | { |
1245 | unregister_chrdev(fw_cdev_major, "firewire"); | 1245 | unregister_chrdev(fw_cdev_major, "firewire"); |
1246 | bus_unregister(&fw_bus_type); | 1246 | bus_unregister(&fw_bus_type); |
1247 | destroy_workqueue(fw_wq); | 1247 | destroy_workqueue(fw_workqueue); |
1248 | idr_destroy(&fw_device_idr); | 1248 | idr_destroy(&fw_device_idr); |
1249 | } | 1249 | } |
1250 | 1250 | ||
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 00ea7730c6a7..0fe4e4e6eda7 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h | |||
@@ -140,8 +140,6 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p); | |||
140 | extern struct rw_semaphore fw_device_rwsem; | 140 | extern struct rw_semaphore fw_device_rwsem; |
141 | extern struct idr fw_device_idr; | 141 | extern struct idr fw_device_idr; |
142 | extern int fw_cdev_major; | 142 | extern int fw_cdev_major; |
143 | struct workqueue_struct; | ||
144 | extern struct workqueue_struct *fw_wq; | ||
145 | 143 | ||
146 | struct fw_device *fw_device_get_by_devt(dev_t devt); | 144 | struct fw_device *fw_device_get_by_devt(dev_t devt); |
147 | int fw_device_set_broadcast_channel(struct device *dev, void *gen); | 145 | int fw_device_set_broadcast_channel(struct device *dev, void *gen); |
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 2aafc614ae14..41841a3e3f99 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c | |||
@@ -826,8 +826,6 @@ static void sbp2_target_put(struct sbp2_target *tgt) | |||
826 | kref_put(&tgt->kref, sbp2_release_target); | 826 | kref_put(&tgt->kref, sbp2_release_target); |
827 | } | 827 | } |
828 | 828 | ||
829 | static struct workqueue_struct *sbp2_wq; | ||
830 | |||
831 | /* | 829 | /* |
832 | * Always get the target's kref when scheduling work on one its units. | 830 | * Always get the target's kref when scheduling work on one its units. |
833 | * Each workqueue job is responsible to call sbp2_target_put() upon return. | 831 | * Each workqueue job is responsible to call sbp2_target_put() upon return. |
@@ -835,7 +833,7 @@ static struct workqueue_struct *sbp2_wq; | |||
835 | static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay) | 833 | static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay) |
836 | { | 834 | { |
837 | sbp2_target_get(lu->tgt); | 835 | sbp2_target_get(lu->tgt); |
838 | if (!queue_delayed_work(sbp2_wq, &lu->work, delay)) | 836 | if (!queue_delayed_work(fw_workqueue, &lu->work, delay)) |
839 | sbp2_target_put(lu->tgt); | 837 | sbp2_target_put(lu->tgt); |
840 | } | 838 | } |
841 | 839 | ||
@@ -1645,17 +1643,12 @@ MODULE_ALIAS("sbp2"); | |||
1645 | 1643 | ||
1646 | static int __init sbp2_init(void) | 1644 | static int __init sbp2_init(void) |
1647 | { | 1645 | { |
1648 | sbp2_wq = create_singlethread_workqueue(KBUILD_MODNAME); | ||
1649 | if (!sbp2_wq) | ||
1650 | return -ENOMEM; | ||
1651 | |||
1652 | return driver_register(&sbp2_driver.driver); | 1646 | return driver_register(&sbp2_driver.driver); |
1653 | } | 1647 | } |
1654 | 1648 | ||
1655 | static void __exit sbp2_cleanup(void) | 1649 | static void __exit sbp2_cleanup(void) |
1656 | { | 1650 | { |
1657 | driver_unregister(&sbp2_driver.driver); | 1651 | driver_unregister(&sbp2_driver.driver); |
1658 | destroy_workqueue(sbp2_wq); | ||
1659 | } | 1652 | } |
1660 | 1653 | ||
1661 | module_init(sbp2_init); | 1654 | module_init(sbp2_init); |
diff --git a/include/linux/firewire.h b/include/linux/firewire.h index c0fb405bb435..5e6f42789afe 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h | |||
@@ -449,4 +449,6 @@ void fw_iso_resource_manage(struct fw_card *card, int generation, | |||
449 | u64 channels_mask, int *channel, int *bandwidth, | 449 | u64 channels_mask, int *channel, int *bandwidth, |
450 | bool allocate); | 450 | bool allocate); |
451 | 451 | ||
452 | extern struct workqueue_struct *fw_workqueue; | ||
453 | |||
452 | #endif /* _LINUX_FIREWIRE_H */ | 454 | #endif /* _LINUX_FIREWIRE_H */ |