firewire: core: use release/acquire pair when replacing underlying driver on card removal

When removing a card instance, the underlying driver is replaced to handle
closing operations. Without proper memory reordering, other CPUs may
observe the driver before it is fully initialized.

Use a release/acquire pair to ensure that the driver's initialization is
visible before the driver is accessed for close operations.

Link: https://lore.kernel.org/r/20261017081720.186688-1-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
This commit is contained in:
Takashi Sakamoto 2026-10-17 17:17:20 +09:00
parent aa7f13e25c
commit 23b790766a
5 changed files with 47 additions and 31 deletions

View File

@ -153,8 +153,8 @@ static void update_config_roms(void)
list_for_each_entry (card, &card_list, link) {
generate_config_rom(card, tmp_config_rom);
card->driver->set_config_rom(card, tmp_config_rom,
config_rom_length);
smp_load_acquire(&card->driver)->set_config_rom(card, tmp_config_rom,
config_rom_length);
}
}
@ -231,7 +231,7 @@ static int reset_bus(struct fw_card *card, bool short_reset)
trace_bus_reset_initiate(card->index, card->generation, short_reset);
return card->driver->update_phy_reg(card, reg, 0, bit);
return smp_load_acquire(&card->driver)->update_phy_reg(card, reg, 0, bit);
}
void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset)
@ -660,7 +660,8 @@ int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid,
scoped_guard(mutex, &card_mutex) {
generate_config_rom(card, tmp_config_rom);
ret = card->driver->enable(card, tmp_config_rom, config_rom_length);
ret = smp_load_acquire(&card->driver)->enable(card, tmp_config_rom,
config_rom_length);
if (ret < 0) {
card->isoc_wq = NULL;
card->async_wq = NULL;
@ -804,7 +805,8 @@ void fw_core_remove_card(struct fw_card *card)
dummy_driver.free_iso_context = card->driver->free_iso_context;
dummy_driver.stop_iso = card->driver->stop_iso;
dummy_driver.disable = card->driver->disable;
card->driver = &dummy_driver;
// Ensure the entire contents of the driver are visible to other CPUs.
smp_store_release(&card->driver, &dummy_driver);
drain_workqueue(card->isoc_wq);
drain_workqueue(card->async_wq);
@ -843,12 +845,12 @@ EXPORT_SYMBOL(fw_core_remove_card);
*/
int fw_card_read_cycle_time(struct fw_card *card, u32 *cycle_time)
{
if (card->driver->read_csr == dummy_read_csr)
if (smp_load_acquire(&card->driver)->read_csr == dummy_read_csr)
return -ENODEV;
// It's possible to switch to dummy driver between the above and the below. This is the best
// effort to return -ENODEV.
*cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME);
*cycle_time = smp_load_acquire(&card->driver)->read_csr(card, CSR_CYCLE_TIME);
return 0;
}
EXPORT_SYMBOL_GPL(fw_card_read_cycle_time);

View File

@ -1731,7 +1731,7 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
trace_async_phy_outbound_initiate((uintptr_t)&e->p, card->index, e->p.generation,
e->p.header[1], e->p.header[2]);
card->driver->send_request(card, &e->p);
smp_load_acquire(&card->driver)->send_request(card, &e->p);
return 0;
}

View File

@ -261,14 +261,14 @@ EXPORT_SYMBOL(fw_bus_type);
int fw_device_enable_phys_dma(struct fw_device *device)
{
int generation = device->generation;
struct fw_card *card = device->card;
/* device->node_id, accessed below, must not be older than generation */
int generation = device->generation;
smp_rmb();
int node_id = device->node_id;
return device->card->driver->enable_phys_dma(device->card,
device->node_id,
generation);
return smp_load_acquire(&card->driver)->enable_phys_dma(device->card, node_id, generation);
}
EXPORT_SYMBOL(fw_device_enable_phys_dma);

View File

