]> Gentwo Git Trees - linux/.git/commitdiff
firewire: core: clear sources of hardware interrupt at card removal
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Sun, 9 Nov 2025 06:55:25 +0000 (15:55 +0900)
committerTakashi Sakamoto <o-takashi@sakamocchi.jp>
Sun, 9 Nov 2025 06:59:14 +0000 (15:59 +0900)
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: Takashi Sakamoto <o-takashi@sakamocchi.jp>
drivers/firewire/core-card.c
drivers/firewire/core.h
drivers/firewire/ohci.c

index 6979d6a88ae212b75d9cb25e9768af5521b93484..65bd9db996c05a21441008e599a2362f1bc8dd55 100644 (file)
@@ -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);
index e67395ce26b5e3c44c9310c4b88cb20b243cae1b..903812b6bb3f7942cf533a04711d3b65dab4abf9 100644 (file)
@@ -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);
index 757dd9c64b1c0dadd79cda1b51b7bc525b14e1f0..0625d11dbd745ec8923ae99c57126ebb27829b49 100644 (file)
@@ -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);
 
        /*