diff mbox series

[RFC,10/11] PCI: hotplug: movable bus numbers: rename proc and sysfs entries

Message ID 20191024172157.878735-11-s.miroshnichenko@yadro.com (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show
Series PCI: hotplug: Movable bus numbers | expand

Commit Message

Sergei Miroshnichenko Oct. 24, 2019, 5:21 p.m. UTC
Changing the number of a bus (therefore changing addresses of this bus, of
its children and all the buses next in the tree) invalidates entries in
/sys/devices/pci*, /proc/bus/pci/* and symlinks in /sys/bus/pci/devices/*
for all the renamed devices and buses.

Remove the affected proc and sysfs entries and symlinks before renaming the
bus, then created them back.

Signed-off-by: Sergey Miroshnichenko <s.miroshnichenko@yadro.com>
---
 drivers/pci/probe.c | 105 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 104 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index be9e5754cac7..fe9bf012ef33 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1096,12 +1096,99 @@  static void pci_enable_crs(struct pci_dev *pdev)
 					 PCI_EXP_RTCTL_CRSSVE);
 }
 
+static void pci_buses_remove_sysfs(int domain, int busnr, int max_bus_number)
+{
+	struct pci_bus *bus;
+	struct pci_dev *dev = NULL;
+
+	bus = pci_find_bus(domain, busnr);
+	if (!bus)
+		return;
+
+	if (busnr < max_bus_number)
+		pci_buses_remove_sysfs(domain, busnr + 1, max_bus_number);
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		device_remove_class_symlinks(&dev->dev);
+		pci_remove_sysfs_dev_files(dev);
+		pci_proc_detach_device(dev);
+		bus_disconnect_device(&dev->dev);
+	}
+
+	device_remove_class_symlinks(&bus->dev);
+	pci_proc_detach_bus(bus);
+}
+
+static void pci_buses_create_sysfs(int domain, int busnr, int max_bus_number)
+{
+	struct pci_bus *bus;
+	struct pci_dev *dev = NULL;
+
+	bus = pci_find_bus(domain, busnr);
+	if (!bus)
+		return;
+
+	device_add_class_symlinks(&bus->dev);
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		bus_add_device(&dev->dev);
+		if (pci_dev_is_added(dev)) {
+			pci_proc_attach_device(dev);
+			pci_create_sysfs_dev_files(dev);
+			device_add_class_symlinks(&dev->dev);
+		}
+	}
+
+	if (busnr < max_bus_number)
+		pci_buses_create_sysfs(domain, busnr + 1, max_bus_number);
+}
+
+static void pci_rename_bus(struct pci_bus *bus, const char *new_bus_name)
+{
+	struct class *class;
+	int err;
+
+	class = bus->dev.class;
+	bus->dev.class = NULL;
+	err = device_rename(&bus->dev, new_bus_name);
+	bus->dev.class = class;
+}
+
+static void pci_rename_bus_devices(struct pci_bus *bus, const int domain,
+				   const int new_busnr)
+{
+	struct pci_dev *dev = NULL;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		char old_name[64];
+		char new_name[64];
+		struct class *class;
+		int err;
+		int i;
+
+		strncpy(old_name, dev_name(&dev->dev), sizeof(old_name));
+		sprintf(new_name, "%04x:%02x:%02x.%d", domain, new_busnr,
+			PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+		class = dev->dev.class;
+		dev->dev.class = NULL;
+		err = device_rename(&dev->dev, new_name);
+		dev->dev.class = class;
+
+		for (i = 0; i < PCI_BRIDGE_RESOURCES; i++)
+			dev->resource[i].name = pci_name(dev);
+	}
+}
+
 static void pci_do_move_buses(const int domain, int busnr, int first_moved_busnr,
 			      int delta, const struct resource *valid_range)
 {
 	struct pci_bus *bus;
-	int subordinate;
+	int subordinate, old_primary;
 	u32 old_buses, buses;
+	char old_bus_name[64];
+	char new_bus_name[64];
+	struct resource old_res;
+	int new_busnr = busnr + delta;
 
 	if (busnr < valid_range->start || busnr > valid_range->end)
 		return;
@@ -1110,11 +1197,21 @@  static void pci_do_move_buses(const int domain, int busnr, int first_moved_busnr
 	if (!bus)
 		return;
 
+	old_primary = bus->primary;
+	strncpy(old_bus_name, dev_name(&bus->dev), sizeof(old_bus_name));
+	sprintf(new_bus_name, "%04x:%02x", domain, new_busnr);
+
 	if (delta > 0) {
 		pci_do_move_buses(domain, busnr + 1, first_moved_busnr,
 				  delta, valid_range);
+		pci_rename_bus_devices(bus, domain, new_busnr);
+		pci_rename_bus(bus, new_bus_name);
+	} else {
+		pci_rename_bus(bus, new_bus_name);
+		pci_rename_bus_devices(bus, domain, new_busnr);
 	}
 
+	memcpy(&old_res, &bus->busn_res, sizeof(old_res));
 	bus->number += delta;
 	bus->busn_res.start += delta;
 
@@ -1132,6 +1229,10 @@  static void pci_do_move_buses(const int domain, int busnr, int first_moved_busnr
 	buses |= (unsigned int)(subordinate << 16);
 	pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
 
+	dev_warn(&bus->dev, "Renamed bus %s (%02x-%pR) to %s (%02x-%pR)\n",
+		 old_bus_name, old_primary, &old_res,
+		 new_bus_name, bus->primary, &bus->busn_res);
+
 	if (delta < 0)
 		pci_do_move_buses(domain, busnr + 1, first_moved_busnr,
 				  delta, valid_range);
@@ -1192,8 +1293,10 @@  static int pci_move_buses(int domain, int busnr, int delta,
 		}
 	}
 
+	pci_buses_remove_sysfs(domain, busnr, valid_range->end);
 	pci_do_move_buses(domain, busnr, busnr,
 			  delta, valid_range);
+	pci_buses_create_sysfs(domain, busnr + delta, valid_range->end);
 
 	return 0;
 }