Skip to content

Commit

Permalink
PCI: hv: Add support for protocol 1.3 and support PCI_BUS_RELATIONS2
Browse files Browse the repository at this point in the history
Starting with Hyper-V PCI protocol version 1.3, the host VSP can send
PCI_BUS_RELATIONS2 and pass the vNUMA node information for devices on the
bus. The vNUMA node tells which guest NUMA node this device is on based
on guest VM configuration topology and physical device information.

Add code to negotiate v1.3 and process PCI_BUS_RELATIONS2.

Signed-off-by: Long Li <longli@microsoft.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
  • Loading branch information
Long Li authored and Lorenzo Pieralisi committed Mar 6, 2020
1 parent f9ad0f3 commit 999dd95
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions drivers/pci/controller/pci-hyperv.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
enum pci_protocol_version_t {
PCI_PROTOCOL_VERSION_1_1 = PCI_MAKE_VERSION(1, 1), /* Win10 */
PCI_PROTOCOL_VERSION_1_2 = PCI_MAKE_VERSION(1, 2), /* RS1 */
PCI_PROTOCOL_VERSION_1_3 = PCI_MAKE_VERSION(1, 3), /* Vibranium */
};

#define CPU_AFFINITY_ALL -1ULL
Expand All @@ -72,6 +73,7 @@ enum pci_protocol_version_t {
* first.
*/
static enum pci_protocol_version_t pci_protocol_versions[] = {
PCI_PROTOCOL_VERSION_1_3,
PCI_PROTOCOL_VERSION_1_2,
PCI_PROTOCOL_VERSION_1_1,
};
Expand Down Expand Up @@ -119,6 +121,7 @@ enum pci_message_type {
PCI_RESOURCES_ASSIGNED2 = PCI_MESSAGE_BASE + 0x16,
PCI_CREATE_INTERRUPT_MESSAGE2 = PCI_MESSAGE_BASE + 0x17,
PCI_DELETE_INTERRUPT_MESSAGE2 = PCI_MESSAGE_BASE + 0x18, /* unused */
PCI_BUS_RELATIONS2 = PCI_MESSAGE_BASE + 0x19,
PCI_MESSAGE_MAXIMUM
};

Expand Down Expand Up @@ -164,6 +167,26 @@ struct pci_function_description {
u32 ser; /* serial number */
} __packed;

enum pci_device_description_flags {
HV_PCI_DEVICE_FLAG_NONE = 0x0,
HV_PCI_DEVICE_FLAG_NUMA_AFFINITY = 0x1,
};

struct pci_function_description2 {
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;
u16 reserved;
} __packed;

/**
* struct hv_msi_desc
* @vector: IDT entry
Expand Down Expand Up @@ -299,6 +322,12 @@ struct pci_bus_relations {
struct pci_function_description func[0];
} __packed;

struct pci_bus_relations2 {
struct pci_incoming_message incoming;
u32 device_count;
struct pci_function_description2 func[0];
} __packed;

struct pci_q_res_req_response {
struct vmpacket_descriptor hdr;
s32 status; /* negative values are failures */
Expand Down Expand Up @@ -1415,6 +1444,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
break;

case PCI_PROTOCOL_VERSION_1_2:
case PCI_PROTOCOL_VERSION_1_3:
size = hv_compose_msi_req_v2(&ctxt.int_pkts.v2,
dest,
hpdev->desc.win_slot.slot,
Expand Down Expand Up @@ -1813,6 +1843,27 @@ static void hv_pci_remove_slots(struct hv_pcibus_device *hbus)
}
}

/*
* Set NUMA node for the devices on the bus
*/
static void hv_pci_assign_numa_node(struct hv_pcibus_device *hbus)
{
struct pci_dev *dev;
struct pci_bus *bus = hbus->pci_bus;
struct hv_pci_dev *hv_dev;

list_for_each_entry(dev, &bus->devices, bus_list) {
hv_dev = get_pcichild_wslot(hbus, devfn_to_wslot(dev->devfn));
if (!hv_dev)
continue;

if (hv_dev->desc.flags & HV_PCI_DEVICE_FLAG_NUMA_AFFINITY)
set_dev_node(&dev->dev, hv_dev->desc.virtual_numa_node);

put_pcichild(hv_dev);
}
}

/**
* create_root_hv_pci_bus() - Expose a new root PCI bus
* @hbus: Root PCI bus, as understood by this driver
Expand All @@ -1835,6 +1886,7 @@ static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus)

pci_lock_rescan_remove();
pci_scan_child_bus(hbus->pci_bus);
hv_pci_assign_numa_node(hbus);
pci_bus_assign_resources(hbus->pci_bus);
hv_pci_assign_slots(hbus);
pci_bus_add_devices(hbus->pci_bus);
Expand Down Expand Up @@ -2103,6 +2155,7 @@ static void pci_devices_present_work(struct work_struct *work)
*/
pci_lock_rescan_remove();
pci_scan_child_bus(hbus->pci_bus);
hv_pci_assign_numa_node(hbus);
hv_pci_assign_slots(hbus);
pci_unlock_rescan_remove();
break;
Expand Down Expand Up @@ -2206,6 +2259,46 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
kfree(dr);
}

/**
* hv_pci_devices_present2() - Handle list of new children
* @hbus: Root PCI bus, as understood by this driver
* @relations: Packet from host listing children
*
* This function is the v2 version of hv_pci_devices_present()
*/
static void hv_pci_devices_present2(struct hv_pcibus_device *hbus,
struct pci_bus_relations2 *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;
dr->func[i].flags = relations->func[i].flags;
dr->func[i].virtual_numa_node =
relations->func[i].virtual_numa_node;
}

if (hv_pci_start_relations_work(hbus, dr))
kfree(dr);
}

/**
* hv_eject_device_work() - Asynchronously handles ejection
* @work: Work struct embedded in internal device struct
Expand Down Expand Up @@ -2319,6 +2412,7 @@ static void hv_pci_onchannelcallback(void *context)
struct pci_response *response;
struct pci_incoming_message *new_message;
struct pci_bus_relations *bus_rel;
struct pci_bus_relations2 *bus_rel2;
struct pci_dev_inval_block *inval;
struct pci_dev_incoming *dev_message;
struct hv_pci_dev *hpdev;
Expand Down Expand Up @@ -2386,6 +2480,21 @@ static void hv_pci_onchannelcallback(void *context)
hv_pci_devices_present(hbus, bus_rel);
break;

case PCI_BUS_RELATIONS2:

bus_rel2 = (struct pci_bus_relations2 *)buffer;
if (bytes_recvd <
offsetof(struct pci_bus_relations2, func) +
(sizeof(struct pci_function_description2) *
(bus_rel2->device_count))) {
dev_err(&hbus->hdev->device,
"bus relations v2 too small\n");
break;
}

hv_pci_devices_present2(hbus, bus_rel2);
break;

case PCI_EJECT:

dev_message = (struct pci_dev_incoming *)buffer;
Expand Down

0 comments on commit 999dd95

Please sign in to comment.