Commit 594a6a27 authored by Takashi Sakamoto's avatar Takashi Sakamoto
Browse files

firewire: core: clear sources of hardware interrupt at card removal

Due to the factors external to the system, hardware events may still be
handled while a card instance is being removed. The sources of hardware
IRQs should be cleared during card removal so that workqueues can be safely
destroyed.

This commit adds a disable callback to the underlying driver operations.
After this callback returns, the underlying driver guarantees that it
will no longer handle hardware events.

Link: https://lore.kernel.org/r/20251109065525.163464-1-o-takashi@sakamocchi.jp


Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
parent fa2dc271
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -784,9 +784,12 @@ void fw_core_remove_card(struct fw_card *card)
	/* Switch off most of the card driver interface. */
	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;

	drain_workqueue(card->isoc_wq);
	drain_workqueue(card->async_wq);
	card->driver->disable(card);

	scoped_guard(spinlock_irqsave, &card->lock)
		fw_destroy_nodes(card);
+3 −0
Original line number Diff line number Diff line
@@ -65,6 +65,9 @@ struct fw_card_driver {
	int (*enable)(struct fw_card *card,
		      const __be32 *config_rom, size_t length);

	// After returning the call, any function is no longer triggered to handle hardware event.
	void (*disable)(struct fw_card *card);

	int (*read_phy_reg)(struct fw_card *card, int address);
	int (*update_phy_reg)(struct fw_card *card, int address,
			      int clear_bits, int set_bits);
+36 −8
Original line number Diff line number Diff line
@@ -2408,6 +2408,41 @@ static int ohci_enable(struct fw_card *card,
	return 0;
}

static void ohci_disable(struct fw_card *card)
{
	struct pci_dev *pdev = to_pci_dev(card->device);
	struct fw_ohci *ohci = pci_get_drvdata(pdev);
	int i, irq = pci_irq_vector(pdev, 0);

	// If the removal is happening from the suspend state, LPS won't be enabled and host
	// registers (eg., IntMaskClear) won't be accessible.
	if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_LPS))
		return;

	reg_write(ohci, OHCI1394_IntMaskClear, ~0);
	flush_writes(ohci);

	if (irq >= 0)
		synchronize_irq(irq);

	flush_work(&ohci->ar_request_ctx.work);
	flush_work(&ohci->ar_response_ctx.work);
	flush_work(&ohci->at_request_ctx.work);
	flush_work(&ohci->at_response_ctx.work);

	for (i = 0; i < ohci->n_ir; ++i) {
		if (!(ohci->ir_context_mask & BIT(i)))
			flush_work(&ohci->ir_context_list[i].base.work);
	}
	for (i = 0; i < ohci->n_it; ++i) {
		if (!(ohci->it_context_mask & BIT(i)))
			flush_work(&ohci->it_context_list[i].base.work);
	}

	at_context_flush(&ohci->at_request_ctx);
	at_context_flush(&ohci->at_response_ctx);
}

static int ohci_set_config_rom(struct fw_card *card,
			       const __be32 *config_rom, size_t length)
{
@@ -3442,6 +3477,7 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base)

static const struct fw_card_driver ohci_driver = {
	.enable			= ohci_enable,
	.disable		= ohci_disable,
	.read_phy_reg		= ohci_read_phy_reg,
	.update_phy_reg		= ohci_update_phy_reg,
	.set_config_rom		= ohci_set_config_rom,
@@ -3681,14 +3717,6 @@ static void pci_remove(struct pci_dev *dev)
	struct fw_ohci *ohci = pci_get_drvdata(dev);
	int irq;

	/*
	 * If the removal is happening from the suspend state, LPS won't be
	 * enabled and host registers (eg., IntMaskClear) won't be accessible.
	 */
	if (reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_LPS) {
		reg_write(ohci, OHCI1394_IntMaskClear, ~0);
		flush_writes(ohci);
	}
	fw_core_remove_card(&ohci->card);

	/*