Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 1 Mar 2015 00:40:27 +0000 (UTC)
From:      Ryan Stone <rstone@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r279449 - head/sys/dev/pci
Message-ID:  <201503010040.t210eRTK093003@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rstone
Date: Sun Mar  1 00:40:26 2015
New Revision: 279449
URL: https://svnweb.freebsd.org/changeset/base/279449

Log:
  Allocate PCI I/O memory spaces for VFs
  
  When creating VFs, we must size each SR-IOV BAR on the PF and
  allocate a configuous I/O memory window large enough for every VF.
  However, the window only needs to be aligned to a boundary equal
  to the size of the window for a single VF.
  
  When a VF attempts to allocate an I/O memory resource, we must
  intercept the request in the pci driver and pass it off to the
  SR-IOV code, which will allocate the correct window from the
  pre-allocated memory space for the PF.
  
  Inform the pci driver about the size and address of the BARs on
  the VF when the VF is created.  This is required by pciconf -b and
  bhyve.
  
  Differential Revision:	https://reviews.freebsd.org/D78
  Reviewed by:		jhb
  MFC after: 		1 month
  Sponsored by:		Sandvine Inc.

Modified:
  head/sys/dev/pci/pci.c
  head/sys/dev/pci/pci_iov.c
  head/sys/dev/pci/pci_iov_private.h
  head/sys/dev/pci/pci_private.h
  head/sys/dev/pci/pcivar.h

Modified: head/sys/dev/pci/pci.c
==============================================================================
--- head/sys/dev/pci/pci.c	Sun Mar  1 00:40:19 2015	(r279448)
+++ head/sys/dev/pci/pci.c	Sun Mar  1 00:40:26 2015	(r279449)
@@ -4695,11 +4695,30 @@ struct resource *
 pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
     u_long start, u_long end, u_long count, u_int flags)
 {
+#ifdef PCI_IOV
+	struct pci_devinfo *dinfo;
+#endif
 
 	if (device_get_parent(child) != dev)
 		return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
 		    type, rid, start, end, count, flags));
 
+#ifdef PCI_IOV
+	dinfo = device_get_ivars(child);
+	if (dinfo->cfg.flags & PCICFG_VF) {
+		switch (type) {
+		/* VFs can't have I/O BARs. */
+		case SYS_RES_IOPORT:
+			return (NULL);
+		case SYS_RES_MEMORY:
+			return (pci_vf_alloc_mem_resource(dev, child, rid,
+			    start, end, count, flags));
+		}
+
+		/* Fall through for other types of resource allocations. */
+	}
+#endif
+
 	return (pci_alloc_multi_resource(dev, child, type, rid, start, end,
 	    count, 1, flags));
 }
@@ -4718,6 +4737,22 @@ pci_release_resource(device_t dev, devic
 
 	dinfo = device_get_ivars(child);
 	cfg = &dinfo->cfg;
+
+#ifdef PCI_IOV
+	if (dinfo->cfg.flags & PCICFG_VF) {
+		switch (type) {
+		/* VFs can't have I/O BARs. */
+		case SYS_RES_IOPORT:
+			return (EDOOFUS);
+		case SYS_RES_MEMORY:
+			return (pci_vf_release_mem_resource(dev, child, rid,
+			    r));
+		}
+
+		/* Fall through for other types of resource allocations. */
+	}
+#endif
+
 #ifdef NEW_PCIB
 	/*
 	 * PCI-PCI bridge I/O window resources are not BARs.  For

Modified: head/sys/dev/pci/pci_iov.c
==============================================================================
--- head/sys/dev/pci/pci_iov.c	Sun Mar  1 00:40:19 2015	(r279448)
+++ head/sys/dev/pci/pci_iov.c	Sun Mar  1 00:40:26 2015	(r279449)
@@ -106,7 +106,6 @@ pci_iov_attach_method(device_t bus, devi
 		error = EBUSY;
 		goto cleanup;
 	}
-
 	iov->iov_pos = iov_pos;
 
 	iov->iov_cdev = make_dev(&iov_cdevsw, device_get_unit(dev),
@@ -162,6 +161,56 @@ pci_iov_detach_method(device_t bus, devi
 	return (0);
 }
 
+static int
+pci_iov_alloc_bar(struct pci_devinfo *dinfo, int bar, pci_addr_t bar_shift)
+{
+	struct resource *res;
+	struct pcicfg_iov *iov;
+	device_t dev, bus;
+	u_long start, end;
+	pci_addr_t bar_size;
+	int rid;
+
+	iov = dinfo->cfg.iov;
+	dev = dinfo->cfg.dev;
+	bus = device_get_parent(dev);
+	rid = iov->iov_pos + PCIR_SRIOV_BAR(bar);
+	bar_size = 1 << bar_shift;
+
+	res = pci_alloc_multi_resource(bus, dev, SYS_RES_MEMORY, &rid, 0ul,
+	    ~0ul, 1, iov->iov_num_vfs, RF_ACTIVE);
+
+	if (res == NULL)
+		return (ENXIO);
+
+	iov->iov_bar[bar].res = res;
+	iov->iov_bar[bar].bar_size = bar_size;
+	iov->iov_bar[bar].bar_shift = bar_shift;
+
+	start = rman_get_start(res);
+	end = rman_get_end(res);
+	return (rman_manage_region(&iov->rman, start, end));
+}
+
+static void
+pci_iov_add_bars(struct pcicfg_iov *iov, struct pci_devinfo *dinfo)
+{
+	struct pci_iov_bar *bar;
+	uint64_t bar_start;
+	int i;
+
+	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
+		bar = &iov->iov_bar[i];
+		if (bar->res != NULL) {
+			bar_start = rman_get_start(bar->res) +
+			    dinfo->cfg.vf.index * bar->bar_size;
+
+			pci_add_bar(dinfo->cfg.dev, PCIR_BAR(i), bar_start,
+			    bar->bar_shift);
+		}
+	}
+}
+
 /*
  * Set the ARI_EN bit in the lowest-numbered PCI function with the SR-IOV
  * capability.  This bit is only writeable on the lowest-numbered PF but
@@ -235,6 +284,63 @@ pci_iov_config_page_size(struct pci_devi
 	return (0);
 }
 
+static int
+pci_iov_init_rman(device_t pf, struct pcicfg_iov *iov)
+{
+	int error;
+
+	iov->rman.rm_start = 0;
+	iov->rman.rm_end = ~0ul;
+	iov->rman.rm_type = RMAN_ARRAY;
+	snprintf(iov->rman_name, sizeof(iov->rman_name), "%s VF I/O memory",
+	    device_get_nameunit(pf));
+	iov->rman.rm_descr = iov->rman_name;
+
+	error = rman_init(&iov->rman);
+	if (error != 0)
+		return (error);
+
+	iov->iov_flags |= IOV_RMAN_INITED;
+	return (0);
+}
+
+static int
+pci_iov_setup_bars(struct pci_devinfo *dinfo)
+{
+	device_t dev;
+	struct pcicfg_iov *iov;
+	pci_addr_t bar_value, testval;
+	int i, last_64, error;
+
+	iov = dinfo->cfg.iov;
+	dev = dinfo->cfg.dev;
+	last_64 = 0;
+
+	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
+		/*
+		 * If a PCI BAR is a 64-bit wide BAR, then it spans two
+		 * consecutive registers.  Therefore if the last BAR that
+		 * we looked at was a 64-bit BAR, we need to skip this
+		 * register as it's the second half of the last BAR.
+		 */
+		if (!last_64) {
+			pci_read_bar(dev,
+			    iov->iov_pos + PCIR_SRIOV_BAR(i),
+			    &bar_value, &testval, &last_64);
+
+			if (testval != 0) {
+				error = pci_iov_alloc_bar(dinfo, i,
+				   pci_mapsize(testval));
+				if (error != 0)
+					return (error);
+			}
+		} else
+			last_64 = 0;
+	}
+
+	return (0);
+}
+
 static void
 pci_iov_enumerate_vfs(struct pci_devinfo *dinfo, const char *driver,
     uint16_t first_rid, uint16_t rid_stride)
@@ -266,6 +372,8 @@ pci_iov_enumerate_vfs(struct pci_devinfo
 		vfinfo->cfg.iov = iov;
 		vfinfo->cfg.vf.index = i;
 
+		pci_iov_add_bars(iov, vfinfo);
+
 		error = PCI_ADD_VF(dev, i);
 		if (error != 0) {
 			device_printf(dev, "Failed to add VF %d\n", i);
@@ -283,7 +391,7 @@ pci_iov_config(struct cdev *cdev, struct
 	const char *driver;
 	struct pci_devinfo *dinfo;
 	struct pcicfg_iov *iov;
-	int error;
+	int i, error;
 	uint16_t rid_off, rid_stride;
 	uint16_t first_rid, last_rid;
 	uint16_t iov_ctl;
@@ -350,10 +458,18 @@ pci_iov_config(struct cdev *cdev, struct
 	iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE);
 	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2);
 
+	error = pci_iov_init_rman(dev, iov);
+	if (error != 0)
+		goto out;
+
 	iov->iov_num_vfs = arg->num_vfs;
 
+	error = pci_iov_setup_bars(dinfo);
+	if (error != 0)
+		goto out;
+
 	iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2);
-	iov_ctl |= PCIM_SRIOV_VF_EN;
+	iov_ctl |= PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE;
 	IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2);
 
 	/* Per specification, we must wait 100ms before accessing VFs. */
@@ -365,6 +481,22 @@ pci_iov_config(struct cdev *cdev, struct
 out:
 	if (iov_inited)
 		PCI_UNINIT_IOV(dev);
+
+	for (i = 0; i <= PCIR_MAX_BAR_0; i++) {
+		if (iov->iov_bar[i].res != NULL) {
+			pci_release_resource(bus, dev, SYS_RES_MEMORY,
+			    iov->iov_pos + PCIR_SRIOV_BAR(i),
+			    iov->iov_bar[i].res);
+			pci_delete_resource(bus, dev, SYS_RES_MEMORY,
+			    iov->iov_pos + PCIR_SRIOV_BAR(i));
+			iov->iov_bar[i].res = NULL;
+		}
+	}
+
+	if (iov->iov_flags & IOV_RMAN_INITED) {
+		rman_fini(&iov->rman);
+		iov->iov_flags &= ~IOV_RMAN_INITED;
+	}
 	iov->iov_num_vfs = 0;
 	mtx_unlock(&Giant);
 	return (error);
@@ -383,3 +515,92 @@ pci_iov_ioctl(struct cdev *dev, u_long c
 	}
 }
 
