diff --git a/examples/meson.build b/examples/meson.build index 0589e357..c8626549 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -14,6 +14,7 @@ examples = { 'io': ['io.c'], 'perf': ['perf.c'], 'regs': ['regs.c'], + 'sriov': ['sriov.c'], } foreach example, sources : examples diff --git a/examples/sriov.c b/examples/sriov.c new file mode 100644 index 00000000..243f298b --- /dev/null +++ b/examples/sriov.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * This file is part of libvfn. + * + * Copyright (C) 2022 The libvfn Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include + +#include + +#include "ccan/err/err.h" +#include "ccan/opt/opt.h" +#include "ccan/str/str.h" + +#include "common.h" + +static int vfnum; + +static struct opt_table opts[] = { + OPT_SUBTABLE(opts_base, NULL), + OPT_WITH_ARG("-s|--secondary VFNUM", opt_set_intval, opt_show_intval, &vfnum, + "secondary controller virtual function number"), + OPT_ENDTABLE, +}; + +int main(int argc, char **argv) +{ + char *vf_bdf; + uint16_t scid; + + struct nvme_ctrl ctrl = {}, sctrl = {}; + + opt_register_table(opts, NULL); + opt_parse(&argc, argv, opt_log_stderr_exit); + + if (show_usage) + opt_usage_and_exit(NULL); + + if (streq(bdf, "")) + opt_usage_exit_fail("missing --device parameter"); + + opt_free_table(); + + if (nvme_init(&ctrl, bdf, NULL)) + err(1, "failed to init nvme controller"); + + vf_bdf = pci_get_vf_bdf(bdf, vfnum); + if (!vf_bdf) + err(1, "pci_get_vf_bdf"); + + if (vfio_pci_open(&sctrl.pci, vf_bdf)) + err(1, "vfio_pci_open"); + + if (nvme_get_vf_cntlid(&ctrl, vfnum, &scid)) + err(1, "nvme_get_vf_cntlid"); + + if (nvme_vm_set_offline(&ctrl, scid)) + err(1, "could not offline secondary controller"); + + if (nvme_vm_assign_max_flexible(&ctrl, scid)) + err(1, "could not assign resources"); + + if (vfio_reset(&sctrl.pci.dev)) + err(1, "vfio_reset"); + + if (nvme_vm_set_online(&ctrl, scid)) + err(1, "could not online secondary controller"); + + if (nvme_init(&sctrl, vf_bdf, NULL)) + err(1, "failed to init nvme controller"); + + return 0; +} diff --git a/include/vfn/nvme/util.h b/include/vfn/nvme/util.h index 0e9b6b0e..ff3c227b 100644 --- a/include/vfn/nvme/util.h +++ b/include/vfn/nvme/util.h @@ -115,4 +115,58 @@ int nvme_sync(struct nvme_ctrl *ctrl, struct nvme_sq *sq, union nvme_cmd *sqe, v int nvme_admin(struct nvme_ctrl *ctrl, union nvme_cmd *sqe, void *buf, size_t len, struct nvme_cqe *cqe_copy); +/** + * nvme_vm_assign_max_flexible - Assign the maximum number of flexible resources + * to secondary controller + * @ctrl: primary controller &struct nvme_ctrl + * @scid: secondary controller identifier + * + * Use the VQFRSM and VIFRSM fields of the Primary Controller Capabilities + * Identify data structure to determine the maximum number of flexible resources + * that can be assigned to a single VF and assign that. + * + * Return: On success, returns ``0``; on error, returns ``-1`` and sets + * ``errno``. + */ +int nvme_vm_assign_max_flexible(struct nvme_ctrl *ctrl, uint16_t scid); + +/** + * nvme_vm_set_online - Online a secondary controller + * @ctrl: primary controller &struct nvme_ctrl + * @scid: secondary controller identifier + * + * Online a secondary controller. + * + * Return: On success, returns ``0``; on error, returns ``-1`` and sets + * ``errno``. + */ +int nvme_vm_set_online(struct nvme_ctrl *ctrl, uint16_t scid); + +/** + * nvme_vm_set_offline - Offline a secondary controller + * @ctrl: primary controller &struct nvme_ctrl + * @scid: secondary controller identifier + * + * Offline a secondary controller. + * + * Return: On success, returns ``0``; on error, returns ``-1`` and sets + * ``errno``. + */ +int nvme_vm_set_offline(struct nvme_ctrl *ctrl, uint16_t scid); + +/** + * nvme_get_vf_cntlid - Get the controller identifier for a VF + * @ctrl: primary controller &struct nvme_ctrl + * @vfnum: virtual function number + * @cntlid: output parameter for the secondary controller identifier + * + * Use the Secondary Controller List Identify data structure to determine the + * controller identifier of the secondary controller identified by a Virtual + * Function Number. + * + * Return: On success, returns ``0``; on error, returns ``-1`` and sets + * ``errno``. + */ +int nvme_get_vf_cntlid(struct nvme_ctrl *ctrl, int vfnum, uint16_t *cntlid); + #endif /* LIBVFN_NVME_UTIL_H */ diff --git a/src/nvme/types.h b/src/nvme/types.h index 91ef66af..3b4900ca 100644 --- a/src/nvme/types.h +++ b/src/nvme/types.h @@ -94,11 +94,14 @@ enum nvme_admin_opcode { NVME_ADMIN_IDENTIFY = 0x06, NVME_ADMIN_SET_FEATURES = 0x09, NVME_ADMIN_ASYNC_EVENT = 0x0c, + NVME_ADMIN_VIRT_MGMT = 0x1c, NVME_ADMIN_DBCONFIG = 0x7c, }; enum nvme_identify_cns { - NVME_IDENTIFY_CNS_CTRL = 0x01, + NVME_IDENTIFY_CNS_CTRL = 0x01, + NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP = 0x14, + NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST = 0x15, }; enum nvme_identify_ctrl_offset { @@ -117,3 +120,55 @@ enum nvme_identify_ctrl_sgls { NVME_IDENTIFY_CTRL_SGLS_ALIGNMENT_NONE = 0x1, NVME_IDENTIFY_CTRL_SGLS_ALIGNMENT_DWORD = 0x2, }; + +struct nvme_primary_ctrl_cap { + leint16_t cntlid; + leint16_t portid; + uint8_t crt; + uint8_t rsvd5[27]; + leint32_t vqfrt; + leint32_t vqrfa; + leint16_t vqrfap; + leint16_t vqprt; + leint16_t vqfrsm; + leint16_t vqgran; + uint8_t rsvd48[16]; + leint32_t vifrt; + leint32_t virfa; + leint16_t virfap; + leint16_t viprt; + leint16_t vifrsm; + leint16_t vigran; + uint8_t rsvd80[4016]; +}; + +struct nvme_secondary_ctrl { + leint16_t scid; + leint16_t pcid; + uint8_t scs; + uint8_t rsvd5[3]; + leint16_t vfn; + leint16_t nvq; + leint16_t nvi; + uint8_t rsvd14[18]; +}; + +#define NVME_ID_SECONDARY_CTRL_MAX 127 + +struct nvme_secondary_ctrl_list { + uint8_t num; + uint8_t rsvd[31]; + struct nvme_secondary_ctrl sc_entry[NVME_ID_SECONDARY_CTRL_MAX]; +}; + +enum nvme_virt_mgmt_rt { + NVME_VIRT_MGMT_RESOURCE_TYPE_VQ = 0x0, + NVME_VIRT_MGMT_RESOURCE_TYPE_VI = 0x1, +}; + +enum nvme_virt_mgmt_act { + NVME_VIRT_MGMT_ACTION_PRIMARY_ALLOC_FLEXIBLE = 0x1, + NVME_VIRT_MGMT_ACTION_SECONDARY_OFFLINE = 0x7, + NVME_VIRT_MGMT_ACTION_SECONDARY_ASSIGN_FLEXIBLE = 0x8, + NVME_VIRT_MGMT_ACTION_SECONDARY_ONLINE = 0x9, +}; diff --git a/src/nvme/util.c b/src/nvme/util.c index c5b25776..14070a19 100644 --- a/src/nvme/util.c +++ b/src/nvme/util.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -134,3 +135,94 @@ int nvme_admin(struct nvme_ctrl *ctrl, union nvme_cmd *sqe, void *buf, size_t le { return nvme_sync(ctrl, ctrl->adminq.sq, sqe, buf, len, cqe_copy); } + +static int nvme_virt_mgmt(struct nvme_ctrl *ctrl, uint16_t cntlid, enum nvme_virt_mgmt_rt rt, + enum nvme_virt_mgmt_act act, uint16_t nr) +{ + union nvme_cmd cmd = { + .opcode = NVME_ADMIN_VIRT_MGMT, + + .cdw10 = cpu_to_le32(cntlid << 16 | rt << 8 | act), + .cdw11 = cpu_to_le32(nr), + }; + + return nvme_admin(ctrl, &cmd, NULL, 0, NULL); +} + +int nvme_vm_assign_max_flexible(struct nvme_ctrl *ctrl, uint16_t scid) +{ + union nvme_cmd cmd; + struct nvme_primary_ctrl_cap *cap; + + __autovar_s(iommu_dmabuf) buffer; + + if (iommu_get_dmabuf(__iommu_ctx(ctrl), &buffer, NVME_IDENTIFY_DATA_SIZE)) + return -1; + + cmd.identify = (struct nvme_cmd_identify) { + .opcode = NVME_ADMIN_IDENTIFY, + .cns = NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP, + }; + + if (nvme_admin(ctrl, &cmd, buffer.vaddr, buffer.len, NULL)) + return -1; + + cap = buffer.vaddr; + + if (nvme_virt_mgmt(ctrl, scid, NVME_VIRT_MGMT_RESOURCE_TYPE_VQ, + NVME_VIRT_MGMT_ACTION_SECONDARY_ASSIGN_FLEXIBLE, + le16_to_cpu(cap->vqfrsm))) + return -1; + + if (nvme_virt_mgmt(ctrl, scid, NVME_VIRT_MGMT_RESOURCE_TYPE_VI, + NVME_VIRT_MGMT_ACTION_SECONDARY_ASSIGN_FLEXIBLE, + le16_to_cpu(cap->vifrsm))) + return -1; + + return 0; +} + +int nvme_get_vf_cntlid(struct nvme_ctrl *ctrl, int vfnum, uint16_t *cntlid) +{ + union nvme_cmd cmd; + struct nvme_secondary_ctrl_list *list; + + __autovar_s(iommu_dmabuf) buffer; + + if (iommu_get_dmabuf(__iommu_ctx(ctrl), &buffer, NVME_IDENTIFY_DATA_SIZE)) + return -1; + + cmd.identify = (struct nvme_cmd_identify) { + .opcode = NVME_ADMIN_IDENTIFY, + .cns = NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST, + }; + + if (nvme_admin(ctrl, &cmd, buffer.vaddr, buffer.len, NULL)) + return -1; + + list = buffer.vaddr; + + for (uint8_t i = 0; i < list->num; i++) { + struct nvme_secondary_ctrl *sctrl = &list->sc_entry[i]; + + if (le16_to_cpu(sctrl->vfn) == vfnum) { + *cntlid = le16_to_cpu(sctrl->scid); + + return 0; + } + } + + errno = ENOENT; + + return -1; +} + +int nvme_vm_set_online(struct nvme_ctrl *ctrl, uint16_t scid) +{ + return nvme_virt_mgmt(ctrl, scid, 0, NVME_VIRT_MGMT_ACTION_SECONDARY_ONLINE, 0); +} + +int nvme_vm_set_offline(struct nvme_ctrl *ctrl, uint16_t scid) +{ + return nvme_virt_mgmt(ctrl, scid, 0, NVME_VIRT_MGMT_ACTION_SECONDARY_OFFLINE, 0); +}