svn commit: r308795 - in head/sys: conf dev/hyperv/pcib modules/hyperv/pcib

Dexuan Cui dexuan at FreeBSD.org
Fri Nov 18 06:44:19 UTC 2016


Author: dexuan
Date: Fri Nov 18 06:44:18 2016
New Revision: 308795
URL: https://svnweb.freebsd.org/changeset/base/308795

Log:
  hyperv/pcib: change the file name: pcib.c -> vmbus_pcib.c
  
  This makes the file name and the variable naming in the file consistent.
  
  Reviewed by:	sephe
  Approved by:	sephe (mentor)
  MFC after:	1 week
  Sponsored by:	Microsoft

Added:
  head/sys/dev/hyperv/pcib/vmbus_pcib.c
     - copied unchanged from r308794, head/sys/dev/hyperv/pcib/pcib.c
Deleted:
  head/sys/dev/hyperv/pcib/pcib.c
Modified:
  head/sys/conf/files.amd64
  head/sys/conf/files.i386
  head/sys/modules/hyperv/pcib/Makefile

Modified: head/sys/conf/files.amd64
==============================================================================
--- head/sys/conf/files.amd64	Fri Nov 18 06:24:22 2016	(r308794)
+++ head/sys/conf/files.amd64	Fri Nov 18 06:44:18 2016	(r308795)
@@ -292,7 +292,7 @@ dev/hwpmc/hwpmc_uncore.c	optional	hwpmc
 dev/hwpmc/hwpmc_piv.c		optional	hwpmc
 dev/hwpmc/hwpmc_tsc.c		optional	hwpmc
 dev/hwpmc/hwpmc_x86.c		optional	hwpmc
-dev/hyperv/pcib/pcib.c					optional	hyperv pci
+dev/hyperv/pcib/vmbus_pcib.c				optional	hyperv pci
 dev/hyperv/netvsc/hn_nvs.c				optional	hyperv
 dev/hyperv/netvsc/hn_rndis.c				optional	hyperv
 dev/hyperv/netvsc/if_hn.c				optional	hyperv

Modified: head/sys/conf/files.i386
==============================================================================
--- head/sys/conf/files.i386	Fri Nov 18 06:24:22 2016	(r308794)
+++ head/sys/conf/files.i386	Fri Nov 18 06:44:18 2016	(r308795)
@@ -249,7 +249,7 @@ dev/hwpmc/hwpmc_piv.c		optional hwpmc
 dev/hwpmc/hwpmc_ppro.c		optional hwpmc
 dev/hwpmc/hwpmc_tsc.c		optional hwpmc
 dev/hwpmc/hwpmc_x86.c		optional hwpmc
-dev/hyperv/pcib/pcib.c					optional	hyperv pci
+dev/hyperv/pcib/vmbus_pcib.c				optional	hyperv pci
 dev/hyperv/netvsc/hn_nvs.c				optional	hyperv
 dev/hyperv/netvsc/hn_rndis.c				optional	hyperv
 dev/hyperv/netvsc/if_hn.c				optional	hyperv

