Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e2d98dc authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/reset' into next

* pci/reset:
  vfio-pci: Use pci "try" reset interface
  PCI: Add pci_try_reset_function(), pci_try_reset_slot(), pci_try_reset_bus()
parents 40304618 890ed578
Loading
Loading
Loading
Loading
+155 −0
Original line number Original line Diff line number Diff line
@@ -3250,6 +3250,18 @@ static void pci_dev_lock(struct pci_dev *dev)
	device_lock(&dev->dev);
	device_lock(&dev->dev);
}
}


/* Return 1 on successful lock, 0 on contention */
static int pci_dev_trylock(struct pci_dev *dev)
{
	if (pci_cfg_access_trylock(dev)) {
		if (device_trylock(&dev->dev))
			return 1;
		pci_cfg_access_unlock(dev);
	}

	return 0;
}

static void pci_dev_unlock(struct pci_dev *dev)
static void pci_dev_unlock(struct pci_dev *dev)
{
{
	device_unlock(&dev->dev);
	device_unlock(&dev->dev);
@@ -3393,6 +3405,34 @@ int pci_reset_function(struct pci_dev *dev)
}
}
EXPORT_SYMBOL_GPL(pci_reset_function);
EXPORT_SYMBOL_GPL(pci_reset_function);


/**
 * pci_try_reset_function - quiesce and reset a PCI device function
 * @dev: PCI device to reset
 *
 * Same as above, except return -EAGAIN if unable to lock device.
 */
int pci_try_reset_function(struct pci_dev *dev)
{
	int rc;

	rc = pci_dev_reset(dev, 1);
	if (rc)
		return rc;

	pci_dev_save_and_disable(dev);

	if (pci_dev_trylock(dev)) {
		rc = __pci_dev_reset(dev, 0);
		pci_dev_unlock(dev);
	} else
		rc = -EAGAIN;

	pci_dev_restore(dev);

	return rc;
}
EXPORT_SYMBOL_GPL(pci_try_reset_function);

/* Lock devices from the top of the tree down */
/* Lock devices from the top of the tree down */
static void pci_bus_lock(struct pci_bus *bus)
static void pci_bus_lock(struct pci_bus *bus)
{
{
@@ -3417,6 +3457,32 @@ static void pci_bus_unlock(struct pci_bus *bus)
	}
	}
}
}


/* Return 1 on successful lock, 0 on contention */
static int pci_bus_trylock(struct pci_bus *bus)
{
	struct pci_dev *dev;

	list_for_each_entry(dev, &bus->devices, bus_list) {
		if (!pci_dev_trylock(dev))
			goto unlock;
		if (dev->subordinate) {
			if (!pci_bus_trylock(dev->subordinate)) {
				pci_dev_unlock(dev);
				goto unlock;
			}
		}
	}
	return 1;

unlock:
	list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) {
		if (dev->subordinate)
			pci_bus_unlock(dev->subordinate);
		pci_dev_unlock(dev);
	}
	return 0;
}

/* Lock devices from the top of the tree down */
/* Lock devices from the top of the tree down */
static void pci_slot_lock(struct pci_slot *slot)
static void pci_slot_lock(struct pci_slot *slot)
{
{
@@ -3445,6 +3511,37 @@ static void pci_slot_unlock(struct pci_slot *slot)
	}
	}
}
}


/* Return 1 on successful lock, 0 on contention */
static int pci_slot_trylock(struct pci_slot *slot)
{
	struct pci_dev *dev;

	list_for_each_entry(dev, &slot->bus->devices, bus_list) {
		if (!dev->slot || dev->slot != slot)
			continue;
		if (!pci_dev_trylock(dev))
			goto unlock;
		if (dev->subordinate) {
			if (!pci_bus_trylock(dev->subordinate)) {
				pci_dev_unlock(dev);
				goto unlock;
			}
		}
	}
	return 1;

unlock:
	list_for_each_entry_continue_reverse(dev,
					     &slot->bus->devices, bus_list) {
		if (!dev->slot || dev->slot != slot)
			continue;
		if (dev->subordinate)
			pci_bus_unlock(dev->subordinate);
		pci_dev_unlock(dev);
	}
	return 0;
}

/* Save and disable devices from the top of the tree down */
/* Save and disable devices from the top of the tree down */
static void pci_bus_save_and_disable(struct pci_bus *bus)
static void pci_bus_save_and_disable(struct pci_bus *bus)
{
{
@@ -3568,6 +3665,35 @@ int pci_reset_slot(struct pci_slot *slot)
}
}
EXPORT_SYMBOL_GPL(pci_reset_slot);
EXPORT_SYMBOL_GPL(pci_reset_slot);


/**
 * pci_try_reset_slot - Try to reset a PCI slot
 * @slot: PCI slot to reset
 *
 * Same as above except return -EAGAIN if the slot cannot be locked
 */
int pci_try_reset_slot(struct pci_slot *slot)
{
	int rc;

	rc = pci_slot_reset(slot, 1);
	if (rc)
		return rc;

	pci_slot_save_and_disable(slot);

	if (pci_slot_trylock(slot)) {
		might_sleep();
		rc = pci_reset_hotplug_slot(slot->hotplug, 0);
		pci_slot_unlock(slot);
	} else
		rc = -EAGAIN;

	pci_slot_restore(slot);

	return rc;
}
EXPORT_SYMBOL_GPL(pci_try_reset_slot);

