Re: a small problem about pci_stop_and_remove_bus_device() function

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Aug 29, 2012 at 2:58 AM, Yijing Wang <wangyijing@xxxxxxxxxx> wrote:
> Hi Bjorn,
>    The drivers/pci/remove.c now is so clean after applied series "[PATCH v2 00/16] Clean up drivers/pci/remove.c"(very good ^_^).
> I have a small problem about pci_stop_and_remove_bus_device() function. Traditionally, pci_bus is removed after its pci bridge stop.
> But now pci_bus is remove before its pci brdige.

Let me restate this to make sure I understand the problem.  Prior to
my cleanups, the flow when removing a bridge device was like this:

    pci_stop_and_remove_bus_device(bridge)
        pci_stop_bus_device(bridge)
            pci_stop_bus_devices(bridge->subordinate)
            pci_stop_dev(bridge)
                device_unregister(bridge->dev)
                    device_del(bridge->dev)
                        blocking_notifier_call_chain(pci_bus_type, ...)
                            <notifier>
                                if (dev->bus == &pci_bus_type)
                                    pdev = to_pci_dev(dev)
                                    if (pdev->subordinate)
                                        ...
        __pci_remove_bus_device(bridge)
            if (bridge->subordinate)
                pci_remove_bus(bridge->subordinate)
                bridge->subordinate = NULL

After my changes, the flow is like this:

    pci_stop_and_remove_bus_device(bridge)
        pci_remove_bus(bridge->subordinate)
        bridge->subordinate = NULL
        pci_stop_dev(bridge)
            device_unregister(bridge->dev)
                device_del(bridge->dev)
                    blocking_notifier_call_chain(pci_bus_type, ...)
                        <notifier>
                            if (dev->bus == &pci_bus_type)
                                pdev = to_pci_dev(dev)
                                if (pdev->subordinate)
                                    ** broken because
pdev->subordinate is always NULL **

The problem is that before, a notifier could check "dev->subordinate"
to identify a bridge device, but now, "dev->subordinate" has already
been cleared by the time the notifier runs, so that test no longer
identifies bridges.

I didn't intend this change in behavior, so in that sense, this is a
regression.  We could restore the previous behavior by changing
pci_stop_and_remove_bus_device() so the "pci_remove_bus();
dev->subordinate = NULL;" code is after the pci_stop_dev() call, as
you suggest.

But I'm not 100% sure that's the correct fix.  I think it's possible
to have a bridge device that has no secondary bus.  For example, I
don't think a bridge configured with "secondary > subordinate", e.g.,
"[bus ff-00]", will forward any config transactions downstream.  In
that case, we'll have a struct pci_dev for the bridge device, but
there won't be a struct pci_bus for the secondary bus, so
dev->subordinate will be NULL.  There are actually quite a few
existing tests for this situation in pnv_ioda_setup_bus_PE(),
eeh_add_device_tree_late(), yenta_probe(), etc.

When we enumerate bridges, we build the bridge pci_dev before building
the downstream pci_bus, so symmetry suggests that we should tear down
the pci_bus before tearing down the pci_dev.

So I wonder if a better fix is remove the assumption that
"dev->subordinate != NULL means this is a bridge device."  There are
many places where we test "dev->subordinate" to iterate through
downstream devices or something similar; those should be fine.  We'd
only have to change the places that care about actual type of the
device, e.g., the config space differences between header types.

Where did you trip over this?  If you just found this by inspection,
my congratulations, it's a pretty subtle issue :)

> So If I use pci_bus_type notifier, I can't identify pci_dev whether is bridge(by pci_dev->subordinate)
> when notifier arrives.
>    What about move pci_remove_bus after stop pci bridge?
>
>  82     if (bus) {
>  83         list_for_each_entry_safe_reverse(child, tmp,
>  84                          &bus->devices, bus_list)
>  85             pci_stop_and_remove_bus_device(child);
>  86
>  87     }
>  88
>  89     pci_stop_dev(dev);
>  90     if (bus) {
>  91         pci_remove_bus(bus);
>  92         dev->subordinate = NULL;
>  93     }
>  94     pci_destroy_dev(dev);
>
>
> --
> Thanks!
> Yijing
>
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux