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>
This commit is contained in:
Родитель
13882a82ee
Коммит
6ea9e7bbfc
|
@ -228,8 +228,8 @@ void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset)
|
|||
|
||||
/* Use an arbitrary short delay to combine multiple reset requests. */
|
||||
fw_card_get(card);
|
||||
if (!schedule_delayed_work(&card->br_work,
|
||||
delayed ? DIV_ROUND_UP(HZ, 100) : 0))
|
||||
if (!queue_delayed_work(fw_wq, &card->br_work,
|
||||
delayed ? DIV_ROUND_UP(HZ, 100) : 0))
|
||||
fw_card_put(card);
|
||||
}
|
||||
EXPORT_SYMBOL(fw_schedule_bus_reset);
|
||||
|
@ -241,7 +241,7 @@ static void br_work(struct work_struct *work)
|
|||
/* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */
|
||||
if (card->reset_jiffies != 0 &&
|
||||
time_before64(get_jiffies_64(), card->reset_jiffies + 2 * HZ)) {
|
||||
if (!schedule_delayed_work(&card->br_work, 2 * HZ))
|
||||
if (!queue_delayed_work(fw_wq, &card->br_work, 2 * HZ))
|
||||
fw_card_put(card);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ static void release_iso_resource(struct client *, struct client_resource *);
|
|||
static void schedule_iso_resource(struct iso_resource *r, unsigned long delay)
|
||||
{
|
||||
client_get(r->client);
|
||||
if (!schedule_delayed_work(&r->work, delay))
|
||||
if (!queue_delayed_work(fw_wq, &r->work, delay))
|
||||
client_put(r->client);
|
||||
}
|
||||
|
||||
|
|
|
@ -725,6 +725,14 @@ struct fw_device *fw_device_get_by_devt(dev_t devt)
|
|||
return device;
|
||||
}
|
||||
|
||||
struct workqueue_struct *fw_wq;
|
||||
|
||||
static void fw_schedule_device_work(struct fw_device *device,
|
||||
unsigned long delay)
|
||||
{
|
||||
queue_delayed_work(fw_wq, &device->work, delay);
|
||||
}
|
||||
|
||||
/*
|
||||
* These defines control the retry behavior for reading the config
|
||||
* 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)
|
|||
if (time_before64(get_jiffies_64(),
|
||||
device->card->reset_jiffies + SHUTDOWN_DELAY)
|
||||
&& !list_empty(&device->card->link)) {
|
||||
schedule_delayed_work(&device->work, SHUTDOWN_DELAY);
|
||||
fw_schedule_device_work(device, SHUTDOWN_DELAY);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -862,7 +870,7 @@ static int lookup_existing_device(struct device *dev, void *data)
|
|||
fw_notify("rediscovered device %s\n", dev_name(dev));
|
||||
|
||||
PREPARE_DELAYED_WORK(&old->work, fw_device_update);
|
||||
schedule_delayed_work(&old->work, 0);
|
||||
fw_schedule_device_work(old, 0);
|
||||
|
||||
if (current_node == card->root_node)
|
||||
fw_schedule_bm_work(card, 0);
|
||||
|
@ -953,7 +961,7 @@ static void fw_device_init(struct work_struct *work)
|
|||
if (device->config_rom_retries < MAX_RETRIES &&
|
||||
atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
|
||||
device->config_rom_retries++;
|
||||
schedule_delayed_work(&device->work, RETRY_DELAY);
|
||||
fw_schedule_device_work(device, RETRY_DELAY);
|
||||
} else {
|
||||
if (device->node->link_on)
|
||||
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)
|
|||
FW_DEVICE_INITIALIZING,
|
||||
FW_DEVICE_RUNNING) == FW_DEVICE_GONE) {
|
||||
PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
|
||||
schedule_delayed_work(&device->work, SHUTDOWN_DELAY);
|
||||
fw_schedule_device_work(device, SHUTDOWN_DELAY);
|
||||
} else {
|
||||
if (device->config_rom_retries)
|
||||
fw_notify("created device %s: GUID %08x%08x, S%d00, "
|
||||
|
@ -1098,7 +1106,7 @@ static void fw_device_refresh(struct work_struct *work)
|
|||
if (device->config_rom_retries < MAX_RETRIES / 2 &&
|
||||
atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
|
||||
device->config_rom_retries++;
|
||||
schedule_delayed_work(&device->work, RETRY_DELAY / 2);
|
||||
fw_schedule_device_work(device, RETRY_DELAY / 2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1131,7 +1139,7 @@ static void fw_device_refresh(struct work_struct *work)
|
|||
if (device->config_rom_retries < MAX_RETRIES &&
|
||||
atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
|
||||
device->config_rom_retries++;
|
||||
schedule_delayed_work(&device->work, RETRY_DELAY);
|
||||
fw_schedule_device_work(device, RETRY_DELAY);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1158,7 +1166,7 @@ static void fw_device_refresh(struct work_struct *work)
|
|||
gone:
|
||||
atomic_set(&device->state, FW_DEVICE_GONE);
|
||||
PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
|
||||
schedule_delayed_work(&device->work, SHUTDOWN_DELAY);
|
||||
fw_schedule_device_work(device, SHUTDOWN_DELAY);
|
||||
out:
|
||||
if (node_id == card->root_node->node_id)
|
||||
fw_schedule_bm_work(card, 0);
|
||||
|
@ -1214,7 +1222,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
|
|||
* first config rom scan half a second after bus reset.
|
||||
*/
|
||||
INIT_DELAYED_WORK(&device->work, fw_device_init);
|
||||
schedule_delayed_work(&device->work, INITIAL_DELAY);
|
||||
fw_schedule_device_work(device, INITIAL_DELAY);
|
||||
break;
|
||||
|
||||
case FW_NODE_INITIATED_RESET:
|
||||
|
@ -1230,7 +1238,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
|
|||
FW_DEVICE_RUNNING,
|
||||
FW_DEVICE_INITIALIZING) == FW_DEVICE_RUNNING) {
|
||||
PREPARE_DELAYED_WORK(&device->work, fw_device_refresh);
|
||||
schedule_delayed_work(&device->work,
|
||||
fw_schedule_device_work(device,
|
||||
device->is_local ? 0 : INITIAL_DELAY);
|
||||
}
|
||||
break;
|
||||
|
@ -1245,7 +1253,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
|
|||
device->generation = card->generation;
|
||||
if (atomic_read(&device->state) == FW_DEVICE_RUNNING) {
|
||||
PREPARE_DELAYED_WORK(&device->work, fw_device_update);
|
||||
schedule_delayed_work(&device->work, 0);
|
||||
fw_schedule_device_work(device, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1270,7 +1278,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
|
|||
if (atomic_xchg(&device->state,
|
||||
FW_DEVICE_GONE) == FW_DEVICE_RUNNING) {
|
||||
PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
|
||||
schedule_delayed_work(&device->work,
|
||||
fw_schedule_device_work(device,
|
||||
list_empty(&card->link) ? 0 : SHUTDOWN_DELAY);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
|
@ -1213,13 +1214,21 @@ static int __init fw_core_init(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
fw_wq = alloc_workqueue(KBUILD_MODNAME,
|
||||
WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0);
|
||||
if (!fw_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = bus_register(&fw_bus_type);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
destroy_workqueue(fw_wq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
|
||||
if (fw_cdev_major < 0) {
|
||||
bus_unregister(&fw_bus_type);
|
||||
destroy_workqueue(fw_wq);
|
||||
return fw_cdev_major;
|
||||
}
|
||||
|
||||
|
@ -1235,6 +1244,7 @@ static void __exit fw_core_cleanup(void)
|
|||
{
|
||||
unregister_chrdev(fw_cdev_major, "firewire");
|
||||
bus_unregister(&fw_bus_type);
|
||||
destroy_workqueue(fw_wq);
|
||||
idr_destroy(&fw_device_idr);
|
||||
}
|
||||
|
||||
|
|
|
@ -140,6 +140,8 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p);
|
|||
extern struct rw_semaphore fw_device_rwsem;
|
||||
extern struct idr fw_device_idr;
|
||||
extern int fw_cdev_major;
|
||||
struct workqueue_struct;
|
||||
extern struct workqueue_struct *fw_wq;
|
||||
|
||||
struct fw_device *fw_device_get_by_devt(dev_t devt);
|
||||
int fw_device_set_broadcast_channel(struct device *dev, void *gen);
|
||||
|
|
Загрузка…
Ссылка в новой задаче