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:
@ -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;
|
||||
|
Reference in New Issue
Block a user