diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2010-10-13 07:39:46 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2011-05-10 16:53:45 -0400 |
commit | 6ea9e7bbfc389a12d52646449a201fe933ccd663 (patch) | |
tree | f84e89a394d45db4f30e1286966f69be62f203da | |
parent | 13882a82ee1646336c3996c93b4a560a55d2a419 (diff) |
firewire: core: use non-reentrant workqueue with rescuer
firewire-core manages the following types of work items:
fw_card.br_work:
- resets the bus on a card and possibly sends a PHY packet before that
- does not sleep for long or not at all
- is scheduled via fw_schedule_bus_reset() by
- firewire-ohci's pci_probe method
- firewire-ohci's set_config_rom method, called by kernelspace
protocol drivers and userspace drivers which add/remove
Configuration ROM descriptors
- userspace drivers which use the bus reset ioctl
- itself if the last reset happened less than 2 seconds ago
fw_card.bm_work:
- performs bus management duties
- usually does not (but may in corner cases) sleep for long
- is scheduled via fw_schedule_bm_work() by
- firewire-ohci's self-ID-complete IRQ handler tasklet
- firewire-core's fw_device.work instances whenever the root node
device was (successfully or unsuccessfully) discovered,
refreshed, or rediscovered
- itself in case of resource allocation failures or in order to
obey the 125ms bus manager arbitration interval
fw_device.work:
- performs node probe, update, shutdown, revival, removal; including
kernel driver probe, update, shutdown and bus reset notification to
userspace drivers
- usually sleeps moderately long, in corner cases very long
- is scheduled by
- firewire-ohci's self-ID-complete IRQ handler tasklet via the
core's fw_node_event
- firewire-ohci's pci_remove method via core's fw_destroy_nodes/
fw_node_event
- itself during retries, e.g. while a node is powering up
iso_resource.work:
- accesses registers at the Isochronous Resource Manager node
- usually does not (but may in corner cases) sleep for long
- is scheduled via schedule_iso_resource() by
- the owning userspace driver at addition and removal of the
resource
- firewire-core's fw_device.work instances after bus reset
- itself in case of resource allocation if necessary to obey the
1000ms reallocation period after bus reset
fw_card.br_work instances should not, and instances of the others must
not, be executed in parallel by multiple CPUs -- but were not protected
against that. Hence allocate a non-reentrant workqueue for them.
fw_device.work may be used in the memory reclaim path in case of SBP-2
device updates. Hence we need a workqueue with rescuer and cannot use
system_nrt_wq.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Reviewed-by: Tejun Heo <tj@kernel.org>
-rw-r--r-- | drivers/firewire/core-card.c | 6 | ||||
-rw-r--r-- | drivers/firewire/core-cdev.c | 2 | ||||
-rw-r--r-- | drivers/firewire/core-device.c | 30 | ||||
-rw-r--r-- | drivers/firewire/core-transaction.c | 12 | ||||
-rw-r--r-- | drivers/firewire/core.h | 2 |
5 files changed, 36 insertions, 16 deletions
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index f05fc7bfceeb..bb8c4d22b03e 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c | |||
@@ -228,8 +228,8 @@ 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 (!schedule_delayed_work(&card->br_work, | 231 | if (!queue_delayed_work(fw_wq, &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 | } |
235 | EXPORT_SYMBOL(fw_schedule_bus_reset); | 235 | EXPORT_SYMBOL(fw_schedule_bus_reset); |
@@ -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 (!schedule_delayed_work(&card->br_work, 2 * HZ)) | 244 | if (!queue_delayed_work(fw_wq, &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 64768c2194f1..aa1131d26e30 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 (!schedule_delayed_work(&r->work, delay)) | 152 | if (!queue_delayed_work(fw_wq, &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 9a262439e3a7..ef900d923f15 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c | |||
@@ -725,6 +725,14 @@ 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; | ||
729 | |||
730 | static void fw_schedule_device_work(struct fw_device *device, | ||
731 | unsigned long delay) | ||
732 | { | ||
733 | queue_delayed_work(fw_wq, &device->work, delay); | ||
734 | } | ||
735 | |||
728 | /* | 736 | /* |
729 | * These defines control the retry behavior for reading the config | 737 | * These defines control the retry behavior for reading the config |
730 | * rom. It shouldn't be necessary to tweak these; if the device | 738 | * rom. It shouldn't be necessary to tweak these; if the device |
@@ -750,7 +758,7 @@ static void fw_device_shutdown(struct work_struct *work) | |||
750 | if (time_before64(get_jiffies_64(), | 758 | if (time_before64(get_jiffies_64(), |
751 | device->card->reset_jiffies + SHUTDOWN_DELAY) | 759 | device->card->reset_jiffies + SHUTDOWN_DELAY) |
752 | && !list_empty(&device->card->link)) { | 760 | && !list_empty(&device->card->link)) { |
753 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | 761 | fw_schedule_device_work(device, SHUTDOWN_DELAY); |
754 | return; | 762 | return; |
755 | } | 763 | } |
756 | 764 | ||
@@ -862,7 +870,7 @@ static int lookup_existing_device(struct device *dev, void *data) | |||
862 | fw_notify("rediscovered device %s\n", dev_name(dev)); | 870 | fw_notify("rediscovered device %s\n", dev_name(dev)); |
863 | 871 | ||
864 | PREPARE_DELAYED_WORK(&old->work, fw_device_update); | 872 | PREPARE_DELAYED_WORK(&old->work, fw_device_update); |
865 | schedule_delayed_work(&old->work, 0); | 873 | fw_schedule_device_work(old, 0); |
866 | 874 | ||
867 | if (current_node == card->root_node) | 875 | if (current_node == card->root_node) |
868 | fw_schedule_bm_work(card, 0); | 876 | fw_schedule_bm_work(card, 0); |
@@ -953,7 +961,7 @@ static void fw_device_init(struct work_struct *work) | |||
953 | if (device->config_rom_retries < MAX_RETRIES && | 961 | if (device->config_rom_retries < MAX_RETRIES && |
954 | atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { | 962 | atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { |
955 | device->config_rom_retries++; | 963 | device->config_rom_retries++; |
956 | schedule_delayed_work(&device->work, RETRY_DELAY); | 964 | fw_schedule_device_work(device, RETRY_DELAY); |
957 | } else { | 965 | } else { |
958 | if (device->node->link_on) | 966 | if (device->node->link_on) |
959 | fw_notify("giving up on config rom for node id %x\n", | 967 | fw_notify("giving up on config rom for node id %x\n", |
@@ -1019,7 +1027,7 @@ static void fw_device_init(struct work_struct *work) | |||
1019 | FW_DEVICE_INITIALIZING, | 1027 | FW_DEVICE_INITIALIZING, |
1020 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { | 1028 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { |
1021 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); | 1029 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
1022 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | 1030 | fw_schedule_device_work(device, SHUTDOWN_DELAY); |
1023 | } else { | 1031 | } else { |
1024 | if (device->config_rom_retries) | 1032 | if (device->config_rom_retries) |
1025 | fw_notify("created device %s: GUID %08x%08x, S%d00, " | 1033 | fw_notify("created device %s: GUID %08x%08x, S%d00, " |
@@ -1098,7 +1106,7 @@ static void fw_device_refresh(struct work_struct *work) | |||
1098 | if (device->config_rom_retries < MAX_RETRIES / 2 && | 1106 | if (device->config_rom_retries < MAX_RETRIES / 2 && |
1099 | atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { | 1107 | atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { |
1100 | device->config_rom_retries++; | 1108 | device->config_rom_retries++; |
1101 | schedule_delayed_work(&device->work, RETRY_DELAY / 2); | 1109 | fw_schedule_device_work(device, RETRY_DELAY / 2); |
1102 | 1110 | ||
1103 | return; | 1111 | return; |
1104 | } | 1112 | } |
@@ -1131,7 +1139,7 @@ static void fw_device_refresh(struct work_struct *work) | |||
1131 | if (device->config_rom_retries < MAX_RETRIES && | 1139 | if (device->config_rom_retries < MAX_RETRIES && |
1132 | atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { | 1140 | atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { |
1133 | device->config_rom_retries++; | 1141 | device->config_rom_retries++; |
1134 | schedule_delayed_work(&device->work, RETRY_DELAY); | 1142 | fw_schedule_device_work(device, RETRY_DELAY); |
1135 | 1143 | ||
1136 | return; | 1144 | return; |
1137 | } | 1145 | } |
@@ -1158,7 +1166,7 @@ static void fw_device_refresh(struct work_struct *work) | |||
1158 | gone: | 1166 | gone: |
1159 | atomic_set(&device->state, FW_DEVICE_GONE); | 1167 | atomic_set(&device->state, FW_DEVICE_GONE); |
1160 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); | 1168 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
1161 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | 1169 | fw_schedule_device_work(device, SHUTDOWN_DELAY); |
1162 | out: | 1170 | out: |
1163 | if (node_id == card->root_node->node_id) | 1171 | if (node_id == card->root_node->node_id) |
1164 | fw_schedule_bm_work(card, 0); | 1172 | fw_schedule_bm_work(card, 0); |
@@ -1214,7 +1222,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
1214 | * first config rom scan half a second after bus reset. | 1222 | * first config rom scan half a second after bus reset. |
1215 | */ | 1223 | */ |
1216 | INIT_DELAYED_WORK(&device->work, fw_device_init); | 1224 | INIT_DELAYED_WORK(&device->work, fw_device_init); |
1217 | schedule_delayed_work(&device->work, INITIAL_DELAY); | 1225 | fw_schedule_device_work(device, INITIAL_DELAY); |
1218 | break; | 1226 | break; |
1219 | 1227 | ||
1220 | case FW_NODE_INITIATED_RESET: | 1228 | case FW_NODE_INITIATED_RESET: |
@@ -1230,7 +1238,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
1230 | FW_DEVICE_RUNNING, | 1238 | FW_DEVICE_RUNNING, |
1231 | FW_DEVICE_INITIALIZING) == FW_DEVICE_RUNNING) { | 1239 | FW_DEVICE_INITIALIZING) == FW_DEVICE_RUNNING) { |
1232 | PREPARE_DELAYED_WORK(&device->work, fw_device_refresh); | 1240 | PREPARE_DELAYED_WORK(&device->work, fw_device_refresh); |
1233 | schedule_delayed_work(&device->work, | 1241 | fw_schedule_device_work(device, |
1234 | device->is_local ? 0 : INITIAL_DELAY); | 1242 | device->is_local ? 0 : INITIAL_DELAY); |
1235 | } | 1243 | } |
1236 | break; | 1244 | break; |
@@ -1245,7 +1253,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
1245 | device->generation = card->generation; | 1253 | device->generation = card->generation; |
1246 | if (atomic_read(&device->state) == FW_DEVICE_RUNNING) { | 1254 | if (atomic_read(&device->state) == FW_DEVICE_RUNNING) { |
1247 | PREPARE_DELAYED_WORK(&device->work, fw_device_update); | 1255 | PREPARE_DELAYED_WORK(&device->work, fw_device_update); |
1248 | schedule_delayed_work(&device->work, 0); | 1256 | fw_schedule_device_work(device, 0); |
1249 | } | 1257 | } |
1250 | break; | 1258 | break; |
1251 | 1259 | ||
@@ -1270,7 +1278,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
1270 | if (atomic_xchg(&device->state, | 1278 | if (atomic_xchg(&device->state, |
1271 | FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { | 1279 | FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { |
1272 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); | 1280 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
1273 | schedule_delayed_work(&device->work, | 1281 | fw_schedule_device_work(device, |
1274 | list_empty(&card->link) ? 0 : SHUTDOWN_DELAY); | 1282 | list_empty(&card->link) ? 0 : SHUTDOWN_DELAY); |
1275 | } | 1283 | } |
1276 | break; | 1284 | break; |
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 77275fdf6c1f..d4c28a217b2c 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/string.h> | 36 | #include <linux/string.h> |
37 | #include <linux/timer.h> | 37 | #include <linux/timer.h> |
38 | #include <linux/types.h> | 38 | #include <linux/types.h> |
39 | #include <linux/workqueue.h> | ||
39 | 40 | ||
40 | #include <asm/byteorder.h> | 41 | #include <asm/byteorder.h> |
41 | 42 | ||
@@ -1213,13 +1214,21 @@ static int __init fw_core_init(void) | |||
1213 | { | 1214 | { |
1214 | int ret; | 1215 | int ret; |
1215 | 1216 | ||
1217 | fw_wq = alloc_workqueue(KBUILD_MODNAME, | ||
1218 | WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); | ||
1219 | if (!fw_wq) | ||
1220 | return -ENOMEM; | ||
1221 | |||
1216 | ret = bus_register(&fw_bus_type); | 1222 | ret = bus_register(&fw_bus_type); |
1217 | if (ret < 0) | 1223 | if (ret < 0) { |
1224 | destroy_workqueue(fw_wq); | ||
1218 | return ret; | 1225 | return ret; |
1226 | } | ||
1219 | 1227 | ||
1220 | fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); | 1228 | fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); |
1221 | if (fw_cdev_major < 0) { | 1229 | if (fw_cdev_major < 0) { |
1222 | bus_unregister(&fw_bus_type); | 1230 | bus_unregister(&fw_bus_type); |
1231 | destroy_workqueue(fw_wq); | ||
1223 | return fw_cdev_major; | 1232 | return fw_cdev_major; |
1224 | } | 1233 | } |
1225 | 1234 | ||
@@ -1235,6 +1244,7 @@ static void __exit fw_core_cleanup(void) | |||
1235 | { | 1244 | { |
1236 | unregister_chrdev(fw_cdev_major, "firewire"); | 1245 | unregister_chrdev(fw_cdev_major, "firewire"); |
1237 | bus_unregister(&fw_bus_type); | 1246 | bus_unregister(&fw_bus_type); |
1247 | destroy_workqueue(fw_wq); | ||
1238 | idr_destroy(&fw_device_idr); | 1248 | idr_destroy(&fw_device_idr); |
1239 | } | 1249 | } |
1240 | 1250 | ||
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 0fe4e4e6eda7..00ea7730c6a7 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h | |||
@@ -140,6 +140,8 @@ 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; | ||
143 | 145 | ||
144 | struct fw_device *fw_device_get_by_devt(dev_t devt); | 146 | struct fw_device *fw_device_get_by_devt(dev_t devt); |
145 | int fw_device_set_broadcast_channel(struct device *dev, void *gen); | 147 | int fw_device_set_broadcast_channel(struct device *dev, void *gen); |