diff options
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/dev/pci/pci_iov.c | 174 | ||||
| -rw-r--r-- | sys/sys/iov.h | 46 |
2 files changed, 219 insertions, 1 deletions
diff --git a/sys/dev/pci/pci_iov.c b/sys/dev/pci/pci_iov.c index 643f0e59b9b8..00a9c8e8be72 100644 --- a/sys/dev/pci/pci_iov.c +++ b/sys/dev/pci/pci_iov.c @@ -27,6 +27,7 @@ #include <sys/cdefs.h> #include "opt_bus.h" +#include <sys/abi_compat.h> #include <sys/param.h> #include <sys/conf.h> #include <sys/kernel.h> @@ -876,6 +877,129 @@ pci_iov_is_child_vf(struct pcicfg_iov *pf, device_t child) } static int +pci_iov_build_status(struct pci_devinfo *dinfo, nvlist_t **statusp) +{ + const char *driver; + device_t bus, child, dev, pcib, *devlist, *vfdevs; + nvlist_t *pf, *status, **vfs; + struct pcicfg_iov *iov; + struct pci_devinfo *vfinfo; + bool attached, passthrough; + int busno, devcount, error, func, i, slot; + uint16_t rid_off, rid_stride, vf_rid; + + mtx_assert(&Giant, MA_OWNED); + + iov = dinfo->cfg.iov; + dev = dinfo->cfg.dev; + bus = device_get_parent(dev); + pcib = device_get_parent(bus); + devlist = NULL; + vfdevs = NULL; + vfs = NULL; + status = NULL; + pf = NULL; + error = 0; + + if (iov->iov_num_vfs != 0) { + vfdevs = mallocarray(iov->iov_num_vfs, sizeof(*vfdevs), + M_SRIOV, M_WAITOK | M_ZERO); + error = device_get_children(bus, &devlist, &devcount); + if (error != 0) + goto out; + for (i = 0; i < devcount; i++) { + child = devlist[i]; + if (!pci_iov_is_child_vf(iov, child)) + continue; + vfinfo = device_get_ivars(child); + if (vfinfo->cfg.vf.index < iov->iov_num_vfs) + vfdevs[vfinfo->cfg.vf.index] = child; + } + } + + status = nvlist_create(0); + pf = nvlist_create(0); + if (status == NULL || pf == NULL) { + error = ENOMEM; + goto out; + } + nvlist_add_number(status, IOV_STATUS_VERSION_NAME, IOV_STATUS_VERSION); + nvlist_add_string(pf, IOV_STATUS_DEVICE_NAME, device_get_nameunit(dev)); + nvlist_add_stringf(pf, IOV_STATUS_PCI_LOCATION_NAME, "pci%u:%u:%u:%u", + (u_int)pci_get_domain(dev), (u_int)pci_get_bus(dev), + (u_int)pci_get_slot(dev), (u_int)pci_get_function(dev)); + nvlist_add_bool(pf, IOV_STATUS_ENABLED_NAME, + (IOV_READ(dinfo, PCIR_SRIOV_CTL, 2) & PCIM_SRIOV_VF_EN) != 0); + nvlist_add_number(pf, IOV_STATUS_NUM_VFS_NAME, iov->iov_num_vfs); + nvlist_add_number(pf, IOV_STATUS_TOTAL_VFS_NAME, + IOV_READ(dinfo, PCIR_SRIOV_TOTAL_VFS, 2)); + error = nvlist_error(pf); + if (error != 0) + goto out; + nvlist_move_nvlist(status, IOV_STATUS_PF_NAME, pf); + pf = NULL; + + if (iov->iov_num_vfs != 0) + vfs = mallocarray(iov->iov_num_vfs, sizeof(*vfs), M_SRIOV, + M_WAITOK | M_ZERO); + rid_off = IOV_READ(dinfo, PCIR_SRIOV_VF_OFF, 2); + rid_stride = IOV_READ(dinfo, PCIR_SRIOV_VF_STRIDE, 2); + vf_rid = pci_get_rid(dev) + rid_off; + for (i = 0; i < iov->iov_num_vfs; i++, vf_rid += rid_stride) { + vfs[i] = nvlist_create(0); + if (vfs[i] == NULL) { + error = ENOMEM; + goto out; + } + nvlist_add_number(vfs[i], IOV_STATUS_VF_INDEX_NAME, i); + child = vfdevs[i]; + if (child != NULL) { + busno = pci_get_bus(child); + slot = pci_get_slot(child); + func = pci_get_function(child); + } else + PCIB_DECODE_RID(pcib, vf_rid, &busno, &slot, &func); + nvlist_add_stringf(vfs[i], IOV_STATUS_PCI_LOCATION_NAME, + "pci%u:%u:%u:%u", (u_int)pci_get_domain(dev), + (u_int)busno, (u_int)slot, (u_int)func); + attached = child != NULL && device_is_attached(child); + passthrough = child != NULL && device_get_name(child) != NULL && + strcmp(device_get_name(child), "ppt") == 0; + nvlist_add_bool(vfs[i], IOV_STATUS_ATTACHED_NAME, attached); + nvlist_add_bool(vfs[i], IOV_STATUS_PASSTHROUGH_NAME, + passthrough); + if (attached) { + driver = device_get_nameunit(child); + if (driver != NULL) + nvlist_add_string(vfs[i], + IOV_STATUS_BOUND_DRIVER_NAME, driver); + } + error = nvlist_error(vfs[i]); + if (error != 0) + goto out; + } + if (iov->iov_num_vfs != 0) + nvlist_add_nvlist_array(status, IOV_STATUS_VFS_NAME, + (const nvlist_t * const *)vfs, iov->iov_num_vfs); + error = nvlist_error(status); + if (error != 0) + goto out; + *statusp = status; + status = NULL; +out: + if (vfs != NULL) { + for (i = 0; i < iov->iov_num_vfs; i++) + nvlist_destroy(vfs[i]); + free(vfs, M_SRIOV); + } + nvlist_destroy(pf); + nvlist_destroy(status); + free(vfdevs, M_SRIOV); + free(devlist, M_TEMP); + return (error); +} + +static int pci_iov_delete_iov_children(struct pci_devinfo *dinfo) { device_t bus, dev, vf, *devlist; @@ -985,7 +1109,8 @@ pci_iov_get_schema_ioctl(struct cdev *cdev, struct pci_iov_schema *output) { struct pci_devinfo *dinfo; void *packed; - size_t output_len, size; + size_t size; + uint64_t output_len; int error; packed = NULL; @@ -1026,6 +1151,50 @@ fail: } static int +pci_iov_get_status_ioctl(struct cdev *cdev, struct pci_iov_status *output) +{ + struct pci_devinfo *dinfo; + nvlist_t *status; + void *packed; + size_t output_len, size; + int error; + + status = NULL; + packed = NULL; + if (output->reserved != 0) + return (EINVAL); + mtx_lock(&Giant); + dinfo = cdev->si_drv1; + error = pci_iov_build_status(dinfo, &status); + mtx_unlock(&Giant); + if (error != 0) + goto out; + + packed = nvlist_pack(status, &size); + if (packed == NULL) { + error = ENOMEM; + goto out; + } + + output_len = output->len; + output->len = size; + if (size <= output_len) { + error = copyout(packed, PTRIN(output->status), size); + if (error != 0) + goto out; + output->error = 0; + } else { + /* Keep the ioctl successful so the required size is copied out. */ + output->error = EMSGSIZE; + } + error = 0; +out: + free(packed, M_NVLIST); + nvlist_destroy(status); + return (error); +} + +static int pci_iov_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { @@ -1038,6 +1207,9 @@ pci_iov_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, case IOV_GET_SCHEMA: return (pci_iov_get_schema_ioctl(dev, (struct pci_iov_schema *)data)); + case IOV_GET_STATUS: + return (pci_iov_get_status_ioctl(dev, + (struct pci_iov_status *)data)); default: return (EINVAL); } diff --git a/sys/sys/iov.h b/sys/sys/iov.h index 2ae7e5ac6767..67a890bba66f 100644 --- a/sys/sys/iov.h +++ b/sys/sys/iov.h @@ -165,6 +165,51 @@ struct pci_iov_schema }; /* + * IOV_GET_STATUS schema contract. + * + * The top-level nvlist contains a version number, a PF nvlist, and, when VFs + * are configured, an array of per-VF nvlists. The "vfs" key is omitted when + * "num-vfs" is zero; its absence therefore means no VFs are configured. The + * PF record identifies the device, reports the live SR-IOV VF Enable state, + * and gives the number of VFs configured by the PCI IOV framework and the + * hardware limit. When present, the array contains one VF record for each + * configured VF, even when its newbus child could not be attached. + * + * PCI locations use FreeBSD's native decimal pciD:B:S:F notation (for + * example, pci0:2:16:2). "attached" means that newbus successfully attached + * a driver. "bound-driver" is present only for an attached VF and contains + * the driver's nameunit. "passthrough" means that the VF has the ppt host + * devclass; it does not imply that a running virtual machine currently owns + * the VF. + * + * Consumers must ignore unknown keys. Additive optional keys retain the + * status version; incompatible type or structural changes require a new + * version. + */ +#define IOV_STATUS_VERSION 1 +#define IOV_STATUS_VERSION_NAME "version" +#define IOV_STATUS_PF_NAME "pf" +#define IOV_STATUS_VFS_NAME "vfs" +#define IOV_STATUS_DEVICE_NAME "device" +#define IOV_STATUS_PCI_LOCATION_NAME "pci-location" +#define IOV_STATUS_ENABLED_NAME "enabled" +#define IOV_STATUS_NUM_VFS_NAME "num-vfs" +#define IOV_STATUS_TOTAL_VFS_NAME "total-vfs" +#define IOV_STATUS_VF_INDEX_NAME "index" +#define IOV_STATUS_ATTACHED_NAME "attached" +#define IOV_STATUS_BOUND_DRIVER_NAME "bound-driver" +#define IOV_STATUS_PASSTHROUGH_NAME "passthrough" + +/* Fixed-width fields keep the ioctl ABI identical for 32-bit callers. */ +struct pci_iov_status +{ + uint64_t status; /* User pointer to the packed nvlist. */ + uint64_t len; + int32_t error; + uint32_t reserved; /* Must be zero. */ +}; + +/* * SR-IOV configuration is passed to the kernel as a packed nvlist. See nv(3) * for the details of the nvlist API. The expected format of the nvlist is: * @@ -254,5 +299,6 @@ struct pci_iov_arg #define IOV_CONFIG _IOW('p', 10, struct pci_iov_arg) #define IOV_DELETE _IO('p', 11) #define IOV_GET_SCHEMA _IOWR('p', 12, struct pci_iov_schema) +#define IOV_GET_STATUS _IOWR('p', 13, struct pci_iov_status) #endif |
