Commit a29a44c1 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

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
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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) {}
+186 −74
Original line number Diff line number Diff line
@@ -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 @@ destroy_wq:
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);