linuxlinux-kernellinux-device-driverpcipci-bus

pci_rescan_bus() doesn't rescan PCI bus in Linux


I am trying to rescan PCI bus in my own kernel driver with the help of pci_rescan_bus() kernel function but I do not see it is functioning same.

If I try to do same from user space by running following command, I see that rescanning happens:

echo 1 > /sys/devices/pci0000:00/0000:00:14.1/rescan

I am trying to re-initialize my ethernet ports which sit on PCI bus. Below is the code I am using for now:

struct pci_dev *pci_eth_dev01, *pci_eth_dev02 = NULL;
pci_eth_dev01 = pci_get_device(0x10ec, 0x8168, NULL);
if (pci_eth_dev01 != NULL)
    dev_info(&info->client->dev, "class - %2X\tbus number - %d\n", pci_eth_dev01->class, pci_eth_dev01->bus->number);
else
    dev_info(&info->client->dev, "Error retreiving pci device\n");

pci_eth_dev02 = pci_get_device(0x10ec, 0x8168, pci_eth_dev01);
if (pci_eth_dev02 != NULL)
    dev_info(&info->client->dev, "class - %2X\tbus number - %d\n", pci_eth_dev02->class, pci_eth_dev02->bus->number);
else
    dev_info(&info->client->dev, "Error retreiving pci device\n");

pci_stop_and_remove_bus_device(pci_eth_dev02);
pci_remove_bus(pci_eth_dev02->bus);

unsigned int ret = 0;
pci_lock_rescan_remove();
ret = pci_rescan_bus(pci_eth_dev02->bus);
pci_unlock_rescan_remove();
dev_info(&info->client->dev, "ret from pci_rescan_bus - %d\n", ret);

I get 2 as a return value from pci_rescan_bus() function call.

Am I doing anything wrong here?


Solution

  • Found that pci_rescan_bus(pci_eth_dev02->bus->parent) is the right call to make instead of pci_rescan_bus(pci_eth_dev02->bus) and with little more modifications this is how my current implementation looks like,

        static bool re_initialize_eth_ports(struct supercap_info *info)
        {
            struct pci_dev *pci_eth_dev01 = NULL;
            struct pci_dev *pci_eth_dev02 = NULL;
        
            // Get PCI device structures since ethernet ports sit on PCI bus.
            pci_eth_dev01 = pci_get_device(REALTEK_ETH_VENDOR_ID,
                                           REALTEK_ETH_DEVICE_ID, NULL);
            pci_eth_dev02 = pci_get_device(REALTEK_ETH_VENDOR_ID,
                                           REALTEK_ETH_DEVICE_ID, pci_eth_dev01);
            if (!pci_eth_dev01 || !pci_eth_dev02) {
                dev_err(&info->client->dev, "Error retrieving PCI device structure\n");
                return false;
            }
            dev_dbg(&info->client->dev, "Device Class - 0x%X\tPCI Bus Number - %d\n",
                    pci_eth_dev01->class, pci_eth_dev01->bus->number);
            dev_dbg(&info->client->dev, "Device Class - 0x%X\tPCI Bus Number - %d\n",
                    pci_eth_dev02->class, pci_eth_dev02->bus->number);
            // Remove PCI bus devices from the device lists.
            pci_stop_and_remove_bus_device_locked(pci_eth_dev01);
            pci_stop_and_remove_bus_device_locked(pci_eth_dev02);
            // Acquire a mutex lock before re-scanning PCI buses.
            pci_lock_rescan_remove();
            // Re-scan PCI parent buses for devices.
            pci_rescan_bus(pci_eth_dev01->bus->parent);
            pci_rescan_bus(pci_eth_dev02->bus->parent);
            // Release a mutex lock.
            pci_unlock_rescan_remove();
            return true;
        }