+struct resource *
+pci_vf_alloc_mem_resource(device_t dev, device_t child, int *rid, u_long start,
+    u_long end, u_long count, u_int flags)
+{
+	struct pci_devinfo *dinfo;
+	struct pcicfg_iov *iov;
+	struct pci_map *map;
+	struct resource *res;
+	struct resource_list_entry *rle;
+	u_long bar_start, bar_end;
+	pci_addr_t bar_length;
+	int error;
+
+	dinfo = device_get_ivars(child);
+	iov = dinfo->cfg.iov;
+
+	map = pci_find_bar(child, *rid);
+	if (map == NULL)
+		return (NULL);
+
+	bar_length = 1 << map->pm_size;
+	bar_start = map->pm_value;
+	bar_end = bar_start + bar_length - 1;
+
+	/* Make sure that the resource fits the constraints. */
+	if (bar_start >= end || bar_end <= bar_start || count != 1)
+		return (NULL);
+
+	/* Clamp the resource to the constraints if necessary. */
+	if (bar_start < start)
+		bar_start = start;
+	if (bar_end > end)
+		bar_end = end;
+	bar_length = bar_end - bar_start + 1;
+
+	res = rman_reserve_resource(&iov->rman, bar_start, bar_end,
+	    bar_length, flags, child);
+	if (res == NULL)
+		return (NULL);
+
+	rle = resource_list_add(&dinfo->resources, SYS_RES_MEMORY, *rid,
+	    bar_start, bar_end, 1);
+	if (rle == NULL) {
+		rman_release_resource(res);
+		return (NULL);
+	}
+
+	rman_set_rid(res, *rid);
+
+	if (flags & RF_ACTIVE) {
+		error = bus_activate_resource(child, SYS_RES_MEMORY, *rid, res);
+		if (error != 0) {
+			resource_list_delete(&dinfo->resources, SYS_RES_MEMORY,
+			    *rid);
+			rman_release_resource(res);
+			return (NULL);
+		}
+	}
+	rle->res = res;
+
+	return (res);
+}
+
+int
+pci_vf_release_mem_resource(device_t dev, device_t child, int rid,
+    struct resource *r)
+{
+	struct pci_devinfo *dinfo;
+	struct resource_list_entry *rle;
+	int error;
+
+	dinfo = device_get_ivars(child);
+
+	if (rman_get_flags(r) & RF_ACTIVE) {
+		error = bus_deactivate_resource(child, SYS_RES_MEMORY, rid, r);
+		if (error != 0)
+			return (error);
+	}
+
+	rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, rid);
+	if (rle != NULL) {
+		rle->res = NULL;
+		resource_list_delete(&dinfo->resources, SYS_RES_MEMORY,
+		    rid);
+	}
+
+	return (rman_release_resource(r));
+}
+

Modified: head/sys/dev/pci/pci_iov_private.h
==============================================================================
--- head/sys/dev/pci/pci_iov_private.h	Sun Mar  1 00:40:19 2015	(r279448)
+++ head/sys/dev/pci/pci_iov_private.h	Sun Mar  1 00:40:26 2015	(r279449)
@@ -29,12 +29,26 @@
 #ifndef _PCI_IOV_PRIVATE_H_
 #define _PCI_IOV_PRIVATE_H_
 
+struct pci_iov_bar {
+	struct resource *res;
+
+	pci_addr_t bar_size;
+	pci_addr_t bar_shift;
+};
+
 struct pcicfg_iov {
 	struct cdev *iov_cdev;
+
+	struct pci_iov_bar iov_bar[PCIR_MAX_BAR_0 + 1];
+	struct rman rman;
+	char rman_name[64];
  
 	int iov_pos;
 	int iov_num_vfs;
+	uint32_t iov_flags;
 };
 
+#define	IOV_RMAN_INITED		0x0001
+
 #endif
 

Modified: head/sys/dev/pci/pci_private.h
==============================================================================
--- head/sys/dev/pci/pci_private.h	Sun Mar  1 00:40:19 2015	(r279448)
+++ head/sys/dev/pci/pci_private.h	Sun Mar  1 00:40:26 2015	(r279449)
@@ -158,4 +158,9 @@ int		pci_iov_detach_method(device_t bus,
 device_t	pci_create_iov_child_method(device_t bus, device_t pf,
 		    uint16_t rid, uint16_t vid, uint16_t did);
 
+struct resource *pci_vf_alloc_mem_resource(device_t dev, device_t child,
+		    int *rid, u_long start, u_long end, u_long count,
+		    u_int flags);
+int		pci_vf_release_mem_resource(device_t dev, device_t child,
+		    int rid, struct resource *r);
 #endif /* _PCI_PRIVATE_H_ */

Modified: head/sys/dev/pci/pcivar.h
==============================================================================
--- head/sys/dev/pci/pcivar.h	Sun Mar  1 00:40:19 2015	(r279448)
+++ head/sys/dev/pci/pcivar.h	Sun Mar  1 00:40:26 2015	(r279449)
@@ -50,7 +50,7 @@ struct pcicfg_pp {
 struct pci_map {
     pci_addr_t	pm_value;	/* Raw BAR value */
     pci_addr_t	pm_size;
-    uint8_t	pm_reg;
+    uint16_t	pm_reg;
     STAILQ_ENTRY(pci_map) pm_link;
 };
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201503010040.t210eRTK093003>