pci_iov.c revision 296336
1139724Simp/*- 24Srgrimes * Copyright (c) 2013-2015 Sandvine Inc. All rights reserved. 34Srgrimes * All rights reserved. 44Srgrimes * 58876Srgrimes * Redistribution and use in source and binary forms, with or without 64Srgrimes * modification, are permitted provided that the following conditions 74Srgrimes * are met: 84Srgrimes * 1. Redistributions of source code must retain the above copyright 94Srgrimes * notice, this list of conditions and the following disclaimer. 104Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 118876Srgrimes * notice, this list of conditions and the following disclaimer in the 128876Srgrimes * documentation and/or other materials provided with the distribution. 134Srgrimes * 144Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 158876Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 164Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 178876Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 184Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 194Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 204Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 214Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 228876Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 234Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 244Srgrimes * SUCH DAMAGE. 254Srgrimes */ 264Srgrimes 27115683Sobrien#include <sys/cdefs.h> 28115683Sobrien__FBSDID("$FreeBSD: head/sys/dev/pci/pci_iov.c 296336 2016-03-03 05:07:35Z jhibbits $"); 29115683Sobrien 304Srgrimes#include "opt_bus.h" 314Srgrimes 324Srgrimes#include <sys/param.h> 332056Swollman#include <sys/conf.h> 3424494Sbde#include <sys/kernel.h> 352056Swollman#include <sys/systm.h> 364Srgrimes#include <sys/bus.h> 374Srgrimes#include <sys/fcntl.h> 384Srgrimes#include <sys/ioccom.h> 394Srgrimes#include <sys/iov.h> 404Srgrimes#include <sys/linker.h> 414Srgrimes#include <sys/malloc.h> 424Srgrimes#include <sys/module.h> 434Srgrimes#include <sys/pciio.h> 444Srgrimes#include <sys/queue.h> 454Srgrimes#include <sys/rman.h> 464Srgrimes#include <sys/sysctl.h> 474Srgrimes 484Srgrimes#include <machine/bus.h> 494Srgrimes#include <machine/stdarg.h> 504Srgrimes 514Srgrimes#include <sys/nv.h> 524Srgrimes#include <sys/iov_schema.h> 534Srgrimes 544Srgrimes#include <dev/pci/pcireg.h> 554Srgrimes#include <dev/pci/pcivar.h> 564Srgrimes#include <dev/pci/pci_iov.h> 574Srgrimes#include <dev/pci/pci_private.h> 584Srgrimes#include <dev/pci/pci_iov_private.h> 594Srgrimes#include <dev/pci/schema_private.h> 604Srgrimes 614Srgrimes#include "pcib_if.h" 624Srgrimes 634Srgrimesstatic MALLOC_DEFINE(M_SRIOV, "sr_iov", "PCI SR-IOV allocations"); 644Srgrimes 654Srgrimesstatic d_ioctl_t pci_iov_ioctl; 664Srgrimes 674Srgrimesstatic struct cdevsw iov_cdevsw = { 684Srgrimes .d_version = D_VERSION, 694Srgrimes .d_name = "iov", 704Srgrimes .d_ioctl = pci_iov_ioctl 714Srgrimes}; 724Srgrimes 734SrgrimesSYSCTL_DECL(_hw_pci); 744Srgrimes 754Srgrimes/* 764Srgrimes * The maximum amount of memory we will allocate for user configuration of an 774Srgrimes * SR-IOV device. 1MB ought to be enough for anyone, but leave this 784Srgrimes * configurable just in case. 794Srgrimes */ 804Srgrimesstatic u_long pci_iov_max_config = 1024 * 1024; 814SrgrimesSYSCTL_ULONG(_hw_pci, OID_AUTO, iov_max_config, CTLFLAG_RWTUN, 824Srgrimes &pci_iov_max_config, 0, "Maximum allowed size of SR-IOV configuration."); 834Srgrimes 844Srgrimes 854Srgrimes#define IOV_READ(d, r, w) \ 864Srgrimes pci_read_config((d)->cfg.dev, (d)->cfg.iov->iov_pos + r, w) 874Srgrimes 8821277Sbde#define IOV_WRITE(d, r, v, w) \ 8921277Sbde pci_write_config((d)->cfg.dev, (d)->cfg.iov->iov_pos + r, v, w) 9021277Sbde 914Srgrimesstatic nvlist_t *pci_iov_build_schema(nvlist_t **pf_schema, 9211940Sbde nvlist_t **vf_schema); 9314887Swollmanstatic void pci_iov_build_pf_schema(nvlist_t *schema, 944Srgrimes nvlist_t **driver_schema); 954Srgrimesstatic void pci_iov_build_vf_schema(nvlist_t *schema, 964Srgrimes nvlist_t **driver_schema); 9717109Sbdestatic nvlist_t *pci_iov_get_pf_subsystem_schema(void); 984Srgrimesstatic nvlist_t *pci_iov_get_vf_subsystem_schema(void); 994Srgrimes 1004Srgrimesint 1014Srgrimespci_iov_attach_method(device_t bus, device_t dev, nvlist_t *pf_schema, 1024Srgrimes nvlist_t *vf_schema) 1034Srgrimes{ 10411940Sbde device_t pcib; 10514887Swollman struct pci_devinfo *dinfo; 1064Srgrimes struct pcicfg_iov *iov; 1074Srgrimes nvlist_t *schema; 10817109Sbde uint32_t version; 1094Srgrimes int error; 1104Srgrimes int iov_pos; 1114Srgrimes 11214887Swollman dinfo = device_get_ivars(dev); 1134Srgrimes pcib = device_get_parent(bus); 1144Srgrimes schema = NULL; 1154Srgrimes 1164Srgrimes error = pci_find_extcap(dev, PCIZ_SRIOV, &iov_pos); 1174Srgrimes 1184Srgrimes if (error != 0) 1194Srgrimes return (error); 1204Srgrimes 1214Srgrimes version = pci_read_config(dev, iov_pos, 4); 1224Srgrimes if (PCI_EXTCAP_VER(version) != 1) { 12314887Swollman if (bootverbose) 1244Srgrimes device_printf(dev, 1254Srgrimes "Unsupported version of SR-IOV (%d) detected\n", 1264Srgrimes PCI_EXTCAP_VER(version)); 1274Srgrimes 1284Srgrimes return (ENXIO); 1294Srgrimes } 1304Srgrimes 1314Srgrimes iov = malloc(sizeof(*dinfo->cfg.iov), M_SRIOV, M_WAITOK | M_ZERO); 1324Srgrimes 1334Srgrimes mtx_lock(&Giant); 13414887Swollman if (dinfo->cfg.iov != NULL) { 1354Srgrimes error = EBUSY; 1364Srgrimes goto cleanup; 1374Srgrimes } 1384Srgrimes iov->iov_pos = iov_pos; 1394Srgrimes 1404Srgrimes schema = pci_iov_build_schema(&pf_schema, &vf_schema); 1414Srgrimes if (schema == NULL) { 1424Srgrimes error = ENOMEM; 1434Srgrimes goto cleanup; 1444Srgrimes } 14521277Sbde 14621277Sbde error = pci_iov_validate_schema(schema); 14721277Sbde if (error != 0) 14821277Sbde goto cleanup; 14921277Sbde iov->iov_schema = schema; 15021277Sbde 15121277Sbde iov->iov_cdev = make_dev(&iov_cdevsw, device_get_unit(dev), 15221277Sbde UID_ROOT, GID_WHEEL, 0600, "iov/%s", device_get_nameunit(dev)); 15321277Sbde 15421277Sbde if (iov->iov_cdev == NULL) { 15521277Sbde error = ENOMEM; 156181606Sjhb goto cleanup; 157181606Sjhb } 158181606Sjhb 159181606Sjhb dinfo->cfg.iov = iov; 160181606Sjhb iov->iov_cdev->si_drv1 = dinfo; 161181606Sjhb mtx_unlock(&Giant); 162181606Sjhb 163181606Sjhb return (0); 164181606Sjhb 165181606Sjhbcleanup: 166181606Sjhb nvlist_destroy(schema); 167181606Sjhb nvlist_destroy(pf_schema); 168181606Sjhb nvlist_destroy(vf_schema); 169181606Sjhb free(iov, M_SRIOV); 170181606Sjhb mtx_unlock(&Giant); 171181606Sjhb return (error); 172181606Sjhb} 173181606Sjhb 174181606Sjhbint 175181606Sjhbpci_iov_detach_method(device_t bus, device_t dev) 176181606Sjhb{ 177181606Sjhb struct pci_devinfo *dinfo; 17814887Swollman struct pcicfg_iov *iov; 17917109Sbde 18017109Sbde mtx_lock(&Giant); 1814Srgrimes dinfo = device_get_ivars(dev); 1824Srgrimes iov = dinfo->cfg.iov; 1834Srgrimes 184181606Sjhb if (iov == NULL) { 1854Srgrimes mtx_unlock(&Giant); 186181606Sjhb return (0); 1874Srgrimes } 1884Srgrimes 1894Srgrimes if (iov->iov_num_vfs != 0 || iov->iov_flags & IOV_BUSY) { 1904Srgrimes mtx_unlock(&Giant); 1914Srgrimes return (EBUSY); 1924Srgrimes } 1934Srgrimes 1944Srgrimes dinfo->cfg.iov = NULL; 1954Srgrimes 1964Srgrimes if (iov->iov_cdev) { 1974Srgrimes destroy_dev(iov->iov_cdev); 198278655Smarkj iov->iov_cdev = NULL; 199278655Smarkj } 200278655Smarkj nvlist_destroy(iov->iov_schema); 201278655Smarkj 202278655Smarkj free(iov, M_SRIOV); 203278655Smarkj mtx_unlock(&Giant); 204278655Smarkj 205278655Smarkj return (0); 206278655Smarkj} 207278655Smarkj 208278655Smarkjstatic nvlist_t * 209278655Smarkjpci_iov_build_schema(nvlist_t **pf, nvlist_t **vf) 210278655Smarkj{ 211278655Smarkj nvlist_t *schema, *pf_driver, *vf_driver; 212278655Smarkj 213278655Smarkj /* We always take ownership of the schemas. */ 214278655Smarkj pf_driver = *pf; 215278655Smarkj *pf = NULL; 216278655Smarkj vf_driver = *vf; 217278655Smarkj *vf = NULL; 21817109Sbde 21921277Sbde schema = pci_iov_schema_alloc_node(); 22021277Sbde if (schema == NULL) 22121277Sbde goto cleanup; 22221277Sbde 22321277Sbde pci_iov_build_pf_schema(schema, &pf_driver); 2244Srgrimes pci_iov_build_vf_schema(schema, &vf_driver); 22521277Sbde 2264Srgrimes if (nvlist_error(schema) != 0) 2274Srgrimes goto cleanup; 2284Srgrimes 2294Srgrimes return (schema); 2304Srgrimes 2314Srgrimescleanup: 2324Srgrimes nvlist_destroy(schema); 2334Srgrimes nvlist_destroy(pf_driver); 2344Srgrimes nvlist_destroy(vf_driver); 2354Srgrimes return (NULL); 2364Srgrimes} 2374Srgrimes 23814887Swollmanstatic void 23914887Swollmanpci_iov_build_pf_schema(nvlist_t *schema, nvlist_t **driver_schema) 24014887Swollman{ 24114887Swollman nvlist_t *pf_schema, *iov_schema; 24214887Swollman 243181606Sjhb pf_schema = pci_iov_schema_alloc_node(); 244181606Sjhb if (pf_schema == NULL) { 24514887Swollman nvlist_set_error(schema, ENOMEM); 246181606Sjhb return; 24714887Swollman } 24814887Swollman 24914887Swollman iov_schema = pci_iov_get_pf_subsystem_schema(); 25014887Swollman 25114887Swollman /* 25214887Swollman * Note that if either *driver_schema or iov_schema is NULL, then 25314887Swollman * nvlist_move_nvlist will put the schema in the error state and 25414887Swollman * SR-IOV will fail to initialize later, so we don't have to explicitly 25514887Swollman * handle that case. 25614887Swollman */ 25714887Swollman nvlist_move_nvlist(pf_schema, DRIVER_CONFIG_NAME, *driver_schema); 258181603Sjhb nvlist_move_nvlist(pf_schema, IOV_CONFIG_NAME, iov_schema); 259181603Sjhb nvlist_move_nvlist(schema, PF_CONFIG_NAME, pf_schema); 260181603Sjhb *driver_schema = NULL; 261181603Sjhb} 262181603Sjhb 263181603Sjhbstatic void 264181603Sjhbpci_iov_build_vf_schema(nvlist_t *schema, nvlist_t **driver_schema) 265181603Sjhb{ 266181603Sjhb nvlist_t *vf_schema, *iov_schema; 267181603Sjhb 268181603Sjhb vf_schema = pci_iov_schema_alloc_node(); 269181603Sjhb if (vf_schema == NULL) { 270181603Sjhb nvlist_set_error(schema, ENOMEM); 271181603Sjhb return; 272181603Sjhb } 273181603Sjhb 274181603Sjhb iov_schema = pci_iov_get_vf_subsystem_schema(); 275181603Sjhb 276181603Sjhb /* 277181603Sjhb * Note that if either *driver_schema or iov_schema is NULL, then 27817109Sbde * nvlist_move_nvlist will put the schema in the error state and 2794Srgrimes * SR-IOV will fail to initialize later, so we don't have to explicitly 2804Srgrimes * handle that case. 2814Srgrimes */ 2824Srgrimes nvlist_move_nvlist(vf_schema, DRIVER_CONFIG_NAME, *driver_schema); 2834Srgrimes nvlist_move_nvlist(vf_schema, IOV_CONFIG_NAME, iov_schema); 2844Srgrimes nvlist_move_nvlist(schema, VF_SCHEMA_NAME, vf_schema); 2854Srgrimes *driver_schema = NULL; 2864Srgrimes} 2874Srgrimes 2884Srgrimesstatic nvlist_t * 2894Srgrimespci_iov_get_pf_subsystem_schema(void) 2904Srgrimes{ 2914Srgrimes nvlist_t *pf; 2924Srgrimes 2934Srgrimes pf = pci_iov_schema_alloc_node(); 2944Srgrimes if (pf == NULL) 2954Srgrimes return (NULL); 2964Srgrimes 2974Srgrimes pci_iov_schema_add_uint16(pf, "num_vfs", IOV_SCHEMA_REQUIRED, -1); 29817109Sbde pci_iov_schema_add_string(pf, "device", IOV_SCHEMA_REQUIRED, NULL); 2994Srgrimes 3004Srgrimes return (pf); 3014Srgrimes} 3024Srgrimes 3034Srgrimesstatic nvlist_t * 3044Srgrimespci_iov_get_vf_subsystem_schema(void) 3054Srgrimes{ 3064Srgrimes nvlist_t *vf; 3074Srgrimes 3084Srgrimes vf = pci_iov_schema_alloc_node(); 3094Srgrimes if (vf == NULL) 3104Srgrimes return (NULL); 3114Srgrimes 3124Srgrimes pci_iov_schema_add_bool(vf, "passthrough", IOV_SCHEMA_HASDEFAULT, 0); 3134Srgrimes 3144Srgrimes return (vf); 3154Srgrimes} 3164Srgrimes 3174Srgrimesstatic int 31817109Sbdepci_iov_alloc_bar(struct pci_devinfo *dinfo, int bar, pci_addr_t bar_shift) 3194Srgrimes{ 3204Srgrimes struct resource *res; 32121277Sbde struct pcicfg_iov *iov; 32221277Sbde device_t dev, bus; 32317109Sbde rman_res_t start, end; 32417109Sbde pci_addr_t bar_size; 3254Srgrimes int rid; 3264Srgrimes 3274Srgrimes iov = dinfo->cfg.iov; 3284Srgrimes dev = dinfo->cfg.dev; 3294Srgrimes bus = device_get_parent(dev); 33021277Sbde rid = iov->iov_pos + PCIR_SRIOV_BAR(bar); 33121277Sbde bar_size = 1 << bar_shift; 33217109Sbde 33317109Sbde res = pci_alloc_multi_resource(bus, dev, SYS_RES_MEMORY, &rid, 0, 334181606Sjhb ~0, 1, iov->iov_num_vfs, RF_ACTIVE); 335181606Sjhb 3364Srgrimes if (res == NULL) 3374Srgrimes return (ENXIO); 33817109Sbde 33921277Sbde iov->iov_bar[bar].res = res; 34021277Sbde iov->iov_bar[bar].bar_size = bar_size; 3414Srgrimes iov->iov_bar[bar].bar_shift = bar_shift; 34221277Sbde 3434Srgrimes start = rman_get_start(res); 3444Srgrimes end = rman_get_end(res); 34521277Sbde return (rman_manage_region(&iov->rman, start, end)); 34621277Sbde} 3474Srgrimes 3484Srgrimesstatic void 3494Srgrimespci_iov_add_bars(struct pcicfg_iov *iov, struct pci_devinfo *dinfo) 35017109Sbde{ 3514Srgrimes struct pci_iov_bar *bar; 3524Srgrimes uint64_t bar_start; 3534Srgrimes int i; 35421277Sbde 35521277Sbde for (i = 0; i <= PCIR_MAX_BAR_0; i++) { 3564Srgrimes bar = &iov->iov_bar[i]; 3574Srgrimes if (bar->res != NULL) { 35817109Sbde bar_start = rman_get_start(bar->res) + 3594Srgrimes dinfo->cfg.vf.index * bar->bar_size; 3604Srgrimes 3614Srgrimes pci_add_bar(dinfo->cfg.dev, PCIR_BAR(i), bar_start, 3624Srgrimes bar->bar_shift); 3634Srgrimes } 3644Srgrimes } 3654Srgrimes} 36621277Sbde 36721277Sbdestatic int 36821277Sbdepci_iov_parse_config(struct pcicfg_iov *iov, struct pci_iov_arg *arg, 36921277Sbde nvlist_t **ret) 37021277Sbde{ 37121277Sbde void *packed_config; 37221277Sbde nvlist_t *config; 37321277Sbde int error; 37421277Sbde 3754Srgrimes config = NULL; 3764Srgrimes packed_config = NULL; 37714887Swollman 3784Srgrimes if (arg->len > pci_iov_max_config) { 379278655Smarkj error = EMSGSIZE; 3804Srgrimes goto out; 38114887Swollman } 382181603Sjhb 3834Srgrimes packed_config = malloc(arg->len, M_SRIOV, M_WAITOK); 3844Srgrimes 3854Srgrimes error = copyin(arg->config, packed_config, arg->len); 3864Srgrimes if (error != 0) 3874Srgrimes goto out; 3884Srgrimes 3894Srgrimes config = nvlist_unpack(packed_config, arg->len, NV_FLAG_IGNORE_CASE); 3904Srgrimes if (config == NULL) { 3914Srgrimes error = EINVAL; 39221277Sbde goto out; 3934Srgrimes } 3944Srgrimes 3954Srgrimes error = pci_iov_schema_validate_config(iov->iov_schema, config); 39614887Swollman if (error != 0) 3974Srgrimes goto out; 3984Srgrimes 39914887Swollman error = nvlist_error(config); 4004Srgrimes if (error != 0) 4014Srgrimes goto out; 40217109Sbde 4034Srgrimes *ret = config; 4044Srgrimes config = NULL; 40517109Sbde 4064Srgrimesout: 4074Srgrimes nvlist_destroy(config); 4084Srgrimes free(packed_config, M_SRIOV); 40914887Swollman return (error); 4104Srgrimes} 4114Srgrimes 4124Srgrimes/* 41321277Sbde * Set the ARI_EN bit in the lowest-numbered PCI function with the SR-IOV 4144Srgrimes * capability. This bit is only writeable on the lowest-numbered PF but 4154Srgrimes * affects all PFs on the device. 4164Srgrimes */ 41717109Sbdestatic int 41821277Sbdepci_iov_set_ari(device_t bus) 4194Srgrimes{ 4204Srgrimes device_t lowest; 42114887Swollman device_t *devlist; 4224Srgrimes int i, error, devcount, lowest_func, lowest_pos, iov_pos, dev_func; 4234Srgrimes uint16_t iov_ctl; 4244Srgrimes 42517109Sbde /* If ARI is disabled on the downstream port there is nothing to do. */ 4264Srgrimes if (!PCIB_ARI_ENABLED(device_get_parent(bus))) 4274Srgrimes return (0); 4284Srgrimes 42914887Swollman error = device_get_children(bus, &devlist, &devcount); 4304Srgrimes 4314Srgrimes if (error != 0) 4324Srgrimes return (error); 4334Srgrimes 4344Srgrimes lowest = NULL; 4354Srgrimes for (i = 0; i < devcount; i++) { 4364Srgrimes if (pci_find_extcap(devlist[i], PCIZ_SRIOV, &iov_pos) == 0) { 4374Srgrimes dev_func = pci_get_function(devlist[i]); 4384Srgrimes if (lowest == NULL || dev_func < lowest_func) { 4394Srgrimes lowest = devlist[i]; 44014887Swollman lowest_func = dev_func; 4414Srgrimes lowest_pos = iov_pos; 4424Srgrimes } 44317109Sbde } 44421277Sbde } 44517109Sbde 44617109Sbde /* 44717109Sbde * If we called this function some device must have the SR-IOV 44817109Sbde * capability. 4494Srgrimes */ 4504Srgrimes KASSERT(lowest != NULL, 45114887Swollman ("Could not find child of %s with SR-IOV capability", 45221277Sbde device_get_nameunit(bus))); 45321277Sbde 45421277Sbde iov_ctl = pci_read_config(lowest, iov_pos + PCIR_SRIOV_CTL, 2); 45521277Sbde iov_ctl |= PCIM_SRIOV_ARI_EN; 45621277Sbde pci_write_config(lowest, iov_pos + PCIR_SRIOV_CTL, iov_ctl, 2); 45721277Sbde free(devlist, M_TEMP); 45821277Sbde return (0); 45921277Sbde} 4604Srgrimes 4614Srgrimesstatic int 46214887Swollmanpci_iov_config_page_size(struct pci_devinfo *dinfo) 46321277Sbde{ 4644Srgrimes uint32_t page_cap, page_size; 46521277Sbde 46621277Sbde page_cap = IOV_READ(dinfo, PCIR_SRIOV_PAGE_CAP, 4); 46717109Sbde 4684Srgrimes /* 4694Srgrimes * If the system page size is less than the smallest SR-IOV page size 4704Srgrimes * then round up to the smallest SR-IOV page size. 4714Srgrimes */ 4724Srgrimes if (PAGE_SHIFT < PCI_SRIOV_BASE_PAGE_SHIFT) 47314887Swollman page_size = (1 << 0); 4744Srgrimes else 4754Srgrimes page_size = (1 << (PAGE_SHIFT - PCI_SRIOV_BASE_PAGE_SHIFT)); 47621277Sbde 47721277Sbde /* Check that the device supports the system page size. */ 4784Srgrimes if (!(page_size & page_cap)) 4794Srgrimes return (ENXIO); 4804Srgrimes 4814Srgrimes IOV_WRITE(dinfo, PCIR_SRIOV_PAGE_SIZE, page_size, 4); 4824Srgrimes return (0); 4834Srgrimes} 48414887Swollman 4854Srgrimesstatic int 4864Srgrimespci_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *config) 4874Srgrimes{ 4884Srgrimes const nvlist_t *device, *driver_config; 4894Srgrimes 4904Srgrimes device = nvlist_get_nvlist(config, PF_CONFIG_NAME); 4914Srgrimes driver_config = nvlist_get_nvlist(device, DRIVER_CONFIG_NAME); 4924Srgrimes return (PCI_IOV_INIT(dev, num_vfs, driver_config)); 4934Srgrimes} 4944Srgrimes 49514887Swollmanstatic int 49621277Sbdepci_iov_init_rman(device_t pf, struct pcicfg_iov *iov) 49721277Sbde{ 49821277Sbde int error; 49921277Sbde 50021277Sbde iov->rman.rm_start = 0; 50121277Sbde iov->rman.rm_end = ~0; 50221277Sbde iov->rman.rm_type = RMAN_ARRAY; 50321277Sbde snprintf(iov->rman_name, sizeof(iov->rman_name), "%s VF I/O memory", 5044Srgrimes device_get_nameunit(pf)); 5054Srgrimes iov->rman.rm_descr = iov->rman_name; 50614887Swollman 50721277Sbde error = rman_init(&iov->rman); 50821277Sbde if (error != 0) 50921277Sbde return (error); 51021277Sbde 51117109Sbde iov->iov_flags |= IOV_RMAN_INITED; 51221277Sbde return (0); 5134Srgrimes} 51421277Sbde 5154Srgrimesstatic int 5164Srgrimespci_iov_alloc_bar_ea(struct pci_devinfo *dinfo, int bar) 51717109Sbde{ 5184Srgrimes struct pcicfg_iov *iov; 5194Srgrimes rman_res_t start, end; 5204Srgrimes struct resource *res; 5214Srgrimes struct resource_list *rl; 52214887Swollman struct resource_list_entry *rle; 5234Srgrimes 5244Srgrimes rl = &dinfo->resources; 5254Srgrimes iov = dinfo->cfg.iov; 5264Srgrimes 5274Srgrimes rle = resource_list_find(rl, SYS_RES_MEMORY, 5284Srgrimes iov->iov_pos + PCIR_SRIOV_BAR(bar)); 5294Srgrimes if (rle == NULL) 5304Srgrimes rle = resource_list_find(rl, SYS_RES_IOPORT, 5314Srgrimes iov->iov_pos + PCIR_SRIOV_BAR(bar)); 5324Srgrimes if (rle == NULL) 53314887Swollman return (ENXIO); 5344Srgrimes res = rle->res; 5354Srgrimes 5364Srgrimes iov->iov_bar[bar].res = res; 5374Srgrimes iov->iov_bar[bar].bar_size = rman_get_size(res) / iov->iov_num_vfs; 5384Srgrimes iov->iov_bar[bar].bar_shift = pci_mapsize(iov->iov_bar[bar].bar_size); 5394Srgrimes 5404Srgrimes start = rman_get_start(res); 5414Srgrimes end = rman_get_end(res); 5424Srgrimes 5434Srgrimes return (rman_manage_region(&iov->rman, start, end)); 54414887Swollman} 5454Srgrimes 5464Srgrimesstatic int 5474Srgrimespci_iov_setup_bars(struct pci_devinfo *dinfo) 5484Srgrimes{ 5494Srgrimes device_t dev; 5504Srgrimes struct pcicfg_iov *iov; 5514Srgrimes pci_addr_t bar_value, testval; 5524Srgrimes int i, last_64, error; 5534Srgrimes 5544Srgrimes iov = dinfo->cfg.iov; 55517109Sbde dev = dinfo->cfg.dev; 5564Srgrimes last_64 = 0; 5574Srgrimes 5584Srgrimes pci_add_resources_ea(device_get_parent(dev), dev, 1); 5594Srgrimes 5604Srgrimes for (i = 0; i <= PCIR_MAX_BAR_0; i++) { 5614Srgrimes /* First, try to use BARs allocated with EA */ 5624Srgrimes error = pci_iov_alloc_bar_ea(dinfo, i); 5634Srgrimes if (error == 0) 5644Srgrimes continue; 5654Srgrimes 56617109Sbde /* Allocate legacy-BAR only if EA is not enabled */ 5674Srgrimes if (pci_ea_is_enabled(dev, iov->iov_pos + PCIR_SRIOV_BAR(i))) 5684Srgrimes continue; 56921277Sbde 57021277Sbde /* 57121277Sbde * If a PCI BAR is a 64-bit wide BAR, then it spans two 57221277Sbde * consecutive registers. Therefore if the last BAR that 5734Srgrimes * we looked at was a 64-bit BAR, we need to skip this 5744Srgrimes * register as it's the second half of the last BAR. 5754Srgrimes */ 5764Srgrimes if (!last_64) { 57714887Swollman pci_read_bar(dev, 5784Srgrimes iov->iov_pos + PCIR_SRIOV_BAR(i), 5794Srgrimes &bar_value, &testval, &last_64); 5804Srgrimes 5814Srgrimes if (testval != 0) { 58221277Sbde error = pci_iov_alloc_bar(dinfo, i, 5834Srgrimes pci_mapsize(testval)); 5844Srgrimes if (error != 0) 5854Srgrimes return (error); 5864Srgrimes } 5874Srgrimes } else 5884Srgrimes last_64 = 0; 5894Srgrimes } 5904Srgrimes 5914Srgrimes return (0); 5924Srgrimes} 5934Srgrimes 5944Srgrimesstatic void 5954Srgrimespci_iov_enumerate_vfs(struct pci_devinfo *dinfo, const nvlist_t *config, 5964Srgrimes uint16_t first_rid, uint16_t rid_stride) 5974Srgrimes{ 5984Srgrimes char device_name[VF_MAX_NAME]; 5994Srgrimes const nvlist_t *device, *driver_config, *iov_config; 60021277Sbde device_t bus, dev, vf; 6014Srgrimes struct pcicfg_iov *iov; 6024Srgrimes struct pci_devinfo *vfinfo; 6034Srgrimes size_t size; 6044Srgrimes int i, error; 6054Srgrimes uint16_t vid, did, next_rid; 6064Srgrimes 6074Srgrimes iov = dinfo->cfg.iov; 6084Srgrimes dev = dinfo->cfg.dev; 60921277Sbde bus = device_get_parent(dev); 6104Srgrimes size = dinfo->cfg.devinfo_size; 6114Srgrimes next_rid = first_rid; 6124Srgrimes vid = pci_get_vendor(dev); 6134Srgrimes did = IOV_READ(dinfo, PCIR_SRIOV_VF_DID, 2); 6144Srgrimes 6154Srgrimes for (i = 0; i < iov->iov_num_vfs; i++, next_rid += rid_stride) { 6164Srgrimes snprintf(device_name, sizeof(device_name), VF_PREFIX"%d", i); 6174Srgrimes device = nvlist_get_nvlist(config, device_name); 6184Srgrimes iov_config = nvlist_get_nvlist(device, IOV_CONFIG_NAME); 6194Srgrimes driver_config = nvlist_get_nvlist(device, DRIVER_CONFIG_NAME); 6204Srgrimes 62121277Sbde vf = PCI_CREATE_IOV_CHILD(bus, dev, next_rid, vid, did); 6224Srgrimes if (vf == NULL) 6234Srgrimes break; 6244Srgrimes 6254Srgrimes /* 6264Srgrimes * If we are creating passthrough devices then force the ppt 62721277Sbde * driver to attach to prevent a VF driver from claiming the 6284Srgrimes * VFs. 6294Srgrimes */ 6304Srgrimes if (nvlist_get_bool(iov_config, "passthrough")) 6314Srgrimes device_set_devclass_fixed(vf, "ppt"); 6324Srgrimes 6334Srgrimes vfinfo = device_get_ivars(vf); 6344Srgrimes 6354Srgrimes vfinfo->cfg.iov = iov; 6364Srgrimes vfinfo->cfg.vf.index = i; 6374Srgrimes 6384Srgrimes pci_iov_add_bars(iov, vfinfo); 63921277Sbde 6404Srgrimes error = PCI_IOV_ADD_VF(dev, i, driver_config); 6414Srgrimes if (error != 0) { 6424Srgrimes device_printf(dev, "Failed to add VF %d\n", i); 6434Srgrimes pci_delete_child(bus, vf); 6444Srgrimes } 64521277Sbde } 6464Srgrimes 6474Srgrimes bus_generic_attach(bus); 6484Srgrimes} 6494Srgrimes 6504Srgrimesstatic int 6514Srgrimespci_iov_config(struct cdev *cdev, struct pci_iov_arg *arg) 6524Srgrimes{ 6534Srgrimes device_t bus, dev; 6544Srgrimes struct pci_devinfo *dinfo; 6554Srgrimes struct pcicfg_iov *iov; 6564Srgrimes nvlist_t *config; 6574Srgrimes int i, error; 6584Srgrimes uint16_t rid_off, rid_stride; 6594Srgrimes uint16_t first_rid, last_rid; 6604Srgrimes uint16_t iov_ctl; 6614Srgrimes uint16_t num_vfs, total_vfs; 6624Srgrimes int iov_inited; 6634Srgrimes 6644Srgrimes mtx_lock(&Giant); 6654Srgrimes dinfo = cdev->si_drv1; 6664Srgrimes iov = dinfo->cfg.iov; 6674Srgrimes dev = dinfo->cfg.dev; 6684Srgrimes bus = device_get_parent(dev); 6694Srgrimes iov_inited = 0; 6704Srgrimes config = NULL; 6714Srgrimes 6724Srgrimes if ((iov->iov_flags & IOV_BUSY) || iov->iov_num_vfs != 0) { 6734Srgrimes mtx_unlock(&Giant); 6744Srgrimes return (EBUSY); 6754Srgrimes } 6764Srgrimes iov->iov_flags |= IOV_BUSY; 6774Srgrimes 6784Srgrimes error = pci_iov_parse_config(iov, arg, &config); 6794Srgrimes if (error != 0) 6804Srgrimes goto out; 6814Srgrimes 6824Srgrimes num_vfs = pci_iov_config_get_num_vfs(config); 6834Srgrimes total_vfs = IOV_READ(dinfo, PCIR_SRIOV_TOTAL_VFS, 2); 6844Srgrimes if (num_vfs > total_vfs) { 6854Srgrimes error = EINVAL; 6864Srgrimes goto out; 6874Srgrimes } 6884Srgrimes 68921277Sbde error = pci_iov_config_page_size(dinfo); 6904Srgrimes if (error != 0) 6914Srgrimes goto out; 6924Srgrimes 6934Srgrimes error = pci_iov_set_ari(bus); 6944Srgrimes if (error != 0) 6954Srgrimes goto out; 6964Srgrimes 6974Srgrimes error = pci_iov_init(dev, num_vfs, config); 69821277Sbde if (error != 0) 6994Srgrimes goto out; 7004Srgrimes iov_inited = 1; 7014Srgrimes 7024Srgrimes IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, num_vfs, 2); 7034Srgrimes 7044Srgrimes rid_off = IOV_READ(dinfo, PCIR_SRIOV_VF_OFF, 2); 7054Srgrimes rid_stride = IOV_READ(dinfo, PCIR_SRIOV_VF_STRIDE, 2); 7064Srgrimes 7074Srgrimes first_rid = pci_get_rid(dev) + rid_off; 7084Srgrimes last_rid = first_rid + (num_vfs - 1) * rid_stride; 7094Srgrimes 7104Srgrimes /* We don't yet support allocating extra bus numbers for VFs. */ 7114Srgrimes if (pci_get_bus(dev) != PCI_RID2BUS(last_rid)) { 7124Srgrimes error = ENOSPC; 7134Srgrimes goto out; 7144Srgrimes } 7154Srgrimes 7164Srgrimes iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2); 7174Srgrimes iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE); 7184Srgrimes IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2); 7194Srgrimes 7204Srgrimes error = pci_iov_init_rman(dev, iov); 7214Srgrimes if (error != 0) 7224Srgrimes goto out; 72317109Sbde 72417109Sbde iov->iov_num_vfs = num_vfs; 72521277Sbde 72617109Sbde error = pci_iov_setup_bars(dinfo); 7274Srgrimes if (error != 0) 7284Srgrimes goto out; 7294Srgrimes 7304Srgrimes iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2); 7314Srgrimes iov_ctl |= PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE; 7324Srgrimes IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2); 7334Srgrimes 7344Srgrimes /* Per specification, we must wait 100ms before accessing VFs. */ 7354Srgrimes pause("iov", roundup(hz, 10)); 7364Srgrimes pci_iov_enumerate_vfs(dinfo, config, first_rid, rid_stride); 7374Srgrimes 7384Srgrimes nvlist_destroy(config); 7394Srgrimes iov->iov_flags &= ~IOV_BUSY; 7404Srgrimes mtx_unlock(&Giant); 7414Srgrimes 7424Srgrimes return (0); 7434Srgrimesout: 7444Srgrimes if (iov_inited) 7454Srgrimes PCI_IOV_UNINIT(dev); 7464Srgrimes 7474Srgrimes for (i = 0; i <= PCIR_MAX_BAR_0; i++) { 7484Srgrimes if (iov->iov_bar[i].res != NULL) { 7494Srgrimes pci_release_resource(bus, dev, SYS_RES_MEMORY, 7504Srgrimes iov->iov_pos + PCIR_SRIOV_BAR(i), 7514Srgrimes iov->iov_bar[i].res); 7524Srgrimes pci_delete_resource(bus, dev, SYS_RES_MEMORY, 7534Srgrimes iov->iov_pos + PCIR_SRIOV_BAR(i)); 7544Srgrimes iov->iov_bar[i].res = NULL; 7554Srgrimes } 7564Srgrimes } 7574Srgrimes 7584Srgrimes if (iov->iov_flags & IOV_RMAN_INITED) { 7594Srgrimes rman_fini(&iov->rman); 7604Srgrimes iov->iov_flags &= ~IOV_RMAN_INITED; 7614Srgrimes } 7624Srgrimes 7634Srgrimes nvlist_destroy(config); 7644Srgrimes iov->iov_num_vfs = 0; 7654Srgrimes iov->iov_flags &= ~IOV_BUSY; 7664Srgrimes mtx_unlock(&Giant); 7674Srgrimes return (error); 7684Srgrimes} 7694Srgrimes 7704Srgrimes/* Return true if child is a VF of the given PF. */ 7714Srgrimesstatic int 772118Srgrimespci_iov_is_child_vf(struct pcicfg_iov *pf, device_t child) 773118Srgrimes{ 7744Srgrimes struct pci_devinfo *vfinfo; 7754Srgrimes 7764Srgrimes vfinfo = device_get_ivars(child); 7774Srgrimes 7784Srgrimes if (!(vfinfo->cfg.flags & PCICFG_VF)) 7794Srgrimes return (0); 7804Srgrimes 7814Srgrimes return (pf == vfinfo->cfg.iov); 7824Srgrimes} 7834Srgrimes 7844Srgrimesstatic int 7854Srgrimespci_iov_delete(struct cdev *cdev) 7864Srgrimes{ 7874Srgrimes device_t bus, dev, vf, *devlist; 7884Srgrimes struct pci_devinfo *dinfo; 7894Srgrimes struct pcicfg_iov *iov; 7904Srgrimes int i, error, devcount; 7914Srgrimes uint32_t iov_ctl; 7924Srgrimes 7934Srgrimes mtx_lock(&Giant); 7944Srgrimes dinfo = cdev->si_drv1; 79517109Sbde iov = dinfo->cfg.iov; 79617109Sbde dev = dinfo->cfg.dev; 7974Srgrimes bus = device_get_parent(dev); 7984Srgrimes devlist = NULL; 7994Srgrimes 8004Srgrimes if (iov->iov_flags & IOV_BUSY) { 8014Srgrimes mtx_unlock(&Giant); 8024Srgrimes return (EBUSY); 8034Srgrimes } 80421277Sbde 805270844Spfg if (iov->iov_num_vfs == 0) { 8064Srgrimes mtx_unlock(&Giant); 8074Srgrimes return (ECHILD); 8084Srgrimes } 8094Srgrimes 8104Srgrimes iov->iov_flags |= IOV_BUSY; 8114Srgrimes 8124Srgrimes error = device_get_children(bus, &devlist, &devcount); 81317109Sbde 81417109Sbde if (error != 0) 81517109Sbde goto out; 81617109Sbde 81721277Sbde for (i = 0; i < devcount; i++) { 81821277Sbde vf = devlist[i]; 81921277Sbde 8204Srgrimes if (!pci_iov_is_child_vf(iov, vf)) 8214Srgrimes continue; 82217109Sbde 82317109Sbde error = device_detach(vf); 82417109Sbde if (error != 0) { 82517109Sbde device_printf(dev, 82617109Sbde "Could not disable SR-IOV: failed to detach VF %s\n", 82717109Sbde device_get_nameunit(vf)); 82817109Sbde goto out; 82917109Sbde } 8304Srgrimes } 8314Srgrimes 8324Srgrimes for (i = 0; i < devcount; i++) { 8334Srgrimes vf = devlist[i]; 8344Srgrimes 8354Srgrimes if (pci_iov_is_child_vf(iov, vf)) 8364Srgrimes pci_delete_child(bus, vf); 8374Srgrimes } 8384Srgrimes PCI_IOV_UNINIT(dev); 8394Srgrimes 8404Srgrimes iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2); 8414Srgrimes iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE); 8424Srgrimes IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2); 8434Srgrimes IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, 0, 2); 8444Srgrimes 8454Srgrimes iov->iov_num_vfs = 0; 8464Srgrimes 8474Srgrimes for (i = 0; i <= PCIR_MAX_BAR_0; i++) { 8484Srgrimes if (iov->iov_bar[i].res != NULL) { 8494Srgrimes pci_release_resource(bus, dev, SYS_RES_MEMORY, 85021277Sbde iov->iov_pos + PCIR_SRIOV_BAR(i), 8514Srgrimes iov->iov_bar[i].res); 8524Srgrimes pci_delete_resource(bus, dev, SYS_RES_MEMORY, 8534Srgrimes iov->iov_pos + PCIR_SRIOV_BAR(i)); 8544Srgrimes iov->iov_bar[i].res = NULL; 85517109Sbde } 85617109Sbde } 8574Srgrimes 8584Srgrimes if (iov->iov_flags & IOV_RMAN_INITED) { 8594Srgrimes rman_fini(&iov->rman); 8604Srgrimes iov->iov_flags &= ~IOV_RMAN_INITED; 8614Srgrimes } 8624Srgrimes 8634Srgrimes error = 0; 86417109Sbdeout: 86517109Sbde free(devlist, M_TEMP); 8664Srgrimes iov->iov_flags &= ~IOV_BUSY; 8674Srgrimes mtx_unlock(&Giant); 86817109Sbde return (error); 8694Srgrimes} 8704Srgrimes 8714Srgrimesstatic int 8724Srgrimespci_iov_get_schema_ioctl(struct cdev *cdev, struct pci_iov_schema *output) 8734Srgrimes{ 8744Srgrimes struct pci_devinfo *dinfo; 8754Srgrimes void *packed; 8764Srgrimes size_t output_len, size; 8774Srgrimes int error; 8784Srgrimes 8794Srgrimes packed = NULL; 88011940Sbde 8814Srgrimes mtx_lock(&Giant); 8824Srgrimes dinfo = cdev->si_drv1; 88314887Swollman packed = nvlist_pack(dinfo->cfg.iov->iov_schema, &size); 88414887Swollman mtx_unlock(&Giant); 8854Srgrimes 8864Srgrimes if (packed == NULL) { 8874Srgrimes error = ENOMEM; 88814887Swollman goto fail; 8894Srgrimes } 8904Srgrimes 8914Srgrimes output_len = output->len; 8924Srgrimes output->len = size; 8934Srgrimes if (size <= output_len) { 8944Srgrimes error = copyout(packed, output->schema, size); 8954Srgrimes 8964Srgrimes if (error != 0) 8974Srgrimes goto fail; 8984Srgrimes 89914887Swollman output->error = 0; 90043314Sdillon } else 90143314Sdillon /* 90243314Sdillon * If we return an error then the ioctl code won't copyout 9034Srgrimes * output back to userland, so we flag the error in the struct 9044Srgrimes * instead. 90517109Sbde */ 9064Srgrimes output->error = EMSGSIZE; 9074Srgrimes 9084Srgrimes error = 0; 9094Srgrimes 9104Srgrimesfail: 9114Srgrimes free(packed, M_NVLIST); 91214887Swollman 9134Srgrimes return (error); 9144Srgrimes} 9154Srgrimes 9164Srgrimesstatic int 9174Srgrimespci_iov_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 9184Srgrimes struct thread *td) 9194Srgrimes{ 9204Srgrimes 9214Srgrimes switch (cmd) { 9224Srgrimes case IOV_CONFIG: 9234Srgrimes return (pci_iov_config(dev, (struct pci_iov_arg *)data)); 9244Srgrimes case IOV_DELETE: 9254Srgrimes return (pci_iov_delete(dev)); 92611940Sbde case IOV_GET_SCHEMA: 92792770Salfred return (pci_iov_get_schema_ioctl(dev, 92893017Sbde (struct pci_iov_schema *)data)); 92992770Salfred default: 93093017Sbde return (EINVAL); 93111940Sbde } 93293017Sbde} 93393017Sbde 93411940Sbdestruct resource * 9354Srgrimespci_vf_alloc_mem_resource(device_t dev, device_t child, int *rid, 9364Srgrimes rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 9374Srgrimes{ 93811921Sphk struct pci_devinfo *dinfo; 9394Srgrimes struct pcicfg_iov *iov; 9404Srgrimes struct pci_map *map; 9414Srgrimes struct resource *res; 9424Srgrimes struct resource_list_entry *rle; 94317109Sbde rman_res_t bar_start, bar_end; 9444Srgrimes pci_addr_t bar_length; 9453436Sphk int error; 9464Srgrimes 9474Srgrimes dinfo = device_get_ivars(child); 9484Srgrimes iov = dinfo->cfg.iov; 9494Srgrimes 9504Srgrimes map = pci_find_bar(child, *rid); 9514Srgrimes if (map == NULL) 9524Srgrimes return (NULL); 9534Srgrimes 9544Srgrimes bar_length = 1 << map->pm_size; 9554Srgrimes bar_start = map->pm_value; 9564Srgrimes bar_end = bar_start + bar_length - 1; 9574Srgrimes 9584Srgrimes /* Make sure that the resource fits the constraints. */ 9594Srgrimes if (bar_start >= end || bar_end <= bar_start || count != 1) 9604Srgrimes return (NULL); 9614Srgrimes 9624Srgrimes /* Clamp the resource to the constraints if necessary. */ 9634Srgrimes if (bar_start < start) 96421277Sbde bar_start = start; 9654Srgrimes if (bar_end > end) 9664Srgrimes bar_end = end; 9674Srgrimes bar_length = bar_end - bar_start + 1; 9684Srgrimes 9694Srgrimes res = rman_reserve_resource(&iov->rman, bar_start, bar_end, 9704Srgrimes bar_length, flags, child); 9714Srgrimes if (res == NULL) 9724Srgrimes return (NULL); 9734Srgrimes 9744Srgrimes rle = resource_list_add(&dinfo->resources, SYS_RES_MEMORY, *rid, 97521277Sbde bar_start, bar_end, 1); 9764Srgrimes if (rle == NULL) { 9774Srgrimes rman_release_resource(res); 9784Srgrimes return (NULL); 9794Srgrimes } 98021277Sbde 9814Srgrimes rman_set_rid(res, *rid); 9824Srgrimes 9834Srgrimes if (flags & RF_ACTIVE) { 9844Srgrimes error = bus_activate_resource(child, SYS_RES_MEMORY, *rid, res); 9854Srgrimes if (error != 0) { 9864Srgrimes resource_list_delete(&dinfo->resources, SYS_RES_MEMORY, 9874Srgrimes *rid); 9884Srgrimes rman_release_resource(res); 9894Srgrimes return (NULL); 9904Srgrimes } 9914Srgrimes } 9924Srgrimes rle->res = res; 9934Srgrimes 9944Srgrimes return (res); 9954Srgrimes} 9964Srgrimes 9974Srgrimesint 9984Srgrimespci_vf_release_mem_resource(device_t dev, device_t child, int rid, 9994Srgrimes struct resource *r) 10004Srgrimes{ 10014Srgrimes struct pci_devinfo *dinfo; 10024Srgrimes struct resource_list_entry *rle; 10034Srgrimes int error; 10044Srgrimes 10054Srgrimes dinfo = device_get_ivars(child); 10064Srgrimes 10074Srgrimes if (rman_get_flags(r) & RF_ACTIVE) { 10084Srgrimes error = bus_deactivate_resource(child, SYS_RES_MEMORY, rid, r); 10094Srgrimes if (error != 0) 10104Srgrimes return (error); 10114Srgrimes } 10124Srgrimes 10134Srgrimes rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, rid); 10144Srgrimes if (rle != NULL) { 10154Srgrimes rle->res = NULL; 10164Srgrimes resource_list_delete(&dinfo->resources, SYS_RES_MEMORY, 10174Srgrimes rid); 10184Srgrimes } 10194Srgrimes 10204Srgrimes return (rman_release_resource(r)); 10214Srgrimes} 10224Srgrimes 10234Srgrimes