Copied: head/sys/dev/hyperv/pcib/vmbus_pcib.c (from r308794, head/sys/dev/hyperv/pcib/pcib.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/hyperv/pcib/vmbus_pcib.c	Fri Nov 18 06:44:18 2016	(r308795, copy of r308794, head/sys/dev/hyperv/pcib/pcib.c)
@@ -0,0 +1,1791 @@
+/*-
+ * Copyright (c) 2016 Microsoft Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/smp.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/mutex.h>
+#include <sys/errno.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <machine/atomic.h>
+#include <machine/bus.h>
+#include <machine/frame.h>
+#include <machine/pci_cfgreg.h>
+#include <machine/resource.h>
+
+#include <sys/pciio.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pci_private.h>
+#include <dev/pci/pcib_private.h>
+#include "pcib_if.h"
+
+#include <machine/intr_machdep.h>
+#include <x86/apicreg.h>
+
+#include <dev/hyperv/include/hyperv.h>
+#include <dev/hyperv/include/hyperv_busdma.h>
+#include <dev/hyperv/include/vmbus_xact.h>
+#include <dev/hyperv/vmbus/vmbus_reg.h>
+#include <dev/hyperv/vmbus/vmbus_chanvar.h>
+
+#include "vmbus_if.h"
+
+#if __FreeBSD_version < 1100000
+typedef u_long rman_res_t;
+#define RM_MAX_END	(~(rman_res_t)0)
+#endif
+
+struct completion {
+	unsigned int done;
+	struct mtx lock;
+};
+
+static void
+init_completion(struct completion *c)
+{
+	memset(c, 0, sizeof(*c));
+	mtx_init(&c->lock, "hvcmpl", NULL, MTX_DEF);
+	c->done = 0;
+}
+
+static void
+free_completion(struct completion *c)
+{
+	mtx_destroy(&c->lock);
+}
+
+static void
+complete(struct completion *c)
+{
+	mtx_lock(&c->lock);
+	c->done++;
+	mtx_unlock(&c->lock);
+	wakeup(c);
+}
+
+static void
+wait_for_completion(struct completion *c)
+{
+	mtx_lock(&c->lock);
+	while (c->done == 0)
+		mtx_sleep(c, &c->lock, 0, "hvwfc", 0);
+	c->done--;
+	mtx_unlock(&c->lock);
+}
+
+#define PCI_MAKE_VERSION(major, minor) ((uint32_t)(((major) << 16) | (major)))
+
+enum {
+	PCI_PROTOCOL_VERSION_1_1 = PCI_MAKE_VERSION(1, 1),
+	PCI_PROTOCOL_VERSION_CURRENT = PCI_PROTOCOL_VERSION_1_1
+};
+
+#define PCI_CONFIG_MMIO_LENGTH	0x2000
+#define CFG_PAGE_OFFSET 0x1000
+#define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET)
+
+/*
+ * Message Types
+ */
+
+enum pci_message_type {
+	/*
+	 * Version 1.1
+	 */
+	PCI_MESSAGE_BASE                = 0x42490000,
+	PCI_BUS_RELATIONS               = PCI_MESSAGE_BASE + 0,
+	PCI_QUERY_BUS_RELATIONS         = PCI_MESSAGE_BASE + 1,
+	PCI_POWER_STATE_CHANGE          = PCI_MESSAGE_BASE + 4,
+	PCI_QUERY_RESOURCE_REQUIREMENTS = PCI_MESSAGE_BASE + 5,
+	PCI_QUERY_RESOURCE_RESOURCES    = PCI_MESSAGE_BASE + 6,
+	PCI_BUS_D0ENTRY                 = PCI_MESSAGE_BASE + 7,
+	PCI_BUS_D0EXIT                  = PCI_MESSAGE_BASE + 8,
+	PCI_READ_BLOCK                  = PCI_MESSAGE_BASE + 9,
+	PCI_WRITE_BLOCK                 = PCI_MESSAGE_BASE + 0xA,
+	PCI_EJECT                       = PCI_MESSAGE_BASE + 0xB,
+	PCI_QUERY_STOP                  = PCI_MESSAGE_BASE + 0xC,
+	PCI_REENABLE                    = PCI_MESSAGE_BASE + 0xD,
+	PCI_QUERY_STOP_FAILED           = PCI_MESSAGE_BASE + 0xE,
+	PCI_EJECTION_COMPLETE           = PCI_MESSAGE_BASE + 0xF,
+	PCI_RESOURCES_ASSIGNED          = PCI_MESSAGE_BASE + 0x10,
+	PCI_RESOURCES_RELEASED          = PCI_MESSAGE_BASE + 0x11,
+	PCI_INVALIDATE_BLOCK            = PCI_MESSAGE_BASE + 0x12,
+	PCI_QUERY_PROTOCOL_VERSION      = PCI_MESSAGE_BASE + 0x13,
+	PCI_CREATE_INTERRUPT_MESSAGE    = PCI_MESSAGE_BASE + 0x14,
+	PCI_DELETE_INTERRUPT_MESSAGE    = PCI_MESSAGE_BASE + 0x15,
+	PCI_MESSAGE_MAXIMUM
+};
+
+/*
+ * Structures defining the virtual PCI Express protocol.
+ */
+
+union pci_version {
+	struct {
+		uint16_t minor_version;
+		uint16_t major_version;
+	} parts;
+	uint32_t version;
+} __packed;
+
+/*
+ * This representation is the one used in Windows, which is
+ * what is expected when sending this back and forth with
+ * the Hyper-V parent partition.
+ */
+union win_slot_encoding {
+	struct {
+		uint32_t	slot:5;
+		uint32_t	func:3;
+		uint32_t	reserved:24;
+	} bits;
+	uint32_t val;
+} __packed;
+
+struct pci_func_desc {
+	uint16_t	v_id;	/* vendor ID */
+	uint16_t	d_id;	/* device ID */
+	uint8_t		rev;
+	uint8_t		prog_intf;
+	uint8_t		subclass;
+	uint8_t		base_class;
+	uint32_t	subsystem_id;
+	union win_slot_encoding wslot;
+	uint32_t	ser;	/* serial number */
+} __packed;
+
+struct hv_msi_desc {
+	uint8_t		vector;
+	uint8_t		delivery_mode;
+	uint16_t	vector_count;
+	uint32_t	reserved;
+	uint64_t	cpu_mask;
+} __packed;
+
+struct tran_int_desc {
+	uint16_t	reserved;
+	uint16_t	vector_count;
+	uint32_t	data;
+	uint64_t	address;
+} __packed;
+
+struct pci_message {
+	uint32_t type;
+} __packed;
+
+struct pci_child_message {
+	struct pci_message message_type;
+	union win_slot_encoding wslot;
+} __packed;
+
+struct pci_incoming_message {
+	struct vmbus_chanpkt_hdr hdr;
+	struct pci_message message_type;
+} __packed;
+
+struct pci_response {
+	struct vmbus_chanpkt_hdr hdr;
+	int32_t status;	/* negative values are failures */
+} __packed;
+
+struct pci_packet {
+	void (*completion_func)(void *context, struct pci_response *resp,
+	    int resp_packet_size);
+	void *compl_ctxt;
+
+	struct pci_message message[0];
+};
+
+/*
+ * Specific message types supporting the PCI protocol.
+ */
+
+struct pci_version_request {
+	struct pci_message message_type;
+	uint32_t protocol_version;
+	uint32_t is_last_attempt:1;
+	uint32_t reservedz:31;
+} __packed;
+
+struct pci_bus_d0_entry {
+	struct pci_message message_type;
+	uint32_t reserved;
+	uint64_t mmio_base;
+} __packed;
+
+struct pci_bus_relations {
+	struct pci_incoming_message incoming;
+	uint32_t device_count;
+	struct pci_func_desc func[0];
+} __packed;
+
+#define MAX_NUM_BARS	(PCIR_MAX_BAR_0 + 1)
+struct pci_q_res_req_response {
+	struct vmbus_chanpkt_hdr hdr;
+	int32_t status; /* negative values are failures */
+	uint32_t probed_bar[MAX_NUM_BARS];
+} __packed;
+
+struct pci_resources_assigned {
+	struct pci_message message_type;
+	union win_slot_encoding wslot;
+	uint8_t memory_range[0x14][MAX_NUM_BARS]; /* unused here */
+	uint32_t msi_descriptors;
+	uint32_t reserved[4];
+} __packed;
+
+struct pci_create_interrupt {
+	struct pci_message message_type;
+	union win_slot_encoding wslot;
+	struct hv_msi_desc int_desc;
+} __packed;
+
+struct pci_create_int_response {
+	struct pci_response response;
+	uint32_t reserved;
+	struct tran_int_desc int_desc;
+} __packed;
+
+struct pci_delete_interrupt {
+	struct pci_message message_type;
+	union win_slot_encoding wslot;
+	struct tran_int_desc int_desc;
+} __packed;
+
+struct pci_dev_incoming {
+	struct pci_incoming_message incoming;
+	union win_slot_encoding wslot;
+} __packed;
+
+struct pci_eject_response {
+	struct pci_message message_type;
+	union win_slot_encoding wslot;
+	uint32_t status;
+} __packed;
+
+/*
+ * Driver specific state.
+ */
+
+enum hv_pcibus_state {
+	hv_pcibus_init = 0,
+	hv_pcibus_installed,
+};
+
+struct hv_pcibus {
+	device_t pcib;
+	device_t pci_bus;
+	struct vmbus_pcib_softc *sc;
+
+	uint16_t pci_domain;
+
+	enum hv_pcibus_state state;
+
+	struct resource *cfg_res;
+
+	struct completion query_completion, *query_comp;
+
+	struct mtx config_lock; /* Avoid two threads writing index page */
+	struct mtx device_list_lock;    /* Protect lists below */
+	TAILQ_HEAD(, hv_pci_dev) children;
+	TAILQ_HEAD(, hv_dr_state) dr_list;
+
+	volatile int detaching;
+};
+
+struct hv_pci_dev {
+	TAILQ_ENTRY(hv_pci_dev) link;
+
+	struct pci_func_desc desc;
+
+	bool reported_missing;
+
+	struct hv_pcibus *hbus;
+	struct task eject_task;
+
+	TAILQ_HEAD(, hv_irq_desc) irq_desc_list;
+
+	/*
+	 * What would be observed if one wrote 0xFFFFFFFF to a BAR and then
+	 * read it back, for each of the BAR offsets within config space.
+	 */
+	uint32_t probed_bar[MAX_NUM_BARS];
+};
+
+/*
+ * Tracks "Device Relations" messages from the host, which must be both
+ * processed in order.
+ */
+struct hv_dr_work {
+	struct task task;
+	struct hv_pcibus *bus;
+};
+
+struct hv_dr_state {
+	TAILQ_ENTRY(hv_dr_state) link;
+	uint32_t device_count;
+	struct pci_func_desc func[0];
+};
+
+struct hv_irq_desc {
+	TAILQ_ENTRY(hv_irq_desc) link;
+	struct tran_int_desc desc;
+	int irq;
+};
+
+#define PCI_DEVFN(slot, func)   ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+#define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn)         ((devfn) & 0x07)
+
+static uint32_t
+devfn_to_wslot(unsigned int devfn)
+{
+	union win_slot_encoding wslot;
+
+	wslot.val = 0;
+	wslot.bits.slot = PCI_SLOT(devfn);
+	wslot.bits.func = PCI_FUNC(devfn);
+
+	return (wslot.val);
+}
+
+static unsigned int
+wslot_to_devfn(uint32_t wslot)
+{
+	union win_slot_encoding encoding;
+	unsigned int slot;
+	unsigned int func;
+
+	encoding.val = wslot;
+
+	slot = encoding.bits.slot;
+	func = encoding.bits.func;
+
+	return (PCI_DEVFN(slot, func));
+}
+
+struct vmbus_pcib_softc {
+	struct vmbus_channel	*chan;
+	void *rx_buf;
+
+	struct taskqueue	*taskq;
+
+	struct hv_pcibus	*hbus;
+};
+
+/* {44C4F61D-4444-4400-9D52-802E27EDE19F} */
+static const struct hyperv_guid g_pass_through_dev_type = {
+	.hv_guid = {0x1D, 0xF6, 0xC4, 0x44, 0x44, 0x44, 0x00, 0x44,
+	    0x9D, 0x52, 0x80, 0x2E, 0x27, 0xED, 0xE1, 0x9F}
+};
+
+struct hv_pci_compl {
+	struct completion host_event;
+	int32_t completion_status;
+};
+
+struct q_res_req_compl {
+	struct completion host_event;
+	struct hv_pci_dev *hpdev;
+};
+
+struct compose_comp_ctxt {
+	struct hv_pci_compl comp_pkt;
+	struct tran_int_desc int_desc;
+};
+
+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 >= sizeof(struct pci_response))
+		comp_pkt->completion_status = resp->status;
+	else
+		comp_pkt->completion_status = -1;
+
+	complete(&comp_pkt->host_event);
+}
+
+static void
+q_resource_requirements(void *context, struct pci_response *resp,
+    int resp_packet_size)
+{
+	struct q_res_req_compl *completion = context;
+	struct pci_q_res_req_response *q_res_req =
+	    (struct pci_q_res_req_response *)resp;
+	int i;
+
+	if (resp->status < 0) {
+		printf("vmbus_pcib: failed to query resource requirements\n");
+	} else {
+		for (i = 0; i < MAX_NUM_BARS; i++)
+			completion->hpdev->probed_bar[i] =
+			    q_res_req->probed_bar[i];
+	}
+
+	complete(&completion->host_event);
+}
+
+static void
+hv_pci_compose_compl(void *context, struct pci_response *resp,
+    int resp_packet_size)
+{
+	struct compose_comp_ctxt *comp_pkt = context;
+	struct pci_create_int_response *int_resp =
+	    (struct pci_create_int_response *)resp;
+
+	comp_pkt->comp_pkt.completion_status = resp->status;
+	comp_pkt->int_desc = int_resp->int_desc;
+	complete(&comp_pkt->comp_pkt.host_event);
+}
+
+static void
+hv_int_desc_free(struct hv_pci_dev *hpdev, struct hv_irq_desc *hid)
+{
+	struct pci_delete_interrupt *int_pkt;
+	struct {
+		struct pci_packet pkt;
+		uint8_t 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.type = PCI_DELETE_INTERRUPT_MESSAGE;
+	int_pkt->wslot.val = hpdev->desc.wslot.val;
+	int_pkt->int_desc = hid->desc;
+
+	vmbus_chan_send(hpdev->hbus->sc->chan, VMBUS_CHANPKT_TYPE_INBAND, 0,
+	    int_pkt, sizeof(*int_pkt), 0);
+
+	free(hid, M_DEVBUF);
+}
+
+static void
+hv_pci_delete_device(struct hv_pci_dev *hpdev)
+{
+	struct hv_pcibus *hbus = hpdev->hbus;
+	struct hv_irq_desc *hid, *tmp_hid;
+	device_t pci_dev;
+	int devfn;
+
+	devfn = wslot_to_devfn(hpdev->desc.wslot.val);
+
+	mtx_lock(&Giant);
+
+	pci_dev = pci_find_dbsf(hbus->pci_domain,
+	    0, PCI_SLOT(devfn), PCI_FUNC(devfn));
+	if (pci_dev)
+		device_delete_child(hbus->pci_bus, pci_dev);
+
+	mtx_unlock(&Giant);
+
+	mtx_lock(&hbus->device_list_lock);
+	TAILQ_REMOVE(&hbus->children, hpdev, link);
+	mtx_unlock(&hbus->device_list_lock);
+
+	TAILQ_FOREACH_SAFE(hid, &hpdev->irq_desc_list, link, tmp_hid)
+		hv_int_desc_free(hpdev, hid);
+
+	free(hpdev, M_DEVBUF);
+}
+
+static struct hv_pci_dev *
+new_pcichild_device(struct hv_pcibus *hbus, struct pci_func_desc *desc)
+{
+	struct hv_pci_dev *hpdev;
+	struct pci_child_message *res_req;
+	struct q_res_req_compl comp_pkt;
+	struct {
+		struct pci_packet pkt;
+		uint8_t buffer[sizeof(struct pci_child_message)];
+	} ctxt;
+	int ret;
+
+	hpdev = malloc(sizeof(*hpdev), M_DEVBUF, M_WAITOK | M_ZERO);
+	hpdev->hbus = hbus;
+
+	TAILQ_INIT(&hpdev->irq_desc_list);
+
+	init_completion(&comp_pkt.host_event);
+	comp_pkt.hpdev = hpdev;
+
+	ctxt.pkt.compl_ctxt = &comp_pkt;
+	ctxt.pkt.completion_func = q_resource_requirements;
+
+	res_req = (struct pci_child_message *)&ctxt.pkt.message;
+	res_req->message_type.type = PCI_QUERY_RESOURCE_REQUIREMENTS;
+	res_req->wslot.val = desc->wslot.val;
+
+	ret = vmbus_chan_send(hbus->sc->chan,
+	    VMBUS_CHANPKT_TYPE_INBAND, VMBUS_CHANPKT_FLAG_RC,
+	    res_req, sizeof(*res_req), (uint64_t)&ctxt.pkt);
+	if (ret)
+		goto err;
+
+	wait_for_completion(&comp_pkt.host_event);
+	free_completion(&comp_pkt.host_event);
+
+	hpdev->desc = *desc;
+
+	mtx_lock(&hbus->device_list_lock);
+	TAILQ_INSERT_TAIL(&hbus->children, hpdev, link);
+	mtx_unlock(&hbus->device_list_lock);
+	return (hpdev);
+err:
+	free_completion(&comp_pkt.host_event);
+	free(hpdev, M_DEVBUF);
+	return (NULL);
+}
+
+#if __FreeBSD_version < 1100000
+
+/* Old versions don't have BUS_RESCAN(). Let's copy it from FreeBSD 11. */
+
+static struct pci_devinfo *
+pci_identify_function(device_t pcib, device_t dev, int domain, int busno,
+    int slot, int func, size_t dinfo_size)
+{
+	struct pci_devinfo *dinfo;
+
+	dinfo = pci_read_device(pcib, domain, busno, slot, func, dinfo_size);
+	if (dinfo != NULL)
+		pci_add_child(dev, dinfo);
+
+	return (dinfo);
+}
+
+static int
+pci_rescan(device_t dev)
+{
+#define	REG(n, w)	PCIB_READ_CONFIG(pcib, busno, s, f, n, w)
+	device_t pcib = device_get_parent(dev);
+	struct pci_softc *sc;
+	device_t child, *devlist, *unchanged;
+	int devcount, error, i, j, maxslots, oldcount;
+	int busno, domain, s, f, pcifunchigh;
+	uint8_t hdrtype;
+
+	/* No need to check for ARI on a rescan. */
+	error = device_get_children(dev, &devlist, &devcount);
+	if (error)
+		return (error);
+	if (devcount != 0) {
+		unchanged = malloc(devcount * sizeof(device_t), M_TEMP,
+		    M_NOWAIT | M_ZERO);
+		if (unchanged == NULL) {
+			free(devlist, M_TEMP);
+			return (ENOMEM);
+		}
+	} else
+		unchanged = NULL;
+
+	sc = device_get_softc(dev);
+	domain = pcib_get_domain(dev);
+	busno = pcib_get_bus(dev);
+	maxslots = PCIB_MAXSLOTS(pcib);
+	for (s = 0; s <= maxslots; s++) {
+		/* If function 0 is not present, skip to the next slot. */
+		f = 0;
+		if (REG(PCIR_VENDOR, 2) == 0xffff)
+			continue;
+		pcifunchigh = 0;
+		hdrtype = REG(PCIR_HDRTYPE, 1);
+		if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+			continue;
+		if (hdrtype & PCIM_MFDEV)
+			pcifunchigh = PCIB_MAXFUNCS(pcib);
+		for (f = 0; f <= pcifunchigh; f++) {
+			if (REG(PCIR_VENDOR, 2) == 0xffff)
+				continue;
+
+			/*
+			 * Found a valid function.  Check if a
+			 * device_t for this device already exists.
+			 */
+			for (i = 0; i < devcount; i++) {
+				child = devlist[i];
+				if (child == NULL)
+					continue;
+				if (pci_get_slot(child) == s &&
+				    pci_get_function(child) == f) {
+					unchanged[i] = child;
+					goto next_func;
+				}
+			}
+
+			pci_identify_function(pcib, dev, domain, busno, s, f,
+			    sizeof(struct pci_devinfo));
+		next_func:;
+		}
+	}
+
+	/* Remove devices that are no longer present. */
+	for (i = 0; i < devcount; i++) {
+		if (unchanged[i] != NULL)
+			continue;
+		device_delete_child(dev, devlist[i]);
+	}
+
+	free(devlist, M_TEMP);
+	oldcount = devcount;
+
+	/* Try to attach the devices just added. */
+	error = device_get_children(dev, &devlist, &devcount);
+	if (error) {
+		free(unchanged, M_TEMP);
+		return (error);
+	}
+
+	for (i = 0; i < devcount; i++) {
+		for (j = 0; j < oldcount; j++) {
+			if (devlist[i] == unchanged[j])
+				goto next_device;
+		}
+
+		device_probe_and_attach(devlist[i]);
+	next_device:;
+	}
+
+	free(unchanged, M_TEMP);
+	free(devlist, M_TEMP);
+	return (0);
+#undef REG
+}
+
+#else
+
+static int
+pci_rescan(device_t dev)
+{
+	return (BUS_RESCAN(dev));
+}
+
+#endif
+
+static void
+pci_devices_present_work(void *arg, int pending __unused)
+{
+	struct hv_dr_work *dr_wrk = arg;
+	struct hv_dr_state *dr = NULL;
+	struct hv_pcibus *hbus;
+	uint32_t child_no;
+	bool found;
+	struct pci_func_desc *new_desc;
+	struct hv_pci_dev *hpdev, *tmp_hpdev;
+	struct completion *query_comp;
+	bool need_rescan = false;
+
+	hbus = dr_wrk->bus;
+	free(dr_wrk, M_DEVBUF);
+
+	/* Pull this off the queue and process it if it was the last one. */
+	mtx_lock(&hbus->device_list_lock);
+	while (!TAILQ_EMPTY(&hbus->dr_list)) {
+		dr = TAILQ_FIRST(&hbus->dr_list);
+		TAILQ_REMOVE(&hbus->dr_list, dr, link);
+
+		/* Throw this away if the list still has stuff in it. */
+		if (!TAILQ_EMPTY(&hbus->dr_list)) {
+			free(dr, M_DEVBUF);
+			continue;
+		}
+	}
+	mtx_unlock(&hbus->device_list_lock);
+
+	if (!dr)
+		return;
+
+	/* First, mark all existing children as reported missing. */
+	mtx_lock(&hbus->device_list_lock);
+	TAILQ_FOREACH(hpdev, &hbus->children, link)
+		hpdev->reported_missing = true;
+	mtx_unlock(&hbus->device_list_lock);
+
+	/* Next, add back any reported devices. */
+	for (child_no = 0; child_no < dr->device_count; child_no++) {
+		found = false;
+		new_desc = &dr->func[child_no];
+
+		mtx_lock(&hbus->device_list_lock);
+		TAILQ_FOREACH(hpdev, &hbus->children, link) {
+			if ((hpdev->desc.wslot.val ==
+			    new_desc->wslot.val) &&
+			    (hpdev->desc.v_id == new_desc->v_id) &&
+			    (hpdev->desc.d_id == new_desc->d_id) &&
+			    (hpdev->desc.ser == new_desc->ser)) {
+				hpdev->reported_missing = false;
+				found = true;
+				break;
+			}
+		}
+		mtx_unlock(&hbus->device_list_lock);
+
+		if (!found) {
+			if (!need_rescan)
+				need_rescan = true;
+
+			hpdev = new_pcichild_device(hbus, new_desc);
+			if (!hpdev)
+				printf("vmbus_pcib: failed to add a child\n");
+		}
+	}
+
+	/* Remove missing device(s), if any */
+	TAILQ_FOREACH_SAFE(hpdev, &hbus->children, link, tmp_hpdev) {
+		if (hpdev->reported_missing)
+			hv_pci_delete_device(hpdev);
+	}
+
+	/* Rescan the bus to find any new device, if necessary. */
+	if (hbus->state == hv_pcibus_installed && need_rescan)
+		pci_rescan(hbus->pci_bus);
+
+	/* Wake up hv_pci_query_relations(), if it's waiting. */
+	query_comp = hbus->query_comp;
+	if (query_comp) {
+		hbus->query_comp = NULL;
+		complete(query_comp);
+	}
+
+	free(dr, M_DEVBUF);
+}
+
+static struct hv_pci_dev *
+get_pcichild_wslot(struct hv_pcibus *hbus, uint32_t wslot)
+{
+	struct hv_pci_dev *hpdev, *ret = NULL;
+
+	mtx_lock(&hbus->device_list_lock);
+	TAILQ_FOREACH(hpdev, &hbus->children, link) {
+		if (hpdev->desc.wslot.val == wslot) {
+			ret = hpdev;
+			break;
+		}
+	}
+	mtx_unlock(&hbus->device_list_lock);
+
+	return (ret);
+}
+
+static void
+hv_pci_devices_present(struct hv_pcibus *hbus,
+    struct pci_bus_relations *relations)
+{
+	struct hv_dr_state *dr;
+	struct hv_dr_work *dr_wrk;
+	unsigned long dr_size;
+
+	if (hbus->detaching && relations->device_count > 0)
+		return;
+
+	dr_size = offsetof(struct hv_dr_state, func) +
+	    (sizeof(struct pci_func_desc) * relations->device_count);
+	dr = malloc(dr_size, M_DEVBUF, M_WAITOK | M_ZERO);
+
+	dr->device_count = relations->device_count;
+	if (dr->device_count != 0)
+		memcpy(dr->func, relations->func,
+		    sizeof(struct pci_func_desc) * dr->device_count);
+
+	mtx_lock(&hbus->device_list_lock);
+	TAILQ_INSERT_TAIL(&hbus->dr_list, dr, link);
+	mtx_unlock(&hbus->device_list_lock);
+
+	dr_wrk = malloc(sizeof(*dr_wrk), M_DEVBUF, M_WAITOK | M_ZERO);
+	dr_wrk->bus = hbus;
+	TASK_INIT(&dr_wrk->task, 0, pci_devices_present_work, dr_wrk);
+	taskqueue_enqueue(hbus->sc->taskq, &dr_wrk->task);
+}
+
+static void
+hv_eject_device_work(void *arg, int pending __unused)
+{
+	struct hv_pci_dev *hpdev = arg;
+	union win_slot_encoding wslot = hpdev->desc.wslot;
+	struct hv_pcibus *hbus = hpdev->hbus;
+	struct pci_eject_response *eject_pkt;
+	struct {
+		struct pci_packet pkt;
+		uint8_t buffer[sizeof(struct pci_eject_response)];
+	} ctxt;
+
+	hv_pci_delete_device(hpdev);
+
+	memset(&ctxt, 0, sizeof(ctxt));
+	eject_pkt = (struct pci_eject_response *)&ctxt.pkt.message;
+	eject_pkt->message_type.type = PCI_EJECTION_COMPLETE;
+	eject_pkt->wslot.val = wslot.val;
+	vmbus_chan_send(hbus->sc->chan, VMBUS_CHANPKT_TYPE_INBAND, 0,
+	    eject_pkt, sizeof(*eject_pkt), 0);
+}
+
+static void
+hv_pci_eject_device(struct hv_pci_dev *hpdev)
+{
+	struct hv_pcibus *hbus = hpdev->hbus;
+	struct taskqueue *taskq;
+
+	if (hbus->detaching)
+		return;
+
+	/*
+	 * Push this task into the same taskqueue on which
+	 * vmbus_pcib_attach() runs, so we're sure this task can't run
+	 * concurrently with vmbus_pcib_attach().
+	 */
+	TASK_INIT(&hpdev->eject_task, 0, hv_eject_device_work, hpdev);
+	taskq = vmbus_chan_mgmt_tq(hbus->sc->chan);
+	taskqueue_enqueue(taskq, &hpdev->eject_task);
+}
+
+#define PCIB_PACKET_SIZE	0x100
+
+static void
+vmbus_pcib_on_channel_callback(struct vmbus_channel *chan, void *arg)
+{
+	struct vmbus_pcib_softc *sc = arg;
+	struct hv_pcibus *hbus = sc->hbus;
+
+	void *buffer;
+	int bufferlen = PCIB_PACKET_SIZE;
+
+	struct pci_packet *comp_packet;
+	struct pci_response *response;
+	struct pci_incoming_message *new_msg;
+	struct pci_bus_relations *bus_rel;
+	struct pci_dev_incoming *dev_msg;
+	struct hv_pci_dev *hpdev;
+
+	buffer = sc->rx_buf;
+	do {
+		struct vmbus_chanpkt_hdr *pkt = buffer;
+		uint32_t bytes_rxed;
+		int ret;
+
+		bytes_rxed = bufferlen;
+		ret = vmbus_chan_recv_pkt(chan, pkt, &bytes_rxed);
+
+		if (ret == ENOBUFS) {
+			/* Handle large packet */
+			if (bufferlen > PCIB_PACKET_SIZE) {
+				free(buffer, M_DEVBUF);
+				buffer = NULL;
+			}
+
+			/* alloc new buffer */
+			buffer = malloc(bytes_rxed, M_DEVBUF, M_WAITOK | M_ZERO);
+			bufferlen = bytes_rxed;
+
+			continue;
+		}
+
+		if (ret != 0) {
+			/* ignore EIO or EAGAIN */
+			break;
+		}
+
+		if (bytes_rxed <= sizeof(struct pci_response))
+			continue;
+
+		switch (pkt->cph_type) {
+		case VMBUS_CHANPKT_TYPE_COMP:
+			comp_packet = (struct pci_packet *)pkt->cph_xactid;
+			response = (struct pci_response *)pkt;
+			comp_packet->completion_func(comp_packet->compl_ctxt,
+			    response, bytes_rxed);
+			break;
+		case VMBUS_CHANPKT_TYPE_INBAND:
+			new_msg = (struct pci_incoming_message *)buffer;
+
+			switch (new_msg->message_type.type) {
+			case PCI_BUS_RELATIONS:
+				bus_rel = (struct pci_bus_relations *)buffer;
+
+				if (bus_rel->device_count == 0)
+					break;
+
+				if (bytes_rxed <
+				    offsetof(struct pci_bus_relations, func) +
+				        (sizeof(struct pci_func_desc) *
+				            (bus_rel->device_count)))
+					break;
+
+				hv_pci_devices_present(hbus, bus_rel);
+				break;
+
+			case PCI_EJECT:
+				dev_msg = (struct pci_dev_incoming *)buffer;
+				hpdev = get_pcichild_wslot(hbus,
+				    dev_msg->wslot.val);
+
+				if (hpdev)
+					hv_pci_eject_device(hpdev);
+
+				break;
+			default:
+				printf("vmbus_pcib: Unknown msg type 0x%x\n",
+				    new_msg->message_type.type);
+				break;
+			}
+			break;
+		default:

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list