From 542ccf4551fa019a8ae9dfb7c8cd7e73a3d7e614 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 31 Oct 2016 12:04:09 +0100 Subject: [PATCH 1/5] PCI: hv: Make unnecessarily global IRQ masking functions static Make hv_irq_mask() and hv_irq_unmask() static as they are only used in pci-hyperv.c This fixes a sparse warning. Signed-off-by: Tobias Klauser Signed-off-by: Bjorn Helgaas Acked-by: K. Y. Srinivasan --- drivers/pci/host/pci-hyperv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 763ff8745828..06c98695c06c 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -755,7 +755,7 @@ static int hv_set_affinity(struct irq_data *data, const struct cpumask *dest, return parent->chip->irq_set_affinity(parent, dest, force); } -void hv_irq_mask(struct irq_data *data) +static void hv_irq_mask(struct irq_data *data) { pci_msi_mask_irq(data); } @@ -770,7 +770,7 @@ void hv_irq_mask(struct irq_data *data) * is built out of this PCI bus's instance GUID and the function * number of the device. */ -void hv_irq_unmask(struct irq_data *data) +static void hv_irq_unmask(struct irq_data *data) { struct msi_desc *msi_desc = irq_data_get_msi_desc(data); struct irq_cfg *cfg = irqd_cfg(data); From 8286e96d95f2dd01a72e3500322ad95153a469c2 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 10 Nov 2016 07:17:48 +0000 Subject: [PATCH 2/5] PCI: hv: Use the correct buffer size in new_pcichild_device() We don't really need such a big on-stack buffer. vmbus_sendpacket() here only uses sizeof(struct pci_child_message). Signed-off-by: Dexuan Cui Signed-off-by: Bjorn Helgaas Reviewed-by: Jake Oshins --- drivers/pci/host/pci-hyperv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 06c98695c06c..9ea63b4b0e58 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -1271,9 +1271,9 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus, struct hv_pci_dev *hpdev; struct pci_child_message *res_req; struct q_res_req_compl comp_pkt; - union { - struct pci_packet init_packet; - u8 buffer[0x100]; + struct { + struct pci_packet init_packet; + u8 buffer[sizeof(struct pci_child_message)]; } pkt; unsigned long flags; int ret; From 17978524a636d007e6b929304ae3eb5ea0371019 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 10 Nov 2016 07:18:47 +0000 Subject: [PATCH 3/5] PCI: hv: Fix hv_pci_remove() for hot-remove 1. We don't really need such a big on-stack buffer when sending the teardown_packet: vmbus_sendpacket() here only uses sizeof(struct pci_message). 2. In the hot-remove case (PCI_EJECT), after we send PCI_EJECTION_COMPLETE to the host, the host will send a RESCIND_CHANNEL message to us and the host won't access the per-channel ringbuffer any longer, so we needn't send PCI_RESOURCES_RELEASED/PCI_BUS_D0EXIT to the host, and we shouldn't expect the host's completion message of PCI_BUS_D0EXIT, which will never come. 3. We should send PCI_BUS_D0EXIT after hv_send_resources_released(). Signed-off-by: Dexuan Cui Signed-off-by: Bjorn Helgaas Reviewed-by: Jake Oshins Acked-by: K. Y. Srinivasan CC: Haiyang Zhang CC: Vitaly Kuznetsov --- drivers/pci/host/pci-hyperv.c | 53 ++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 9ea63b4b0e58..b1747469a32c 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -2266,24 +2266,32 @@ free_bus: return ret; } -/** - * hv_pci_remove() - Remove routine for this VMBus channel - * @hdev: VMBus's tracking struct for this root PCI bus - * - * Return: 0 on success, -errno on failure - */ -static int hv_pci_remove(struct hv_device *hdev) +static void hv_pci_bus_exit(struct hv_device *hdev) { - int ret; - struct hv_pcibus_device *hbus; - union { + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); + struct { struct pci_packet teardown_packet; - u8 buffer[0x100]; + u8 buffer[sizeof(struct pci_message)]; } pkt; struct pci_bus_relations relations; struct hv_pci_compl comp_pkt; + int ret; - hbus = hv_get_drvdata(hdev); + /* + * After the host sends the RESCIND_CHANNEL message, it doesn't + * access the per-channel ringbuffer any longer. + */ + if (hdev->channel->rescind) + return; + + /* Delete any children which might still exist. */ + memset(&relations, 0, sizeof(relations)); + hv_pci_devices_present(hbus, &relations); + + ret = hv_send_resources_released(hdev); + if (ret) + dev_err(&hdev->device, + "Couldn't send resources released packet(s)\n"); memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet)); init_completion(&comp_pkt.host_event); @@ -2298,7 +2306,19 @@ static int hv_pci_remove(struct hv_device *hdev) VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (!ret) wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ); +} +/** + * hv_pci_remove() - Remove routine for this VMBus channel + * @hdev: VMBus's tracking struct for this root PCI bus + * + * Return: 0 on success, -errno on failure + */ +static int hv_pci_remove(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus; + + hbus = hv_get_drvdata(hdev); if (hbus->state == hv_pcibus_installed) { /* Remove the bus from PCI's point of view. */ pci_lock_rescan_remove(); @@ -2307,17 +2327,10 @@ static int hv_pci_remove(struct hv_device *hdev) pci_unlock_rescan_remove(); } - ret = hv_send_resources_released(hdev); - if (ret) - dev_err(&hdev->device, - "Couldn't send resources released packet(s)\n"); + hv_pci_bus_exit(hdev); vmbus_close(hdev->channel); - /* Delete any children which might still exist. */ - memset(&relations, 0, sizeof(relations)); - hv_pci_devices_present(hbus, &relations); - iounmap(hbus->cfg_addr); hv_free_config_window(hbus); pci_free_resource_list(&hbus->resources_for_children); From e74d2ebdda33b3bdd1826b5b92e9aa45bdf92bb3 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 10 Nov 2016 07:19:52 +0000 Subject: [PATCH 4/5] PCI: hv: Delete the device earlier from hbus->children for hot-remove After we send a PCI_EJECTION_COMPLETE message to the host, the host will immediately send us a PCI_BUS_RELATIONS message with relations->device_count == 0, so pci_devices_present_work(), running on another thread, can find the being-ejected device, mark the hpdev->reported_missing to true, and run list_move_tail()/list_del() for the device -- this races hv_eject_device_work() -> list_del(). Move the list_del() in hv_eject_device_work() to an earlier place, i.e., before we send PCI_EJECTION_COMPLETE, so later the pci_devices_present_work() can't see the device. Signed-off-by: Dexuan Cui Signed-off-by: Bjorn Helgaas Reviewed-by: Jake Oshins Acked-by: K. Y. Srinivasan CC: Haiyang Zhang CC: Vitaly Kuznetsov --- drivers/pci/host/pci-hyperv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index b1747469a32c..a63c3a4f6c7c 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -1582,6 +1582,10 @@ static void hv_eject_device_work(struct work_struct *work) pci_dev_put(pdev); } + spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags); + list_del(&hpdev->list_entry); + spin_unlock_irqrestore(&hpdev->hbus->device_list_lock, flags); + memset(&ctxt, 0, sizeof(ctxt)); ejct_pkt = (struct pci_eject_response *)&ctxt.pkt.message; ejct_pkt->message_type.type = PCI_EJECTION_COMPLETE; @@ -1590,10 +1594,6 @@ static void hv_eject_device_work(struct work_struct *work) sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt, VM_PKT_DATA_INBAND, 0); - spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags); - list_del(&hpdev->list_entry); - spin_unlock_irqrestore(&hpdev->hbus->device_list_lock, flags); - put_pcichild(hpdev, hv_pcidev_ref_childlist); put_pcichild(hpdev, hv_pcidev_ref_pnp); put_hvpcibus(hpdev->hbus); From 0de8ce3ee8e38cc66683438f715c79a2cc69539e Mon Sep 17 00:00:00 2001 From: Long Li Date: Tue, 8 Nov 2016 14:04:38 -0800 Subject: [PATCH 5/5] PCI: hv: Allocate physically contiguous hypercall params buffer hv_do_hypercall() assumes that we pass a segment from a physically contiguous buffer. A buffer allocated on the stack may not work if CONFIG_VMAP_STACK=y is set. Use kmalloc() to allocate this buffer. Reported-by: Haiyang Zhang Signed-off-by: Long Li Signed-off-by: Bjorn Helgaas Acked-by: K. Y. Srinivasan --- drivers/pci/host/pci-hyperv.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index a63c3a4f6c7c..3efcc7bdc5fb 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -378,6 +378,8 @@ struct hv_pcibus_device { struct msi_domain_info msi_info; struct msi_controller msi_chip; struct irq_domain *irq_domain; + struct retarget_msi_interrupt retarget_msi_interrupt_params; + spinlock_t retarget_msi_interrupt_lock; }; /* @@ -774,34 +776,40 @@ static void hv_irq_unmask(struct irq_data *data) { struct msi_desc *msi_desc = irq_data_get_msi_desc(data); struct irq_cfg *cfg = irqd_cfg(data); - struct retarget_msi_interrupt params; + struct retarget_msi_interrupt *params; struct hv_pcibus_device *hbus; struct cpumask *dest; struct pci_bus *pbus; struct pci_dev *pdev; int cpu; + unsigned long flags; dest = irq_data_get_affinity_mask(data); pdev = msi_desc_to_pci_dev(msi_desc); pbus = pdev->bus; hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); - memset(¶ms, 0, sizeof(params)); - params.partition_id = HV_PARTITION_ID_SELF; - params.source = 1; /* MSI(-X) */ - params.address = msi_desc->msg.address_lo; - params.data = msi_desc->msg.data; - params.device_id = (hbus->hdev->dev_instance.b[5] << 24) | + spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags); + + params = &hbus->retarget_msi_interrupt_params; + memset(params, 0, sizeof(*params)); + params->partition_id = HV_PARTITION_ID_SELF; + params->source = 1; /* MSI(-X) */ + params->address = msi_desc->msg.address_lo; + params->data = msi_desc->msg.data; + params->device_id = (hbus->hdev->dev_instance.b[5] << 24) | (hbus->hdev->dev_instance.b[4] << 16) | (hbus->hdev->dev_instance.b[7] << 8) | (hbus->hdev->dev_instance.b[6] & 0xf8) | PCI_FUNC(pdev->devfn); - params.vector = cfg->vector; + params->vector = cfg->vector; for_each_cpu_and(cpu, dest, cpu_online_mask) - params.vp_mask |= (1ULL << vmbus_cpu_number_to_vp_number(cpu)); + params->vp_mask |= (1ULL << vmbus_cpu_number_to_vp_number(cpu)); - hv_do_hypercall(HVCALL_RETARGET_INTERRUPT, ¶ms, NULL); + hv_do_hypercall(HVCALL_RETARGET_INTERRUPT, params, NULL); + + spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags); pci_msi_unmask_irq(data); } @@ -2186,6 +2194,7 @@ static int hv_pci_probe(struct hv_device *hdev, INIT_LIST_HEAD(&hbus->resources_for_children); spin_lock_init(&hbus->config_lock); spin_lock_init(&hbus->device_list_lock); + spin_lock_init(&hbus->retarget_msi_interrupt_lock); sema_init(&hbus->enum_sem, 1); init_completion(&hbus->remove_event);