static int pci_bus_reset(struct pci_bus *bus, int probe)
static int pci_bus_reset(struct pci_bus *bus, int probe)
{
{
	if (!bus->self)
	if (!bus->self)
@@ -3626,6 +3752,35 @@ int pci_reset_bus(struct pci_bus *bus)
}
}
EXPORT_SYMBOL_GPL(pci_reset_bus);
EXPORT_SYMBOL_GPL(pci_reset_bus);


/**
 * pci_try_reset_bus - Try to reset a PCI bus
 * @bus: top level PCI bus to reset
 *
 * Same as above except return -EAGAIN if the bus cannot be locked
 */
int pci_try_reset_bus(struct pci_bus *bus)
{
	int rc;

	rc = pci_bus_reset(bus, 1);
	if (rc)
		return rc;

	pci_bus_save_and_disable(bus);

	if (pci_bus_trylock(bus)) {
		might_sleep();
		pci_reset_bridge_secondary_bus(bus->self);
		pci_bus_unlock(bus);
	} else
		rc = -EAGAIN;

	pci_bus_restore(bus);

	return rc;
}
EXPORT_SYMBOL_GPL(pci_try_reset_bus);

/**
/**
 * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
 * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
 * @dev: PCI device to query
 * @dev: PCI device to query
+9 −20
Original line number Original line Diff line number Diff line
@@ -139,25 +139,14 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
	pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
	pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);


	/*
	/*
	 * Careful, device_lock may already be held.  This is the case if
	 * Try to reset the device.  The success of this is dependent on
	 * a driver unbind is blocked.  Try to get the locks ourselves to
	 * being able to lock the device, which is not always possible.
	 * prevent a deadlock.
	 */
	 */
	if (vdev->reset_works) {
	if (vdev->reset_works) {
		bool reset_done = false;
		int ret = pci_try_reset_function(pdev);

		if (ret)
		if (pci_cfg_access_trylock(pdev)) {
			pr_warn("%s: Failed to reset device %s (%d)\n",
			if (device_trylock(&pdev->dev)) {
				__func__, dev_name(&pdev->dev), ret);
				__pci_reset_function_locked(pdev);
				reset_done = true;
				device_unlock(&pdev->dev);
			}
			pci_cfg_access_unlock(pdev);
		}

		if (!reset_done)
			pr_warn("%s: Unable to acquire locks for reset of %s\n",
				__func__, dev_name(&pdev->dev));
	}
	}


	pci_restore_state(pdev);
	pci_restore_state(pdev);
@@ -514,7 +503,7 @@ static long vfio_pci_ioctl(void *device_data,


	} else if (cmd == VFIO_DEVICE_RESET) {
	} else if (cmd == VFIO_DEVICE_RESET) {
		return vdev->reset_works ?
		return vdev->reset_works ?
			pci_reset_function(vdev->pdev) : -EINVAL;
			pci_try_reset_function(vdev->pdev) : -EINVAL;


	} else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) {
	} else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) {
		struct vfio_pci_hot_reset_info hdr;
		struct vfio_pci_hot_reset_info hdr;
@@ -684,8 +673,8 @@ static long vfio_pci_ioctl(void *device_data,
						    &info, slot);
						    &info, slot);
		if (!ret)
		if (!ret)
			/* User has access, do the reset */
			/* User has access, do the reset */
			ret = slot ? pci_reset_slot(vdev->pdev->slot) :
			ret = slot ? pci_try_reset_slot(vdev->pdev->slot) :
				     pci_reset_bus(vdev->pdev->bus);
				     pci_try_reset_bus(vdev->pdev->bus);


hot_reset_release:
hot_reset_release:
		for (i--; i >= 0; i--)
		for (i--; i >= 0; i--)
+3 −0
Original line number Original line Diff line number Diff line
@@ -949,10 +949,13 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
int __pci_reset_function(struct pci_dev *dev);
int __pci_reset_function(struct pci_dev *dev);
int __pci_reset_function_locked(struct pci_dev *dev);
int __pci_reset_function_locked(struct pci_dev *dev);
int pci_reset_function(struct pci_dev *dev);
int pci_reset_function(struct pci_dev *dev);
int pci_try_reset_function(struct pci_dev *dev);
int pci_probe_reset_slot(struct pci_slot *slot);
int pci_probe_reset_slot(struct pci_slot *slot);
int pci_reset_slot(struct pci_slot *slot);
int pci_reset_slot(struct pci_slot *slot);
int pci_try_reset_slot(struct pci_slot *slot);
int pci_probe_reset_bus(struct pci_bus *bus);
int pci_probe_reset_bus(struct pci_bus *bus);
int pci_reset_bus(struct pci_bus *bus);
int pci_reset_bus(struct pci_bus *bus);
int pci_try_reset_bus(struct pci_bus *bus);
void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
void pci_update_resource(struct pci_dev *dev, int resno);
void pci_update_resource(struct pci_dev *dev, int resno);
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
int __must_check pci_assign_resource(struct pci_dev *dev, int i);