diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index e6395b298508..a03462750b95 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -398,6 +399,7 @@ fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver, static atomic_t index = ATOMIC_INIT(-1); kref_init(&card->kref); + atomic_set(&card->device_count, 0); card->index = atomic_inc_return(&index); card->driver = driver; card->device = device; @@ -528,8 +530,14 @@ fw_core_remove_card(struct fw_card *card) card->driver = &dummy_driver; fw_destroy_nodes(card); - flush_scheduled_work(); + /* + * Wait for all device workqueue jobs to finish. Otherwise the + * firewire-core module could be unloaded before the jobs ran. + */ + while (atomic_read(&card->device_count) > 0) + msleep(100); + cancel_delayed_work_sync(&card->work); fw_flush_transactions(card); del_timer_sync(&card->flush_timer); diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 2ab13e0f3469..870125a3638e 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c @@ -150,21 +150,10 @@ struct bus_type fw_bus_type = { }; EXPORT_SYMBOL(fw_bus_type); -struct fw_device *fw_device_get(struct fw_device *device) -{ - get_device(&device->device); - - return device; -} - -void fw_device_put(struct fw_device *device) -{ - put_device(&device->device); -} - static void fw_device_release(struct device *dev) { struct fw_device *device = fw_device(dev); + struct fw_card *card = device->card; unsigned long flags; /* @@ -176,9 +165,9 @@ static void fw_device_release(struct device *dev) spin_unlock_irqrestore(&device->card->lock, flags); fw_node_put(device->node); - fw_card_put(device->card); kfree(device->config_rom); kfree(device); + atomic_dec(&card->device_count); } int fw_device_enable_phys_dma(struct fw_device *device) @@ -668,7 +657,8 @@ static void fw_device_init(struct work_struct *work) */ if (read_bus_info_block(device, device->generation) < 0) { - if (device->config_rom_retries < MAX_RETRIES) { + 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); } else { @@ -805,7 +795,8 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) */ device_initialize(&device->device); atomic_set(&device->state, FW_DEVICE_INITIALIZING); - device->card = fw_card_get(card); + atomic_inc(&card->device_count); + device->card = card; device->node = fw_node_get(node); device->node_id = node->node_id; device->generation = card->generation; diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h index 43808c02793e..78ecd3991b7f 100644 --- a/drivers/firewire/fw-device.h +++ b/drivers/firewire/fw-device.h @@ -76,9 +76,21 @@ fw_device_is_shutdown(struct fw_device *device) return atomic_read(&device->state) == FW_DEVICE_SHUTDOWN; } -struct fw_device *fw_device_get(struct fw_device *device); +static inline struct fw_device * +fw_device_get(struct fw_device *device) +{ + get_device(&device->device); + + return device; +} + +static inline void +fw_device_put(struct fw_device *device) +{ + put_device(&device->device); +} + struct fw_device *fw_device_get_by_devt(dev_t devt); -void fw_device_put(struct fw_device *device); int fw_device_enable_phys_dma(struct fw_device *device); void fw_device_cdev_update(struct fw_device *device); diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index a093ac329db7..03069a454c07 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -769,6 +769,7 @@ static void sbp2_release_target(struct kref *kref) struct Scsi_Host *shost = container_of((void *)tgt, struct Scsi_Host, hostdata[0]); struct scsi_device *sdev; + struct fw_device *device = fw_device(tgt->unit->device.parent); /* prevent deadlocks */ sbp2_unblock(tgt); @@ -791,6 +792,7 @@ static void sbp2_release_target(struct kref *kref) put_device(&tgt->unit->device); scsi_host_put(shost); + fw_device_put(device); } static struct workqueue_struct *sbp2_wq; @@ -1088,6 +1090,8 @@ static int sbp2_probe(struct device *dev) if (scsi_add_host(shost, &unit->device) < 0) goto fail_shost_put; + fw_device_get(device); + /* Initialize to values that won't match anything in our table. */ firmware_revision = 0xff000000; model = 0xff000000; diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index fa7967b57408..09cb72870454 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h @@ -26,6 +26,7 @@ #include #include #include +#include #define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4) #define TCODE_IS_BLOCK_PACKET(tcode) (((tcode) & 1) != 0) @@ -219,6 +220,7 @@ extern struct bus_type fw_bus_type; struct fw_card { const struct fw_card_driver *driver; struct device *device; + atomic_t device_count; struct kref kref; int node_id;