]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - drivers/pci/host/pci-hyperv.c
PCI: hv: Use device serial number as PCI domain
[thirdparty/kernel/stable.git] / drivers / pci / host / pci-hyperv.c
index 6955ffdb89f33332f89595407b82cd0f8e6af779..ada98569b78ed65561d5fb4479f1eeee62cd00c3 100644 (file)
@@ -130,7 +130,8 @@ union pci_version {
  */
 union win_slot_encoding {
        struct {
-               u32     func:8;
+               u32     dev:5;
+               u32     func:3;
                u32     reserved:24;
        } bits;
        u32 slot;
@@ -200,11 +201,11 @@ struct tran_int_desc {
  */
 
 struct pci_message {
-       u32 message_type;
+       u32 type;
 } __packed;
 
 struct pci_child_message {
-       u32 message_type;
+       struct pci_message message_type;
        union win_slot_encoding wslot;
 } __packed;
 
@@ -222,7 +223,8 @@ struct pci_packet {
        void (*completion_func)(void *context, struct pci_response *resp,
                                int resp_packet_size);
        void *compl_ctxt;
-       struct pci_message message;
+
+       struct pci_message message[0];
 };
 
 /*
@@ -258,7 +260,7 @@ struct pci_bus_d0_entry {
 struct pci_bus_relations {
        struct pci_incoming_message incoming;
        u32 device_count;
-       struct pci_function_description func[1];
+       struct pci_function_description func[0];
 } __packed;
 
 struct pci_q_res_req_response {
@@ -314,7 +316,7 @@ struct pci_dev_incoming {
 } __packed;
 
 struct pci_eject_response {
-       u32 message_type;
+       struct pci_message message_type;
        union win_slot_encoding wslot;
        u32 status;
 } __packed;
@@ -373,11 +375,12 @@ struct hv_pcibus_device {
 
        struct list_head children;
        struct list_head dr_list;
-       struct work_struct wrk;
 
        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;
 };
 
 /*
@@ -393,7 +396,7 @@ struct hv_dr_work {
 struct hv_dr_state {
        struct list_head list_entry;
        u32 device_count;
-       struct pci_function_description func[1];
+       struct pci_function_description func[0];
 };
 
 enum hv_pcichild_state {
@@ -447,15 +450,16 @@ struct hv_pci_compl {
  * for any message for which the completion packet contains a
  * status and nothing else.
  */
-static
-void
-hv_pci_generic_compl(void *context, struct pci_response *resp,
-                    int resp_packet_size)
+static void hv_pci_generic_compl(void *context, struct pci_response *resp,
+                                int resp_packet_size)
 {
        struct hv_pci_compl *comp_pkt = context;
 
        if (resp_packet_size >= offsetofend(struct pci_response, status))
                comp_pkt->completion_status = resp->status;
+       else
+               comp_pkt->completion_status = -1;
+
        complete(&comp_pkt->host_event);
 }
 
@@ -482,7 +486,8 @@ static u32 devfn_to_wslot(int devfn)
        union win_slot_encoding wslot;
 
        wslot.slot = 0;
-       wslot.bits.func = PCI_SLOT(devfn) | (PCI_FUNC(devfn) << 5);
+       wslot.bits.dev = PCI_SLOT(devfn);
+       wslot.bits.func = PCI_FUNC(devfn);
 
        return wslot.slot;
 }
@@ -500,7 +505,7 @@ static int wslot_to_devfn(u32 wslot)
        union win_slot_encoding slot_no;
 
        slot_no.slot = wslot;
-       return PCI_DEVFN(0, slot_no.bits.func);
+       return PCI_DEVFN(slot_no.bits.dev, slot_no.bits.func);
 }
 
 /*
@@ -694,13 +699,12 @@ static void hv_int_desc_free(struct hv_pci_dev *hpdev,
        struct pci_delete_interrupt *int_pkt;
        struct {
                struct pci_packet pkt;
-               u8 buffer[sizeof(struct pci_delete_interrupt) -
-                         sizeof(struct pci_message)];
+               u8 buffer[sizeof(struct pci_delete_interrupt)];
        } ctxt;
 
        memset(&ctxt, 0, sizeof(ctxt));
        int_pkt = (struct pci_delete_interrupt *)&ctxt.pkt.message;
-       int_pkt->message_type.message_type =
+       int_pkt->message_type.type =
                PCI_DELETE_INTERRUPT_MESSAGE;
        int_pkt->wslot.slot = hpdev->desc.win_slot.slot;
        int_pkt->int_desc = *int_desc;
@@ -755,7 +759,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,38 +774,44 @@ 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);
-       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(&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) |
+       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, &params, NULL);
+       hv_do_hypercall(HVCALL_RETARGET_INTERRUPT, params, NULL);
+
+       spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags);
 
        pci_msi_unmask_irq(data);
 }
@@ -847,8 +857,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
        struct cpumask *affinity;
        struct {
                struct pci_packet pkt;
-               u8 buffer[sizeof(struct pci_create_interrupt) -
-                         sizeof(struct pci_message)];
+               u8 buffer[sizeof(struct pci_create_interrupt)];
        } ctxt;
        int cpu;
        int ret;
@@ -876,7 +885,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
        ctxt.pkt.completion_func = hv_pci_compose_compl;
        ctxt.pkt.compl_ctxt = &comp;
        int_pkt = (struct pci_create_interrupt *)&ctxt.pkt.message;
-       int_pkt->message_type.message_type = PCI_CREATE_INTERRUPT_MESSAGE;
+       int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE;
        int_pkt->wslot.slot = hpdev->desc.win_slot.slot;
        int_pkt->int_desc.vector = cfg->vector;
        int_pkt->int_desc.vector_count = 1;
@@ -897,8 +906,10 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
                               sizeof(*int_pkt), (unsigned long)&ctxt.pkt,
                               VM_PKT_DATA_INBAND,
                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
-       if (!ret)
-               wait_for_completion(&comp.comp_pkt.host_event);
+       if (ret)
+               goto free_int_desc;
+
+       wait_for_completion(&comp.comp_pkt.host_event);
 
        if (comp.comp_pkt.completion_status < 0) {
                dev_err(&hbus->hdev->device,
@@ -1270,9 +1281,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;
@@ -1289,7 +1300,7 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
        pkt.init_packet.compl_ctxt = &comp_pkt;
        pkt.init_packet.completion_func = q_resource_requirements;
        res_req = (struct pci_child_message *)&pkt.init_packet.message;
-       res_req->message_type = PCI_QUERY_RESOURCE_REQUIREMENTS;
+       res_req->message_type.type = PCI_QUERY_RESOURCE_REQUIREMENTS;
        res_req->wslot.slot = desc->win_slot.slot;
 
        ret = vmbus_sendpacket(hbus->hdev->channel, res_req,
@@ -1306,6 +1317,18 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
        get_pcichild(hpdev, hv_pcidev_ref_initial);
        get_pcichild(hpdev, hv_pcidev_ref_childlist);
        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;
@@ -1466,8 +1489,7 @@ static void pci_devices_present_work(struct work_struct *work)
                        if (hpdev->reported_missing) {
                                found = true;
                                put_pcichild(hpdev, hv_pcidev_ref_childlist);
-                               list_del(&hpdev->list_entry);
-                               list_add_tail(&hpdev->list_entry, &removed);
+                               list_move_tail(&hpdev->list_entry, &removed);
                                break;
                        }
                }
@@ -1558,8 +1580,7 @@ static void hv_eject_device_work(struct work_struct *work)
        int wslot;
        struct {
                struct pci_packet pkt;
-               u8 buffer[sizeof(struct pci_eject_response) -
-                         sizeof(struct pci_message)];
+               u8 buffer[sizeof(struct pci_eject_response)];
        } ctxt;
 
        hpdev = container_of(work, struct hv_pci_dev, wrk);
@@ -1583,18 +1604,18 @@ 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 = PCI_EJECTION_COMPLETE;
+       ejct_pkt->message_type.type = PCI_EJECTION_COMPLETE;
        ejct_pkt->wslot.slot = hpdev->desc.win_slot.slot;
        vmbus_sendpacket(hpdev->hbus->hdev->channel, ejct_pkt,
                         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);
@@ -1688,7 +1709,7 @@ static void hv_pci_onchannelcallback(void *context)
                case VM_PKT_DATA_INBAND:
 
                        new_message = (struct pci_incoming_message *)buffer;
-                       switch (new_message->message_type.message_type) {
+                       switch (new_message->message_type.type) {
                        case PCI_BUS_RELATIONS:
 
                                bus_rel = (struct pci_bus_relations *)buffer;
@@ -1719,7 +1740,7 @@ static void hv_pci_onchannelcallback(void *context)
                        default:
                                dev_warn(&hbus->hdev->device,
                                        "Unimplemented protocol message %x\n",
-                                       new_message->message_type.message_type);
+                                       new_message->message_type.type);
                                break;
                        }
                        break;
@@ -1772,7 +1793,7 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev)
        pkt->completion_func = hv_pci_generic_compl;
        pkt->compl_ctxt = &comp_pkt;
        version_req = (struct pci_version_request *)&pkt->message;
-       version_req->message_type.message_type = PCI_QUERY_PROTOCOL_VERSION;
+       version_req->message_type.type = PCI_QUERY_PROTOCOL_VERSION;
        version_req->protocol_version = PCI_PROTOCOL_VERSION_CURRENT;
 
        ret = vmbus_sendpacket(hdev->channel, version_req,
@@ -1973,7 +1994,7 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
        pkt->completion_func = hv_pci_generic_compl;
        pkt->compl_ctxt = &comp_pkt;
        d0_entry = (struct pci_bus_d0_entry *)&pkt->message;
-       d0_entry->message_type.message_type = PCI_BUS_D0ENTRY;
+       d0_entry->message_type.type = PCI_BUS_D0ENTRY;
        d0_entry->mmio_base = hbus->mem_config->start;
 
        ret = vmbus_sendpacket(hdev->channel, d0_entry, sizeof(*d0_entry),
@@ -2019,7 +2040,7 @@ static int hv_pci_query_relations(struct hv_device *hdev)
                return -ENOTEMPTY;
 
        memset(&message, 0, sizeof(message));
-       message.message_type = PCI_QUERY_BUS_RELATIONS;
+       message.type = PCI_QUERY_BUS_RELATIONS;
 
        ret = vmbus_sendpacket(hdev->channel, &message, sizeof(message),
                               0, VM_PKT_DATA_INBAND, 0);
@@ -2072,8 +2093,8 @@ static int hv_send_resources_allocated(struct hv_device *hdev)
                init_completion(&comp_pkt.host_event);
                pkt->completion_func = hv_pci_generic_compl;
                pkt->compl_ctxt = &comp_pkt;
-               pkt->message.message_type = PCI_RESOURCES_ASSIGNED;
                res_assigned = (struct pci_resources_assigned *)&pkt->message;
+               res_assigned->message_type.type = PCI_RESOURCES_ASSIGNED;
                res_assigned->wslot.slot = hpdev->desc.win_slot.slot;
 
                put_pcichild(hpdev, hv_pcidev_ref_by_slot);
@@ -2123,7 +2144,7 @@ static int hv_send_resources_released(struct hv_device *hdev)
                        continue;
 
                memset(&pkt, 0, sizeof(pkt));
-               pkt.message_type = PCI_RESOURCES_RELEASED;
+               pkt.message_type.type = PCI_RESOURCES_RELEASED;
                pkt.wslot.slot = hpdev->desc.win_slot.slot;
 
                put_pcichild(hpdev, hv_pcidev_ref_by_slot);
@@ -2187,6 +2208,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);
 
@@ -2267,30 +2289,38 @@ 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);
        pkt.teardown_packet.completion_func = hv_pci_generic_compl;
        pkt.teardown_packet.compl_ctxt = &comp_pkt;
-       pkt.teardown_packet.message.message_type = PCI_BUS_D0EXIT;
+       pkt.teardown_packet.message[0].type = PCI_BUS_D0EXIT;
 
        ret = vmbus_sendpacket(hdev->channel, &pkt.teardown_packet.message,
                               sizeof(struct pci_message),
@@ -2299,7 +2329,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();
@@ -2308,17 +2350,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);