@ -144,8 +144,8 @@ struct fw_iso_context *__fw_iso_context_create(struct fw_card *card, int type, i
{
struct fw_iso_context *ctx;
ctx = card->driver->allocate_iso_context(card, type, channel, header_size,
header_storage_size);
ctx = smp_load_acquire(&card->driver)->allocate_iso_context(card, type, channel,
header_size, header_storage_size);
if (IS_ERR(ctx))
return ctx;
@ -173,7 +173,9 @@ void fw_iso_context_destroy(struct fw_iso_context *ctx)
trace_isoc_inbound_single_destroy(ctx);
trace_isoc_inbound_multiple_destroy(ctx);
ctx->card->driver->free_iso_context(ctx);
struct fw_card *card = ctx->card;
smp_load_acquire(&card->driver)->free_iso_context(ctx);
}
EXPORT_SYMBOL(fw_iso_context_destroy);
@ -184,7 +186,9 @@ int fw_iso_context_start(struct fw_iso_context *ctx,
trace_isoc_inbound_single_start(ctx, cycle, sync, tags);
trace_isoc_inbound_multiple_start(ctx, cycle, sync, tags);
return ctx->card->driver->start_iso(ctx, cycle, sync, tags);
struct fw_card *card = ctx->card;
return smp_load_acquire(&card->driver)->start_iso(ctx, cycle, sync, tags);
}
EXPORT_SYMBOL(fw_iso_context_start);
@ -192,7 +196,9 @@ int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels)
{
trace_isoc_inbound_multiple_channels(ctx, *channels);
return ctx->card->driver->set_iso_channels(ctx, channels);
struct fw_card *card = ctx->card;
return smp_load_acquire(&card->driver)->set_iso_channels(ctx, channels);
}
int fw_iso_context_queue(struct fw_iso_context *ctx,
@ -204,7 +210,9 @@ int fw_iso_context_queue(struct fw_iso_context *ctx,
trace_isoc_inbound_single_queue(ctx, payload, packet);
trace_isoc_inbound_multiple_queue(ctx, payload, packet);
return ctx->card->driver->queue_iso(ctx, packet, buffer, payload);
struct fw_card *card = ctx->card;
return smp_load_acquire(&card->driver)->queue_iso(ctx, packet, buffer, payload);
}
EXPORT_SYMBOL(fw_iso_context_queue);
@ -214,7 +222,9 @@ void fw_iso_context_queue_flush(struct fw_iso_context *ctx)
trace_isoc_inbound_single_flush(ctx);
trace_isoc_inbound_multiple_flush(ctx);
ctx->card->driver->flush_queue_iso(ctx);
struct fw_card *card = ctx->card;
smp_load_acquire(&card->driver)->flush_queue_iso(ctx);
}
EXPORT_SYMBOL(fw_iso_context_queue_flush);
@ -248,7 +258,9 @@ int fw_iso_context_flush_completions(struct fw_iso_context *ctx)
disable_work_sync(&ctx->work);
err = ctx->card->driver->flush_iso_completions(ctx);
struct fw_card *card = ctx->card;
err = smp_load_acquire(&card->driver)->flush_iso_completions(ctx);
enable_work(&ctx->work);
@ -270,7 +282,9 @@ int fw_iso_context_stop(struct fw_iso_context *ctx)
if (WARN_ON_ONCE(current_work() == &ctx->work))
return 0;
err = ctx->card->driver->stop_iso(ctx);
struct fw_card *card = ctx->card;
err = smp_load_acquire(&card->driver)->stop_iso(ctx);
cancel_work_sync(&ctx->work);

View File

@ -133,7 +133,7 @@ __must_not_hold(&card->transactions.lock)
// Cancel the packet transmission if it's still queued. That will schedule the packet
// transmission callback which cancels the transaction.
if (card->driver->cancel_packet(card, &transaction->packet) == 0)
if (smp_load_acquire(&card->driver)->cancel_packet(card, &transaction->packet) == 0)
return 0;
// If the request packet has already been sent, we need to see if the transaction is still
@ -462,7 +462,7 @@ __must_not_hold(&card->transactions.lock)
t->packet.header, payload,
tcode_is_read_request(tcode) ? 0 : length / 4);
card->driver->send_request(card, &t->packet);
smp_load_acquire(&card->driver)->send_request(card, &t->packet);
}
EXPORT_SYMBOL_GPL(__fw_send_request);
@ -559,7 +559,7 @@ void fw_send_phy_config(struct fw_card *card,
}
if (gap_count == FW_PHY_CONFIG_CURRENT_GAP_COUNT) {
gap_count = card->driver->read_phy_reg(card, 1);
gap_count = smp_load_acquire(&card->driver)->read_phy_reg(card, 1);
if (gap_count < 0)
return;
@ -582,7 +582,7 @@ void fw_send_phy_config(struct fw_card *card,
phy_config_packet.generation, phy_config_packet.header[1],
phy_config_packet.header[2]);
card->driver->send_request(card, &phy_config_packet);
smp_load_acquire(&card->driver)->send_request(card, &phy_config_packet);
wait_for_completion_timeout(&phy_config_done, timeout);
}
@ -991,7 +991,7 @@ void fw_send_response(struct fw_card *card,
request->response.header, data,
data ? data_length / 4 : 0);
card->driver->send_response(card, &request->response);
smp_load_acquire(&card->driver)->send_response(card, &request->response);
}
EXPORT_SYMBOL(fw_send_response);
@ -1242,7 +1242,7 @@ __must_not_hold(&card->transactions.lock)
* The response handler may be executed while the request handler
* is still pending. Cancel the request handler.
*/
card->driver->cancel_packet(card, &t->packet);
smp_load_acquire(&card->driver)->cancel_packet(card, &t->packet);
invoke_callback(t, rcode, p->timestamp, data, data_length);
}
@ -1361,17 +1361,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request,
case CSR_BUS_TIME:
case CSR_BUSY_TIMEOUT:
if (tcode == TCODE_READ_QUADLET_REQUEST)
*data = cpu_to_be32(card->driver->read_csr(card, reg));
*data = cpu_to_be32(smp_load_acquire(&card->driver)->read_csr(card, reg));
else if (tcode == TCODE_WRITE_QUADLET_REQUEST)
card->driver->write_csr(card, reg, be32_to_cpu(*data));
smp_load_acquire(&card->driver)->write_csr(card, reg, be32_to_cpu(*data));
else
rcode = RCODE_TYPE_ERROR;
break;
case CSR_RESET_START:
if (tcode == TCODE_WRITE_QUADLET_REQUEST)
card->driver->write_csr(card, CSR_STATE_CLEAR,
CSR_STATE_BIT_ABDICATE);
smp_load_acquire(&card->driver)->write_csr(card, CSR_STATE_CLEAR,
CSR_STATE_BIT_ABDICATE);
else
rcode = RCODE_TYPE_ERROR;
break;