OSDN Git Service

PCI: hv: Decouple the func definition in hv_dr_state from VSP message
authorLong Li <longli@microsoft.com>
Wed, 26 Feb 2020 05:06:07 +0000 (21:06 -0800)
committerLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Fri, 6 Mar 2020 10:18:06 +0000 (10:18 +0000)
hv_dr_state is used to find present PCI devices on the bus. The structure
reuses struct pci_function_description from VSP message to describe a
device.

To prepare support for pci_function_description v2, decouple this
dependence in hv_dr_state so it can work with both v1 and v2 VSP messages.

There is no functionality change.

Signed-off-by: Long Li <longli@microsoft.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
drivers/pci/controller/pci-hyperv.c

index 15011a3..dea197f 100644 (file)
@@ -505,10 +505,24 @@ struct hv_dr_work {
        struct hv_pcibus_device *bus;
 };
 
+struct hv_pcidev_description {
+       u16     v_id;   /* vendor ID */
+       u16     d_id;   /* device ID */
+       u8      rev;
+       u8      prog_intf;
+       u8      subclass;
+       u8      base_class;
+       u32     subsystem_id;
+       union   win_slot_encoding win_slot;
+       u32     ser;    /* serial number */
+       u32     flags;
+       u16     virtual_numa_node;
+};
+
 struct hv_dr_state {
        struct list_head list_entry;
        u32 device_count;
-       struct pci_function_description func[0];
+       struct hv_pcidev_description func[0];
 };
 
 enum hv_pcichild_state {
@@ -525,7 +539,7 @@ struct hv_pci_dev {
        refcount_t refs;
        enum hv_pcichild_state state;
        struct pci_slot *pci_slot;
-       struct pci_function_description desc;
+       struct hv_pcidev_description desc;
        bool reported_missing;
        struct hv_pcibus_device *hbus;
        struct work_struct wrk;
@@ -1877,7 +1891,7 @@ static void q_resource_requirements(void *context, struct pci_response *resp,
  * Return: Pointer to the new tracking struct
  */
 static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
-               struct pci_function_description *desc)
+               struct hv_pcidev_description *desc)
 {
        struct hv_pci_dev *hpdev;
        struct pci_child_message *res_req;
@@ -1988,7 +2002,7 @@ static void pci_devices_present_work(struct work_struct *work)
 {
        u32 child_no;
        bool found;
-       struct pci_function_description *new_desc;
+       struct hv_pcidev_description *new_desc;
        struct hv_pci_dev *hpdev;
        struct hv_pcibus_device *hbus;
        struct list_head removed;
@@ -2107,17 +2121,15 @@ static void pci_devices_present_work(struct work_struct *work)
 }
 
 /**
- * hv_pci_devices_present() - Handles list of new children
+ * hv_pci_start_relations_work() - Queue work to start device discovery
  * @hbus:      Root PCI bus, as understood by this driver
- * @relations: Packet from host listing children
+ * @dr:                The list of children returned from host
  *
- * This function is invoked whenever a new list of devices for
- * this bus appears.
+ * Return:  0 on success, -errno on failure
  */
-static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
-                                  struct pci_bus_relations *relations)
+static int hv_pci_start_relations_work(struct hv_pcibus_device *hbus,
+                                      struct hv_dr_state *dr)
 {
-       struct hv_dr_state *dr;
        struct hv_dr_work *dr_wrk;
        unsigned long flags;
        bool pending_dr;
@@ -2125,29 +2137,15 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
        if (hbus->state == hv_pcibus_removing) {
                dev_info(&hbus->hdev->device,
                         "PCI VMBus BUS_RELATIONS: ignored\n");
-               return;
+               return -ENOENT;
        }
 
        dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT);
        if (!dr_wrk)
-               return;
-
-       dr = kzalloc(offsetof(struct hv_dr_state, func) +
-                    (sizeof(struct pci_function_description) *
-                     (relations->device_count)), GFP_NOWAIT);
-       if (!dr)  {
-               kfree(dr_wrk);
-               return;
-       }
+               return -ENOMEM;
 
        INIT_WORK(&dr_wrk->wrk, pci_devices_present_work);
        dr_wrk->bus = hbus;
-       dr->device_count = relations->device_count;
-       if (dr->device_count != 0) {
-               memcpy(dr->func, relations->func,
-                      sizeof(struct pci_function_description) *
-                      dr->device_count);
-       }
 
        spin_lock_irqsave(&hbus->device_list_lock, flags);
        /*
@@ -2165,6 +2163,47 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
                get_hvpcibus(hbus);
                queue_work(hbus->wq, &dr_wrk->wrk);
        }
+
+       return 0;
+}
+
+/**
+ * hv_pci_devices_present() - Handle list of new children
+ * @hbus:      Root PCI bus, as understood by this driver
+ * @relations: Packet from host listing children
+ *
+ * Process a new list of devices on the bus. The list of devices is
+ * discovered by VSP and sent to us via VSP message PCI_BUS_RELATIONS,
+ * whenever a new list of devices for this bus appears.
+ */
+static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
+                                  struct pci_bus_relations *relations)
+{
+       struct hv_dr_state *dr;
+       int i;
+
+       dr = kzalloc(offsetof(struct hv_dr_state, func) +
+                    (sizeof(struct hv_pcidev_description) *
+                     (relations->device_count)), GFP_NOWAIT);
+
+       if (!dr)
+               return;
+
+       dr->device_count = relations->device_count;
+       for (i = 0; i < dr->device_count; i++) {
+               dr->func[i].v_id = relations->func[i].v_id;
+               dr->func[i].d_id = relations->func[i].d_id;
+               dr->func[i].rev = relations->func[i].rev;
+               dr->func[i].prog_intf = relations->func[i].prog_intf;
+               dr->func[i].subclass = relations->func[i].subclass;
+               dr->func[i].base_class = relations->func[i].base_class;
+               dr->func[i].subsystem_id = relations->func[i].subsystem_id;
+               dr->func[i].win_slot = relations->func[i].win_slot;
+               dr->func[i].ser = relations->func[i].ser;
+       }
+
+       if (hv_pci_start_relations_work(hbus, dr))
+               kfree(dr);
 }
 
 /**
@@ -3069,7 +3108,7 @@ static int hv_pci_bus_exit(struct hv_device *hdev, bool hibernating)
                struct pci_packet teardown_packet;
                u8 buffer[sizeof(struct pci_message)];
        } pkt;
-       struct pci_bus_relations relations;
+       struct hv_dr_state *dr;
        struct hv_pci_compl comp_pkt;
        int ret;
 
@@ -3082,8 +3121,9 @@ static int hv_pci_bus_exit(struct hv_device *hdev, bool hibernating)
 
        if (!hibernating) {
                /* Delete any children which might still exist. */
-               memset(&relations, 0, sizeof(relations));
-               hv_pci_devices_present(hbus, &relations);
+               dr = kzalloc(sizeof(*dr), GFP_KERNEL);
+               if (dr && hv_pci_start_relations_work(hbus, dr))
+                       kfree(dr);
        }
 
        ret = hv_send_resources_released(hdev);