Commit 5d756f3f authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/locking'

- Make pci_stop_dev() and pci_destroy_dev() concurrent safe (Keith Busch)

- Move __pci_walk_bus() mutex up into the caller, which avoids the need for
  a parameter to control locking (Keith Busch)

- Simplify __pci_walk_bus() by making it recursive (Keith Busch)

- Unexport pci_walk_bus_locked(), which is only used internally by the PCI
  core (Keith Busch)

* pci/locking:
  PCI: Unexport pci_walk_bus_locked()
  PCI: Convert __pci_walk_bus() to be recursive
  PCI: Move __pci_walk_bus() mutex to where we need it
  PCI: Make pci_destroy_dev() concurrent safe
  PCI: Make pci_stop_dev() concurrent safe
parents 665e4a34 38a18dfe
Loading
Loading
Loading
Loading
+16 −33
Original line number Diff line number Diff line
@@ -358,7 +358,7 @@ void pci_bus_add_device(struct pci_dev *dev)
	if (retval < 0 && retval != -EPROBE_DEFER)
		pci_warn(dev, "device attach failed (%d)\n", retval);

	pci_dev_assign_added(dev, true);
	pci_dev_assign_added(dev);

	if (dev_of_node(&dev->dev) && pci_is_bridge(dev)) {
		retval = of_platform_populate(dev_of_node(&dev->dev), NULL, NULL,
@@ -399,41 +399,23 @@ void pci_bus_add_devices(const struct pci_bus *bus)
}
EXPORT_SYMBOL(pci_bus_add_devices);

static void __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
			   void *userdata, bool locked)
static int __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
			  void *userdata)
{
	struct pci_dev *dev;
	struct pci_bus *bus;
	struct list_head *next;
	int retval;
	int ret = 0;

	bus = top;
	if (!locked)
		down_read(&pci_bus_sem);
	next = top->devices.next;
	for (;;) {
		if (next == &bus->devices) {
			/* end of this bus, go up or finish */
			if (bus == top)
	list_for_each_entry(dev, &top->devices, bus_list) {
		ret = cb(dev, userdata);
		if (ret)
			break;
			next = bus->self->bus_list.next;
			bus = bus->self->bus;
			continue;
		}
		dev = list_entry(next, struct pci_dev, bus_list);
		if (dev->subordinate) {
			/* this is a pci-pci bridge, do its devices next */
			next = dev->subordinate->devices.next;
			bus = dev->subordinate;
		} else
			next = dev->bus_list.next;

		retval = cb(dev, userdata);
		if (retval)
			ret = __pci_walk_bus(dev->subordinate, cb, userdata);
			if (ret)
				break;
		}
	if (!locked)
		up_read(&pci_bus_sem);
	}
	return ret;
}

/**
@@ -451,7 +433,9 @@ static void __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void
 */
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata)
{
	__pci_walk_bus(top, cb, userdata, false);
	down_read(&pci_bus_sem);
	__pci_walk_bus(top, cb, userdata);
	up_read(&pci_bus_sem);
}
EXPORT_SYMBOL_GPL(pci_walk_bus);

@@ -459,9 +443,8 @@ void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void *
{
	lockdep_assert_held(&pci_bus_sem);

	__pci_walk_bus(top, cb, userdata, true);
	__pci_walk_bus(top, cb, userdata);
}
EXPORT_SYMBOL_GPL(pci_walk_bus_locked);

struct pci_bus *pci_bus_get(struct pci_bus *bus)
{
+18 −2
Original line number Diff line number Diff line
@@ -323,6 +323,9 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
				struct list_head *realloc_head,
				struct list_head *fail_head);
bool pci_bus_clip_resource(struct pci_dev *dev, int idx);
void pci_walk_bus_locked(struct pci_bus *top,
			 int (*cb)(struct pci_dev *, void *),
			 void *userdata);

const char *pci_resource_name(struct pci_dev *dev, unsigned int i);

@@ -493,10 +496,18 @@ static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
#define PCI_DEV_ADDED 0
#define PCI_DPC_RECOVERED 1
#define PCI_DPC_RECOVERING 2
#define PCI_DEV_REMOVED 3

static inline void pci_dev_assign_added(struct pci_dev *dev)
{
	smp_mb__before_atomic();
	set_bit(PCI_DEV_ADDED, &dev->priv_flags);
	smp_mb__after_atomic();
}

static inline void pci_dev_assign_added(struct pci_dev *dev, bool added)
static inline bool pci_dev_test_and_clear_added(struct pci_dev *dev)
{
	assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added);
	return test_and_clear_bit(PCI_DEV_ADDED, &dev->priv_flags);
}

static inline bool pci_dev_is_added(const struct pci_dev *dev)
@@ -504,6 +515,11 @@ static inline bool pci_dev_is_added(const struct pci_dev *dev)
	return test_bit(PCI_DEV_ADDED, &dev->priv_flags);
}

static inline bool pci_dev_test_and_set_removed(struct pci_dev *dev)
{
	return test_and_set_bit(PCI_DEV_REMOVED, &dev->priv_flags);
}

#ifdef CONFIG_PCIEAER
#include <linux/aer.h>

+10 −11
Original line number Diff line number Diff line
@@ -33,21 +33,20 @@ static void pci_stop_dev(struct pci_dev *dev)
{
	pci_pme_active(dev, false);

	if (pci_dev_is_added(dev)) {
	if (!pci_dev_test_and_clear_added(dev))
		return;

	device_for_each_child(dev->dev.parent, dev_of_node(&dev->dev),
			      pci_pwrctl_unregister);
	device_release_driver(&dev->dev);
	pci_proc_detach_device(dev);
	pci_remove_sysfs_dev_files(dev);
	of_pci_remove_node(dev);

		pci_dev_assign_added(dev, false);
	}
}

static void pci_destroy_dev(struct pci_dev *dev)
{
	if (!dev->dev.kobj.parent)
	if (pci_dev_test_and_set_removed(dev))
		return;

	pci_npem_remove(dev);
+0 −2
Original line number Diff line number Diff line
@@ -1612,8 +1612,6 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,

void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
		  void *userdata);
void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
			 void *userdata);
int pci_cfg_space_size(struct pci_dev *dev);
unsigned char pci_bus_max_busnr(struct pci_bus *bus);
void pci_setup_bridge(struct pci_bus *bus);