Commit a29a44c1 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'remotes/lorenzo/pci/hv'

  - Fix memory leak in hv probe path (Dexuan Cui)

  - Add support for Hyper-V protocol 1.3 (Long Li)

  - Replace zero-length array with flexible-array member (Gustavo A. R.
    Silva)

  - Move hypercall definitions to <asm/hyperv-tlfs.h> (Boqun Feng)

  - Move retarget definitions to <asm/hyperv-tlfs.h> and make them packed
    (Boqun Feng)

  - Add struct hv_msi_entry and hv_set_msi_entry_from_desc() to prepare for
    future virtual PCI on non-x86 (Boqun Feng)

* remotes/lorenzo/pci/hv:
  PCI: hv: Introduce hv_msi_entry
  PCI: hv: Move retarget related structures into tlfs header
  PCI: hv: Move hypercall related definitions into tlfs header
  PCI: hv: Replace zero-length array with flexible-array member
  PCI: hv: Add support for protocol 1.3 and support PCI_BUS_RELATIONS2
  PCI: hv: Decouple the func definition in hv_dr_state from VSP message
  PCI: hv: Add missing kfree(hbus) in hv_pci_probe()'s error handling path
  PCI: hv: Remove unnecessary type casting from kzalloc
parents b16f2ab2 1cf106d9
......@@ -376,6 +376,7 @@ struct hv_tsc_emulation_status {
#define HVCALL_SEND_IPI_EX 0x0015
#define HVCALL_POST_MESSAGE 0x005c
#define HVCALL_SIGNAL_EVENT 0x005d
#define HVCALL_RETARGET_INTERRUPT 0x007e
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
......@@ -405,6 +406,8 @@ enum HV_GENERIC_SET_FORMAT {
HV_GENERIC_SET_ALL,
};
#define HV_PARTITION_ID_SELF ((u64)-1)
#define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0)
#define HV_HYPERCALL_FAST_BIT BIT(16)
#define HV_HYPERCALL_VARHEAD_OFFSET 17
......@@ -909,4 +912,42 @@ struct hv_tlb_flush_ex {
struct hv_partition_assist_pg {
u32 tlb_lock_count;
};
union hv_msi_entry {
u64 as_uint64;
struct {
u32 address;
u32 data;
} __packed;
};
struct hv_interrupt_entry {
u32 source; /* 1 for MSI(-X) */
u32 reserved1;
union hv_msi_entry msi_entry;
} __packed;
/*
* flags for hv_device_interrupt_target.flags
*/
#define HV_DEVICE_INTERRUPT_TARGET_MULTICAST 1
#define HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET 2
struct hv_device_interrupt_target {
u32 vector;
u32 flags;
union {
u64 vp_mask;
struct hv_vpset vp_set;
};
} __packed;
/* HvRetargetDeviceInterrupt hypercall */
struct hv_retarget_device_interrupt {
u64 partition_id; /* use "self" */
u64 device_id;
struct hv_interrupt_entry int_entry;
u64 reserved2;
struct hv_device_interrupt_target int_target;
} __packed __aligned(8);
#endif
......@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <linux/nmi.h>
#include <linux/msi.h>
#include <asm/io.h>
#include <asm/hyperv-tlfs.h>
#include <asm/nospec-branch.h>
......@@ -240,6 +241,13 @@ bool hv_vcpu_is_preempted(int vcpu);
static inline void hv_apic_init(void) {}
#endif
static inline void hv_set_msi_entry_from_desc(union hv_msi_entry *msi_entry,
struct msi_desc *msi_desc)
{
msi_entry->address = msi_desc->msg.address_lo;
msi_entry->data = msi_desc->msg.data;
}
#else /* CONFIG_HYPERV */
static inline void hyperv_init(void) {}
static inline void hyperv_setup_mmu_ops(void) {}
......
......@@ -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
......@@ -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,
};
......@@ -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
};
......@@ -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
......@@ -260,7 +283,7 @@ struct pci_packet {
int resp_packet_size);
void *compl_ctxt;
struct pci_message message[0];
struct pci_message message[];
};
/*
......@@ -296,7 +319,13 @@ struct pci_bus_d0_entry {
struct pci_bus_relations {
struct pci_incoming_message incoming;
u32 device_count;
struct pci_function_description func[0];
struct pci_function_description func[];
} __packed;
struct pci_bus_relations2 {
struct pci_incoming_message incoming;
u32 device_count;
struct pci_function_description2 func[];
} __packed;
struct pci_q_res_req_response {
......@@ -406,42 +435,6 @@ struct pci_eject_response {
static int pci_ring_size = (4 * PAGE_SIZE);
/*
* Definitions or interrupt steering hypercall.
*/
#define HV_PARTITION_ID_SELF ((u64)-1)
#define HVCALL_RETARGET_INTERRUPT 0x7e
struct hv_interrupt_entry {
u32 source; /* 1 for MSI(-X) */
u32 reserved1;
u32 address;
u32 data;
};
/*
* flags for hv_device_interrupt_target.flags
*/
#define HV_DEVICE_INTERRUPT_TARGET_MULTICAST 1
#define HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET 2
struct hv_device_interrupt_target {
u32 vector;
u32 flags;
union {
u64 vp_mask;
struct hv_vpset vp_set;
};
};
struct retarget_msi_interrupt {
u64 partition_id; /* use "self" */
u64 device_id;
struct hv_interrupt_entry int_entry;
u64 reserved2;
struct hv_device_interrupt_target int_target;
} __packed __aligned(8);
/*
* Driver specific state.
*/
......@@ -488,7 +481,7 @@ struct hv_pcibus_device {
struct workqueue_struct *wq;
/* hypercall arg, must not cross page boundary */
struct retarget_msi_interrupt retarget_msi_interrupt_params;
struct hv_retarget_device_interrupt retarget_msi_interrupt_params;
/*
* Don't put anything here: retarget_msi_interrupt_params must be last
......@@ -505,10 +498,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[];
};
enum hv_pcichild_state {
......@@ -525,7 +532,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;
......@@ -1184,7 +1191,7 @@ 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 hv_retarget_device_interrupt *params;
struct hv_pcibus_device *hbus;
struct cpumask *dest;
cpumask_var_t tmp;
......@@ -1206,8 +1213,7 @@ static void hv_irq_unmask(struct irq_data *data)
memset(params, 0, sizeof(*params));
params->partition_id = HV_PARTITION_ID_SELF;
params->int_entry.source = 1; /* MSI(-X) */
params->int_entry.address = msi_desc->msg.address_lo;
params->int_entry.data = msi_desc->msg.data;
hv_set_msi_entry_from_desc(&params->int_entry.msi_entry, msi_desc);
params->device_id = (hbus->hdev->dev_instance.b[5] << 24) |
(hbus->hdev->dev_instance.b[4] << 16) |
(hbus->hdev->dev_instance.b[7] << 8) |
......@@ -1401,6 +1407,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,
......@@ -1799,6 +1806,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
......@@ -1821,6 +1849,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);
......@@ -1877,7 +1906,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 +2017,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;
......@@ -2089,6 +2118,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;
......@@ -2107,17 +2137,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 +2153,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 +2179,87 @@ 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);
}
/**
* 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);
}
/**
......@@ -2280,6 +2375,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;
......@@ -2347,6 +2443,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;
......@@ -2922,7 +3033,7 @@ static int hv_pci_probe(struct hv_device *hdev,
* positive by using kmemleak_alloc() and kmemleak_free() to ask
* kmemleak to track and scan the hbus buffer.
*/
hbus = (struct hv_pcibus_device *)kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
hbus = kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
if (!hbus)
return -ENOMEM;
hbus->state = hv_pcibus_init;
......@@ -3058,7 +3169,7 @@ static int hv_pci_probe(struct hv_device *hdev,
free_dom:
hv_put_dom_num(hbus->sysdata.domain);
free_bus:
free_page((unsigned long)hbus);
kfree(hbus);
return ret;
}
......@@ -3069,7 +3180,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 +3193,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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment