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

Commit 741f8e7e authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'lorenzo/pci/hv'

  - fix Hyper-V bus registration failure caused by domain/serial number
    confusion (Sridhar Pitchai)

  - improve Hyper-V refcounting and coding style (Stephen Hemminger)

  - avoid potential Hyper-V hang waiting for a response that will never
    come (Dexuan Cui)

* lorenzo/pci/hv:
  PCI: hv: Do not wait forever on a device that has disappeared
  PCI: hv: Use list_for_each_entry()
  PCI: hv: Convert remove_lock to refcount
  PCI: hv: Remove unused reason for refcount handler
  PCI: hv: Make sure the bus domain is really unique
parents 5db844ee c3635da2
Loading
Loading
Loading
Loading
+71 −91
Original line number Diff line number Diff line
@@ -433,7 +433,7 @@ enum hv_pcibus_state {
struct hv_pcibus_device {
	struct pci_sysdata sysdata;
	enum hv_pcibus_state state;
	atomic_t remove_lock;
	refcount_t remove_lock;
	struct hv_device *hdev;
	resource_size_t low_mmio_space;
	resource_size_t high_mmio_space;
@@ -488,17 +488,6 @@ enum hv_pcichild_state {
	hv_pcichild_maximum
};

enum hv_pcidev_ref_reason {
	hv_pcidev_ref_invalid = 0,
	hv_pcidev_ref_initial,
	hv_pcidev_ref_by_slot,
	hv_pcidev_ref_packet,
	hv_pcidev_ref_pnp,
	hv_pcidev_ref_childlist,
	hv_pcidev_irqdata,
	hv_pcidev_ref_max
};

struct hv_pci_dev {
	/* List protected by pci_rescan_remove_lock */
	struct list_head list_entry;
@@ -548,14 +537,41 @@ static void hv_pci_generic_compl(void *context, struct pci_response *resp,

static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus,
						u32 wslot);
static void get_pcichild(struct hv_pci_dev *hv_pcidev,
			 enum hv_pcidev_ref_reason reason);
static void put_pcichild(struct hv_pci_dev *hv_pcidev,
			 enum hv_pcidev_ref_reason reason);

static void get_pcichild(struct hv_pci_dev *hpdev)
{
	refcount_inc(&hpdev->refs);
}

static void put_pcichild(struct hv_pci_dev *hpdev)
{
	if (refcount_dec_and_test(&hpdev->refs))
		kfree(hpdev);
}

static void get_hvpcibus(struct hv_pcibus_device *hv_pcibus);
static void put_hvpcibus(struct hv_pcibus_device *hv_pcibus);

/*
 * There is no good way to get notified from vmbus_onoffer_rescind(),
 * so let's use polling here, since this is not a hot path.
 */
static int wait_for_response(struct hv_device *hdev,
			     struct completion *comp)
{
	while (true) {
		if (hdev->channel->rescind) {
			dev_warn_once(&hdev->device, "The device is gone.\n");
			return -ENODEV;
		}

		if (wait_for_completion_timeout(comp, HZ / 10))
			break;
	}

	return 0;
}

/**
 * devfn_to_wslot() - Convert from Linux PCI slot to Windows
 * @devfn:	The Linux representation of PCI slot
@@ -762,7 +778,7 @@ static int hv_pcifront_read_config(struct pci_bus *bus, unsigned int devfn,

	_hv_pcifront_read_config(hpdev, where, size, val);

	put_pcichild(hpdev, hv_pcidev_ref_by_slot);
	put_pcichild(hpdev);
	return PCIBIOS_SUCCESSFUL;
}

@@ -790,7 +806,7 @@ static int hv_pcifront_write_config(struct pci_bus *bus, unsigned int devfn,

	_hv_pcifront_write_config(hpdev, where, size, val);

	put_pcichild(hpdev, hv_pcidev_ref_by_slot);
	put_pcichild(hpdev);
	return PCIBIOS_SUCCESSFUL;
}

@@ -856,7 +872,7 @@ static void hv_msi_free(struct irq_domain *domain, struct msi_domain_info *info,
	}

	hv_int_desc_free(hpdev, int_desc);
	put_pcichild(hpdev, hv_pcidev_ref_by_slot);
	put_pcichild(hpdev);
}

static int hv_set_affinity(struct irq_data *data, const struct cpumask *dest,
@@ -1186,13 +1202,13 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
	msg->address_lo = comp.int_desc.address & 0xffffffff;
	msg->data = comp.int_desc.data;

	put_pcichild(hpdev, hv_pcidev_ref_by_slot);
	put_pcichild(hpdev);
	return;

free_int_desc:
	kfree(int_desc);
drop_reference:
	put_pcichild(hpdev, hv_pcidev_ref_by_slot);
	put_pcichild(hpdev);
return_null_message:
	msg->address_hi = 0;
	msg->address_lo = 0;
@@ -1283,7 +1299,6 @@ static u64 get_bar_size(u64 bar_val)
 */
static void survey_child_resources(struct hv_pcibus_device *hbus)
{
	struct list_head *iter;
	struct hv_pci_dev *hpdev;
	resource_size_t bar_size = 0;
	unsigned long flags;
@@ -1309,8 +1324,7 @@ static void survey_child_resources(struct hv_pcibus_device *hbus)
	 * for a child device are a power of 2 in size and aligned in memory,
	 * so it's sufficient to just add them up without tracking alignment.
	 */
	list_for_each(iter, &hbus->children) {
		hpdev = container_of(iter, struct hv_pci_dev, list_entry);
	list_for_each_entry(hpdev, &hbus->children, list_entry) {
		for (i = 0; i < 6; i++) {
			if (hpdev->probed_bar[i] & PCI_BASE_ADDRESS_SPACE_IO)
				dev_err(&hbus->hdev->device,
@@ -1363,7 +1377,6 @@ static void prepopulate_bars(struct hv_pcibus_device *hbus)
	resource_size_t low_base = 0;
	resource_size_t bar_size;
	struct hv_pci_dev *hpdev;
	struct list_head *iter;
	unsigned long flags;
	u64 bar_val;
	u32 command;
@@ -1385,9 +1398,7 @@ static void prepopulate_bars(struct hv_pcibus_device *hbus)

	/* Pick addresses for the BARs. */
	do {
		list_for_each(iter, &hbus->children) {
			hpdev = container_of(iter, struct hv_pci_dev,
					     list_entry);
		list_for_each_entry(hpdev, &hbus->children, list_entry) {
			for (i = 0; i < 6; i++) {
				bar_val = hpdev->probed_bar[i];
				if (bar_val == 0)
@@ -1508,19 +1519,6 @@ static void q_resource_requirements(void *context, struct pci_response *resp,
	complete(&completion->host_event);
}

static void get_pcichild(struct hv_pci_dev *hpdev,
			    enum hv_pcidev_ref_reason reason)
{
	refcount_inc(&hpdev->refs);
}

static void put_pcichild(struct hv_pci_dev *hpdev,
			    enum hv_pcidev_ref_reason reason)
{
	if (refcount_dec_and_test(&hpdev->refs))
		kfree(hpdev);
}

/**
 * new_pcichild_device() - Create a new child device
 * @hbus:	The internal struct tracking this root PCI bus.
@@ -1568,24 +1566,14 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
	if (ret)
		goto error;

	wait_for_completion(&comp_pkt.host_event);
	if (wait_for_response(hbus->hdev, &comp_pkt.host_event))
		goto error;

	hpdev->desc = *desc;
	refcount_set(&hpdev->refs, 1);
	get_pcichild(hpdev, hv_pcidev_ref_childlist);
	get_pcichild(hpdev);
	spin_lock_irqsave(&hbus->device_list_lock, flags);

	/*
	 * When a device is being added to the bus, we set the PCI domain
	 * number to be the device serial number, which is non-zero and
	 * unique on the same VM.  The serial numbers start with 1, and
	 * increase by 1 for each device.  So device names including this
	 * can have shorter names than based on the bus instance UUID.
	 * Only the first device serial number is used for domain, so the
	 * domain number will not change after the first device is added.
	 */
	if (list_empty(&hbus->children))
		hbus->sysdata.domain = desc->ser;
	list_add_tail(&hpdev->list_entry, &hbus->children);
	spin_unlock_irqrestore(&hbus->device_list_lock, flags);
	return hpdev;
@@ -1618,7 +1606,7 @@ static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus,
	list_for_each_entry(iter, &hbus->children, list_entry) {
		if (iter->desc.win_slot.slot == wslot) {
			hpdev = iter;
			get_pcichild(hpdev, hv_pcidev_ref_by_slot);
			get_pcichild(hpdev);
			break;
		}
	}
@@ -1654,7 +1642,6 @@ static void pci_devices_present_work(struct work_struct *work)
{
	u32 child_no;
	bool found;
	struct list_head *iter;
	struct pci_function_description *new_desc;
	struct hv_pci_dev *hpdev;
	struct hv_pcibus_device *hbus;
@@ -1691,9 +1678,7 @@ static void pci_devices_present_work(struct work_struct *work)

	/* First, mark all existing children as reported missing. */
	spin_lock_irqsave(&hbus->device_list_lock, flags);
	list_for_each(iter, &hbus->children) {
			hpdev = container_of(iter, struct hv_pci_dev,
					     list_entry);
	list_for_each_entry(hpdev, &hbus->children, list_entry) {
		hpdev->reported_missing = true;
	}
	spin_unlock_irqrestore(&hbus->device_list_lock, flags);
@@ -1704,11 +1689,8 @@ static void pci_devices_present_work(struct work_struct *work)
		new_desc = &dr->func[child_no];

		spin_lock_irqsave(&hbus->device_list_lock, flags);
		list_for_each(iter, &hbus->children) {
			hpdev = container_of(iter, struct hv_pci_dev,
					     list_entry);
			if ((hpdev->desc.win_slot.slot ==
			     new_desc->win_slot.slot) &&
		list_for_each_entry(hpdev, &hbus->children, list_entry) {
			if ((hpdev->desc.win_slot.slot == new_desc->win_slot.slot) &&
			    (hpdev->desc.v_id == new_desc->v_id) &&
			    (hpdev->desc.d_id == new_desc->d_id) &&
			    (hpdev->desc.ser == new_desc->ser)) {
@@ -1730,12 +1712,10 @@ static void pci_devices_present_work(struct work_struct *work)
	spin_lock_irqsave(&hbus->device_list_lock, flags);
	do {
		found = false;
		list_for_each(iter, &hbus->children) {
			hpdev = container_of(iter, struct hv_pci_dev,
					     list_entry);
		list_for_each_entry(hpdev, &hbus->children, list_entry) {
			if (hpdev->reported_missing) {
				found = true;
				put_pcichild(hpdev, hv_pcidev_ref_childlist);
				put_pcichild(hpdev);
				list_move_tail(&hpdev->list_entry, &removed);
				break;
			}
@@ -1748,7 +1728,7 @@ static void pci_devices_present_work(struct work_struct *work)
		hpdev = list_first_entry(&removed, struct hv_pci_dev,
					 list_entry);
		list_del(&hpdev->list_entry);
		put_pcichild(hpdev, hv_pcidev_ref_initial);
		put_pcichild(hpdev);
	}

	switch (hbus->state) {
@@ -1883,8 +1863,8 @@ static void hv_eject_device_work(struct work_struct *work)
			 sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt,
			 VM_PKT_DATA_INBAND, 0);

	put_pcichild(hpdev, hv_pcidev_ref_childlist);
	put_pcichild(hpdev, hv_pcidev_ref_pnp);
	put_pcichild(hpdev);
	put_pcichild(hpdev);
	put_hvpcibus(hpdev->hbus);
}

@@ -1899,7 +1879,7 @@ static void hv_eject_device_work(struct work_struct *work)
static void hv_pci_eject_device(struct hv_pci_dev *hpdev)
{
	hpdev->state = hv_pcichild_ejecting;
	get_pcichild(hpdev, hv_pcidev_ref_pnp);
	get_pcichild(hpdev);
	INIT_WORK(&hpdev->wrk, hv_eject_device_work);
	get_hvpcibus(hpdev->hbus);
	queue_work(hpdev->hbus->wq, &hpdev->wrk);
@@ -1999,8 +1979,7 @@ static void hv_pci_onchannelcallback(void *context)
						      dev_message->wslot.slot);
				if (hpdev) {
					hv_pci_eject_device(hpdev);
					put_pcichild(hpdev,
							hv_pcidev_ref_by_slot);
					put_pcichild(hpdev);
				}
				break;

@@ -2069,15 +2048,16 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev)
				sizeof(struct pci_version_request),
				(unsigned long)pkt, VM_PKT_DATA_INBAND,
				VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
		if (!ret)
			ret = wait_for_response(hdev, &comp_pkt.host_event);

		if (ret) {
			dev_err(&hdev->device,
				"PCI Pass-through VSP failed sending version reqquest: %#x",
				"PCI Pass-through VSP failed to request version: %d",
				ret);
			goto exit;
		}

		wait_for_completion(&comp_pkt.host_event);

		if (comp_pkt.completion_status >= 0) {
			pci_protocol_version = pci_protocol_versions[i];
			dev_info(&hdev->device,
@@ -2286,11 +2266,12 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
	ret = vmbus_sendpacket(hdev->channel, d0_entry, sizeof(*d0_entry),
			       (unsigned long)pkt, VM_PKT_DATA_INBAND,
			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
	if (!ret)
		ret = wait_for_response(hdev, &comp_pkt.host_event);

	if (ret)
		goto exit;

	wait_for_completion(&comp_pkt.host_event);

	if (comp_pkt.completion_status < 0) {
		dev_err(&hdev->device,
			"PCI Pass-through VSP failed D0 Entry with status %x\n",
@@ -2330,11 +2311,10 @@ static int hv_pci_query_relations(struct hv_device *hdev)

	ret = vmbus_sendpacket(hdev->channel, &message, sizeof(message),
			       0, VM_PKT_DATA_INBAND, 0);
	if (ret)
		return ret;
	if (!ret)
		ret = wait_for_response(hdev, &comp);

	wait_for_completion(&comp);
	return 0;
	return ret;
}

/**
@@ -2398,17 +2378,17 @@ static int hv_send_resources_allocated(struct hv_device *hdev)
				PCI_RESOURCES_ASSIGNED2;
			res_assigned2->wslot.slot = hpdev->desc.win_slot.slot;
		}
		put_pcichild(hpdev, hv_pcidev_ref_by_slot);
		put_pcichild(hpdev);

		ret = vmbus_sendpacket(hdev->channel, &pkt->message,
				size_res, (unsigned long)pkt,
				VM_PKT_DATA_INBAND,
				VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
		if (!ret)
			ret = wait_for_response(hdev, &comp_pkt.host_event);
		if (ret)
			break;

		wait_for_completion(&comp_pkt.host_event);

		if (comp_pkt.completion_status < 0) {
			ret = -EPROTO;
			dev_err(&hdev->device,
@@ -2446,7 +2426,7 @@ static int hv_send_resources_released(struct hv_device *hdev)
		pkt.message_type.type = PCI_RESOURCES_RELEASED;
		pkt.wslot.slot = hpdev->desc.win_slot.slot;

		put_pcichild(hpdev, hv_pcidev_ref_by_slot);
		put_pcichild(hpdev);

		ret = vmbus_sendpacket(hdev->channel, &pkt, sizeof(pkt), 0,
				       VM_PKT_DATA_INBAND, 0);
@@ -2459,12 +2439,12 @@ static int hv_send_resources_released(struct hv_device *hdev)

static void get_hvpcibus(struct hv_pcibus_device *hbus)
{
	atomic_inc(&hbus->remove_lock);
	refcount_inc(&hbus->remove_lock);
}

static void put_hvpcibus(struct hv_pcibus_device *hbus)
{
	if (atomic_dec_and_test(&hbus->remove_lock))
	if (refcount_dec_and_test(&hbus->remove_lock))
		complete(&hbus->remove_event);
}

@@ -2508,7 +2488,7 @@ static int hv_pci_probe(struct hv_device *hdev,
			       hdev->dev_instance.b[8] << 8;

	hbus->hdev = hdev;
	atomic_inc(&hbus->remove_lock);
	refcount_set(&hbus->remove_lock, 1);
	INIT_LIST_HEAD(&hbus->children);
	INIT_LIST_HEAD(&hbus->dr_list);
	INIT_LIST_HEAD(&hbus->resources_for_children);