From nobody Fri Dec 19 19:07:29 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1593511428; cv=none; d=zohomail.com; s=zohoarc; b=Aq71AuGvRUwB7FkOgrzmTdm4fbXk65TLStZbfgN8839bfVoOIbyTGRt5TshgBS6H5ydT6O6vhW0+LARozo5I3Wv+oE1b5OpJbINL9lHWQTikqpn2eDRxSR7X2pZp3lP7sCgp5SupYE8N1eo2YhYfVjO6+SDAcVOm/zcnrpO6P+c= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1593511428; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=vXpBeJOTFf7Jz38nlFW9V8hBvibb0vOLuUAmBrDqkAg=; b=mh4tRHIH7jb6fjEgddYolrhN37Ae4GVfsTcJjIosfGBHitBdnpqO30NU1rlDPeyj7gmqC55obfLcDNrpUIU2VVfDJCxaii1aueRUeJHF1OvkxNVioOpuplAHJUfeR7tzYlZi799Ra5dCKGqh1pklJ7TMpRNv2d0yW4Yc7QeHljU= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 159351142849633.604695802398396; Tue, 30 Jun 2020 03:03:48 -0700 (PDT) Received: from localhost ([::1]:57726 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jqD7C-0001aB-WC for importer@patchew.org; Tue, 30 Jun 2020 06:03:47 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:57882) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jqD5T-0007hE-2P; Tue, 30 Jun 2020 06:01:59 -0400 Received: from charlie.dont.surf ([128.199.63.193]:47540) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jqD5O-0004J9-Ur; Tue, 30 Jun 2020 06:01:58 -0400 Received: from apples.local (80-167-98-190-cable.dk.customer.tdc.net [80.167.98.190]) by charlie.dont.surf (Postfix) with ESMTPSA id A460FBF717; Tue, 30 Jun 2020 10:01:52 +0000 (UTC) From: Klaus Jensen To: qemu-block@nongnu.org Subject: [PATCH 01/10] hw/block/nvme: support I/O Command Sets Date: Tue, 30 Jun 2020 12:01:30 +0200 Message-Id: <20200630100139.1483002-2-its@irrelevant.dk> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20200630100139.1483002-1-its@irrelevant.dk> References: <20200630100139.1483002-1-its@irrelevant.dk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=128.199.63.193; envelope-from=its@irrelevant.dk; helo=charlie.dont.surf X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/30 04:46:49 X-ACL-Warn: Detected OS = Linux 3.11 and newer [fuzzy] X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Niklas Cassel , Damien Le Moal , Dmitry Fomichev , Klaus Jensen , qemu-devel@nongnu.org, Max Reitz , Klaus Jensen , Keith Busch , Javier Gonzalez , Maxim Levitsky , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Matias Bjorling Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" From: Klaus Jensen Implement support for TP 4056 ("Namespace Types"). This adds the 'iocs' (I/O Command Set) device parameter to the nvme-ns device. Signed-off-by: Klaus Jensen --- block/nvme.c | 6 +- hw/block/nvme-ns.c | 24 +++-- hw/block/nvme-ns.h | 11 +- hw/block/nvme.c | 226 +++++++++++++++++++++++++++++++++--------- hw/block/nvme.h | 52 ++++++---- hw/block/trace-events | 6 +- include/block/nvme.h | 53 ++++++++-- 7 files changed, 285 insertions(+), 93 deletions(-) diff --git a/block/nvme.c b/block/nvme.c index 05485fdd1189..e7fe0c7accd1 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -333,7 +333,7 @@ static inline int nvme_translate_error(const NvmeCqe *c) { uint16_t status =3D (le16_to_cpu(c->status) >> 1) & 0xFF; if (status) { - trace_nvme_error(le32_to_cpu(c->result), + trace_nvme_error(le32_to_cpu(c->dw0), le16_to_cpu(c->sq_head), le16_to_cpu(c->sq_id), le16_to_cpu(c->cid), @@ -495,7 +495,7 @@ static void nvme_identify(BlockDriverState *bs, int nam= espace, Error **errp) { BDRVNVMeState *s =3D bs->opaque; NvmeIdCtrl *idctrl; - NvmeIdNs *idns; + NvmeIdNsNvm *idns; NvmeLBAF *lbaf; uint8_t *resp; uint16_t oncs; @@ -512,7 +512,7 @@ static void nvme_identify(BlockDriverState *bs, int nam= espace, Error **errp) goto out; } idctrl =3D (NvmeIdCtrl *)resp; - idns =3D (NvmeIdNs *)resp; + idns =3D (NvmeIdNsNvm *)resp; r =3D qemu_vfio_dma_map(s->vfio, resp, sizeof(NvmeIdCtrl), true, &iova= ); if (r) { error_setg(errp, "Cannot map buffer for DMA"); diff --git a/hw/block/nvme-ns.c b/hw/block/nvme-ns.c index 7c825c38c69d..ae051784caaf 100644 --- a/hw/block/nvme-ns.c +++ b/hw/block/nvme-ns.c @@ -59,8 +59,16 @@ static int nvme_ns_blk_resize(BlockBackend *blk, size_t = len, Error **errp) =20 static void nvme_ns_init(NvmeNamespace *ns) { - NvmeIdNs *id_ns =3D &ns->id_ns; + NvmeIdNsNvm *id_ns; =20 + int unmap =3D blk_get_flags(ns->blk) & BDRV_O_UNMAP; + + ns->id_ns[NVME_IOCS_NVM] =3D g_new0(NvmeIdNsNvm, 1); + id_ns =3D nvme_ns_id_nvm(ns); + + ns->iocs =3D ns->params.iocs; + + id_ns->dlfeat =3D unmap ? 0x9 : 0x0; id_ns->lbaf[0].ds =3D ns->params.lbads; =20 id_ns->nsze =3D cpu_to_le64(nvme_ns_nlbas(ns)); @@ -130,8 +138,7 @@ static int nvme_ns_init_blk_state(NvmeNamespace *ns, Er= ror **errp) return 0; } =20 -static int nvme_ns_init_blk(NvmeCtrl *n, NvmeNamespace *ns, NvmeIdCtrl *id, - Error **errp) +static int nvme_ns_init_blk(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) { uint64_t perm, shared_perm; =20 @@ -174,7 +181,8 @@ static int nvme_ns_init_blk(NvmeCtrl *n, NvmeNamespace = *ns, NvmeIdCtrl *id, return 0; } =20 -static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp) +static int nvme_ns_check_constraints(NvmeCtrl *n, NvmeNamespace *ns, Error + **errp) { if (!ns->blk) { error_setg(errp, "block backend not configured"); @@ -191,11 +199,11 @@ static int nvme_ns_check_constraints(NvmeNamespace *n= s, Error **errp) =20 int nvme_ns_setup(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) { - if (nvme_ns_check_constraints(ns, errp)) { + if (nvme_ns_check_constraints(n, ns, errp)) { return -1; } =20 - if (nvme_ns_init_blk(n, ns, &n->id_ctrl, errp)) { + if (nvme_ns_init_blk(n, ns, errp)) { return -1; } =20 @@ -210,7 +218,8 @@ int nvme_ns_setup(NvmeCtrl *n, NvmeNamespace *ns, Error= **errp) * With a state file in place we can enable the Deallocated or * Unwritten Logical Block Error feature. */ - ns->id_ns.nsfeat |=3D 0x4; + NvmeIdNsNvm *id_ns =3D nvme_ns_id_nvm(ns); + id_ns->nsfeat |=3D 0x4; } =20 if (nvme_register_namespace(n, ns, errp)) { @@ -239,6 +248,7 @@ static Property nvme_ns_props[] =3D { DEFINE_PROP_UINT32("nsid", NvmeNamespace, params.nsid, 0), DEFINE_PROP_UINT8("lbads", NvmeNamespace, params.lbads, BDRV_SECTOR_BI= TS), DEFINE_PROP_DRIVE("state", NvmeNamespace, blk_state), + DEFINE_PROP_UINT8("iocs", NvmeNamespace, params.iocs, 0x0), DEFINE_PROP_END_OF_LIST(), }; =20 diff --git a/hw/block/nvme-ns.h b/hw/block/nvme-ns.h index eb901acc912b..4124f20f1cef 100644 --- a/hw/block/nvme-ns.h +++ b/hw/block/nvme-ns.h @@ -21,6 +21,7 @@ =20 typedef struct NvmeNamespaceParams { uint32_t nsid; + uint8_t iocs; uint8_t lbads; } NvmeNamespaceParams; =20 @@ -30,8 +31,9 @@ typedef struct NvmeNamespace { BlockBackend *blk_state; int32_t bootindex; int64_t size; + uint8_t iocs; =20 - NvmeIdNs id_ns; + void *id_ns[256]; NvmeNamespaceParams params; =20 unsigned long *utilization; @@ -50,9 +52,14 @@ static inline uint32_t nvme_nsid(NvmeNamespace *ns) return -1; } =20 +static inline NvmeIdNsNvm *nvme_ns_id_nvm(NvmeNamespace *ns) +{ + return ns->id_ns[NVME_IOCS_NVM]; +} + static inline NvmeLBAF *nvme_ns_lbaf(NvmeNamespace *ns) { - NvmeIdNs *id_ns =3D &ns->id_ns; + NvmeIdNsNvm *id_ns =3D nvme_ns_id_nvm(ns); return &id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(id_ns->flbas)]; } =20 diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 25d79bcd0bc9..1662c11a4cf3 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -854,7 +854,7 @@ static void nvme_process_aers(void *opaque) =20 req =3D n->aer_reqs[n->outstanding_aers]; =20 - result =3D (NvmeAerResult *) &req->cqe.result; + result =3D (NvmeAerResult *) &req->cqe.dw0; result->event_type =3D event->result.event_type; result->event_info =3D event->result.event_info; result->log_page =3D event->result.log_page; @@ -916,7 +916,8 @@ static inline uint16_t nvme_check_mdts(NvmeCtrl *n, siz= e_t len) static inline uint16_t nvme_check_bounds(NvmeCtrl *n, NvmeNamespace *ns, uint64_t slba, uint32_t nlb) { - uint64_t nsze =3D le64_to_cpu(ns->id_ns.nsze); + NvmeIdNsNvm *id_ns =3D nvme_ns_id_nvm(ns); + uint64_t nsze =3D le64_to_cpu(id_ns->nsze); =20 if (unlikely(UINT64_MAX - slba < nlb || slba + nlb > nsze)) { return NVME_LBA_RANGE | NVME_DNR; @@ -951,8 +952,9 @@ static uint16_t nvme_check_rw(NvmeCtrl *n, NvmeRequest = *req) =20 status =3D nvme_check_bounds(n, ns, req->slba, req->nlb); if (status) { + NvmeIdNsNvm *id_ns =3D nvme_ns_id_nvm(ns); trace_pci_nvme_err_invalid_lba_range(req->slba, req->nlb, - ns->id_ns.nsze); + id_ns->nsze); return status; } =20 @@ -1154,8 +1156,9 @@ static uint16_t nvme_write_zeroes(NvmeCtrl *n, NvmeRe= quest *req) =20 status =3D nvme_check_bounds(n, ns, req->slba, req->nlb); if (status) { + NvmeIdNsNvm *id_ns =3D nvme_ns_id_nvm(ns); trace_pci_nvme_err_invalid_lba_range(req->slba, req->nlb, - ns->id_ns.nsze); + id_ns->nsze); return status; } =20 @@ -1481,14 +1484,19 @@ static uint16_t nvme_effects_log(NvmeCtrl *n, uint3= 2_t buf_len, uint64_t off, NvmeRequest *req) { uint32_t trans_len; + uint8_t csi =3D le32_to_cpu(req->cmd.cdw14) >> 24; =20 - if (off > sizeof(nvme_effects)) { + if (!(n->iocscs[n->features.iocsci] & (1 << csi))) { return NVME_INVALID_FIELD | NVME_DNR; } =20 - trans_len =3D MIN(sizeof(nvme_effects) - off, buf_len); + if (off > sizeof(NvmeEffectsLog)) { + return NVME_INVALID_FIELD | NVME_DNR; + } =20 - return nvme_dma(n, (uint8_t *)&nvme_effects + off, trans_len, + trans_len =3D MIN(sizeof(NvmeEffectsLog) - off, buf_len); + + return nvme_dma(n, (uint8_t *)&nvme_effects[csi] + off, trans_len, DMA_DIRECTION_FROM_DEVICE, req); } =20 @@ -1648,69 +1656,129 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRe= quest *req) return NVME_SUCCESS; } =20 -static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeRequest *req) +static uint16_t nvme_identify_ctrl(NvmeCtrl *n, uint8_t cns, uint8_t csi, + NvmeRequest *req) { + NvmeIdCtrl empty =3D { 0 }; + NvmeIdCtrl *id_ctrl =3D ∅ + trace_pci_nvme_identify_ctrl(); =20 - return nvme_dma(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), + switch (cns) { + case NVME_ID_CNS_CTRL: + id_ctrl =3D &n->id_ctrl; + + break; + + case NVME_ID_CNS_CTRL_IOCS: + if (!(n->iocscs[n->features.iocsci] & (1 << csi))) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + if (n->id_ctrl_iocss[csi]) { + id_ctrl =3D n->id_ctrl_iocss[csi]; + } + + break; + + default: + assert(cns); + } + + return nvme_dma(n, (uint8_t *)id_ctrl, sizeof(*id_ctrl), DMA_DIRECTION_FROM_DEVICE, req); } =20 -static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req) +static uint16_t nvme_identify_ns(NvmeCtrl *n, uint8_t cns, uint8_t csi, + NvmeRequest *req) { + NvmeIdNsNvm empty =3D { 0 }; + void *id_ns =3D ∅ + uint32_t nsid =3D le32_to_cpu(req->cmd.nsid); NvmeNamespace *ns; - NvmeIdentify *c =3D (NvmeIdentify *)&req->cmd; - NvmeIdNs *id_ns, inactive =3D { 0 }; - uint32_t nsid =3D le32_to_cpu(c->nsid); =20 - trace_pci_nvme_identify_ns(nsid); + trace_pci_nvme_identify_ns(nsid, csi); =20 if (!nvme_nsid_valid(n, nsid) || nsid =3D=3D NVME_NSID_BROADCAST) { return NVME_INVALID_NSID | NVME_DNR; } =20 ns =3D nvme_ns(n, nsid); - if (unlikely(!ns)) { - id_ns =3D &inactive; - } else { - id_ns =3D &ns->id_ns; + if (ns) { + switch (cns) { + case NVME_ID_CNS_NS: + id_ns =3D ns->id_ns[NVME_IOCS_NVM]; + if (!id_ns) { + return NVME_INVALID_IOCS | NVME_DNR; + } + + break; + + case NVME_ID_CNS_NS_IOCS: + if (csi =3D=3D NVME_IOCS_NVM) { + break; + } + + id_ns =3D ns->id_ns[csi]; + if (!id_ns) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + break; + + default: + assert(cns); + } } =20 - return nvme_dma(n, (uint8_t *)id_ns, sizeof(NvmeIdNs), + return nvme_dma(n, (uint8_t *)id_ns, NVME_IDENTIFY_DATA_SIZE, DMA_DIRECTION_FROM_DEVICE, req); } =20 -static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeRequest *req) +static uint16_t nvme_identify_nslist(NvmeCtrl *n, uint8_t cns, uint8_t csi, + NvmeRequest *req) { - NvmeIdentify *c =3D (NvmeIdentify *)&req->cmd; - static const int data_len =3D NVME_IDENTIFY_DATA_SIZE; - uint32_t min_nsid =3D le32_to_cpu(c->nsid); + static const int len =3D NVME_IDENTIFY_DATA_SIZE; + uint32_t min_nsid =3D le32_to_cpu(req->cmd.nsid); uint32_t *list; uint16_t ret; int j =3D 0; =20 - trace_pci_nvme_identify_nslist(min_nsid); + trace_pci_nvme_identify_nslist(min_nsid, csi); =20 - list =3D g_malloc0(data_len); + if (min_nsid =3D=3D 0xfffffffe || min_nsid =3D=3D 0xffffffff) { + return NVME_INVALID_NSID | NVME_DNR; + } + + if (cns =3D=3D NVME_ID_CNS_NS_ACTIVE_LIST_IOCS && !csi) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + list =3D g_malloc0(len); for (int i =3D 1; i <=3D n->num_namespaces; i++) { - if (i <=3D min_nsid || !nvme_ns(n, i)) { + NvmeNamespace *ns =3D nvme_ns(n, i); + if (i <=3D min_nsid || !ns) { continue; } + + if (cns =3D=3D NVME_ID_CNS_NS_ACTIVE_LIST_IOCS && csi && csi !=3D = ns->iocs) { + continue; + } + list[j++] =3D cpu_to_le32(i); - if (j =3D=3D data_len / sizeof(uint32_t)) { + if (j =3D=3D len / sizeof(uint32_t)) { break; } } - ret =3D nvme_dma(n, (uint8_t *)list, data_len, DMA_DIRECTION_FROM_DEVI= CE, - req); + ret =3D nvme_dma(n, (uint8_t *)list, len, DMA_DIRECTION_FROM_DEVICE, r= eq); g_free(list); return ret; } =20 static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req) { - NvmeIdentify *c =3D (NvmeIdentify *)&req->cmd; - uint32_t nsid =3D le32_to_cpu(c->nsid); + NvmeNamespace *ns; + uint32_t nsid =3D le32_to_cpu(req->cmd.nsid); uint8_t list[NVME_IDENTIFY_DATA_SIZE]; =20 struct data { @@ -1718,6 +1786,11 @@ static uint16_t nvme_identify_ns_descr_list(NvmeCtrl= *n, NvmeRequest *req) NvmeIdNsDescr hdr; uint8_t v[16]; } uuid; + + struct { + NvmeIdNsDescr hdr; + uint8_t v; + } iocs; }; =20 struct data *ns_descrs =3D (struct data *)list; @@ -1728,7 +1801,8 @@ static uint16_t nvme_identify_ns_descr_list(NvmeCtrl = *n, NvmeRequest *req) return NVME_INVALID_NSID | NVME_DNR; } =20 - if (unlikely(!nvme_ns(n, nsid))) { + ns =3D nvme_ns(n, nsid); + if (unlikely(!ns)) { return NVME_INVALID_FIELD | NVME_DNR; } =20 @@ -1744,25 +1818,45 @@ static uint16_t nvme_identify_ns_descr_list(NvmeCtr= l *n, NvmeRequest *req) ns_descrs->uuid.hdr.nidl =3D NVME_NIDT_UUID_LEN; stl_be_p(&ns_descrs->uuid.v, nsid); =20 + ns_descrs->iocs.hdr.nidt =3D NVME_NIDT_CSI; + ns_descrs->iocs.hdr.nidl =3D NVME_NIDT_CSI_LEN; + stb_p(&ns_descrs->iocs.v, ns->iocs); + return nvme_dma(n, list, NVME_IDENTIFY_DATA_SIZE, DMA_DIRECTION_FROM_DEVICE, req); } =20 +static uint16_t nvme_identify_iocs(NvmeCtrl *n, uint16_t cntid, + NvmeRequest *req) +{ + return nvme_dma(n, (uint8_t *) n->iocscs, sizeof(n->iocscs), + DMA_DIRECTION_FROM_DEVICE, req); +} + static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) { - NvmeIdentify *c =3D (NvmeIdentify *)&req->cmd; + NvmeIdentify *id =3D (NvmeIdentify *) &req->cmd; =20 - switch (le32_to_cpu(c->cns)) { + trace_pci_nvme_identify(nvme_cid(req), le32_to_cpu(req->cmd.nsid), + le16_to_cpu(id->cntid), id->cns, id->csi, + le16_to_cpu(id->nvmsetid)); + + switch (le32_to_cpu(id->cns)) { case NVME_ID_CNS_NS: - return nvme_identify_ns(n, req); + case NVME_ID_CNS_NS_IOCS: + return nvme_identify_ns(n, id->cns, id->csi, req); case NVME_ID_CNS_CTRL: - return nvme_identify_ctrl(n, req); + case NVME_ID_CNS_CTRL_IOCS: + return nvme_identify_ctrl(n, id->cns, id->csi, req); case NVME_ID_CNS_NS_ACTIVE_LIST: - return nvme_identify_nslist(n, req); + case NVME_ID_CNS_NS_ACTIVE_LIST_IOCS: + return nvme_identify_nslist(n, id->cns, id->csi, req); case NVME_ID_CNS_NS_DESCR_LIST: return nvme_identify_ns_descr_list(n, req); + case NVME_ID_CNS_IOCS: + return nvme_identify_iocs(n, id->cntid, req); default: - trace_pci_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns)); + trace_pci_nvme_err_invalid_identify_cns(id->cns); return NVME_INVALID_FIELD | NVME_DNR; } } @@ -1771,7 +1865,7 @@ static uint16_t nvme_abort(NvmeCtrl *n, NvmeRequest *= req) { uint16_t sqid =3D le32_to_cpu(req->cmd.cdw10) & 0xffff; =20 - req->cqe.result =3D 1; + req->cqe.dw0 =3D 1; if (nvme_check_sqid(n, sqid)) { return NVME_INVALID_FIELD | NVME_DNR; } @@ -1954,13 +2048,17 @@ defaults: =20 result =3D cpu_to_le32(result); break; + case NVME_COMMAND_SET_PROFILE: + result =3D cpu_to_le32(n->features.iocsci & 0x1ff); + break; default: result =3D cpu_to_le32(nvme_feature_default[fid]); break; } =20 out: - req->cqe.result =3D result; + req->cqe.dw0 =3D result; + return NVME_SUCCESS; } =20 @@ -1983,6 +2081,7 @@ static uint16_t nvme_set_feature_timestamp(NvmeCtrl *= n, NvmeRequest *req) static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns =3D NULL; + NvmeIdNsNvm *id_ns; =20 NvmeCmd *cmd =3D &req->cmd; uint32_t dw10 =3D le32_to_cpu(cmd->cdw10); @@ -2059,7 +2158,8 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeReq= uest *req) continue; } =20 - if (NVME_ID_NS_NSFEAT_DULBE(ns->id_ns.nsfeat)) { + id_ns =3D nvme_ns_id_nvm(ns); + if (NVME_ID_NS_NSFEAT_DULBE(id_ns->nsfeat)) { ns->features.err_rec =3D dw11; } } @@ -2075,6 +2175,7 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeReq= uest *req) =20 for (int i =3D 1; i <=3D n->num_namespaces; i++) { ns =3D nvme_ns(n, i); + if (!ns) { continue; } @@ -2105,14 +2206,34 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeR= equest *req) ((dw11 >> 16) & 0xFFFF) + 1, n->params.max_ioqpairs, n->params.max_ioqpairs); - req->cqe.result =3D cpu_to_le32((n->params.max_ioqpairs - 1) | - ((n->params.max_ioqpairs - 1) << 16)= ); + req->cqe.dw0 =3D cpu_to_le32((n->params.max_ioqpairs - 1) | + ((n->params.max_ioqpairs - 1) << 16)); break; case NVME_ASYNCHRONOUS_EVENT_CONF: n->features.async_config =3D dw11; break; case NVME_TIMESTAMP: return nvme_set_feature_timestamp(n, req); + case NVME_COMMAND_SET_PROFILE: + if (NVME_CC_CSS(n->bar.cc) =3D=3D NVME_CC_CSS_ALL) { + uint16_t iocsci =3D dw11 & 0x1ff; + uint64_t iocsc =3D n->iocscs[iocsci]; + + for (int i =3D 1; i <=3D n->num_namespaces; i++) { + ns =3D nvme_ns(n, i); + if (!ns) { + continue; + } + + if (!(iocsc & (1 << ns->iocs))) { + return NVME_IOCS_COMB_REJECTED | NVME_DNR; + } + } + + n->features.iocsci =3D iocsci; + } + + break; default: return NVME_FEAT_NOT_CHANGABLE | NVME_DNR; } @@ -2265,6 +2386,8 @@ static int nvme_start_ctrl(NvmeCtrl *n) uint32_t page_bits =3D NVME_CC_MPS(n->bar.cc) + 12; uint32_t page_size =3D 1 << page_bits; =20 + NvmeIdCtrl *id_ctrl =3D &n->id_ctrl; + if (unlikely(n->cq[0])) { trace_pci_nvme_err_startfail_cq(); return -1; @@ -2304,28 +2427,28 @@ static int nvme_start_ctrl(NvmeCtrl *n) return -1; } if (unlikely(NVME_CC_IOCQES(n->bar.cc) < - NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) { + NVME_CTRL_CQES_MIN(id_ctrl->cqes))) { trace_pci_nvme_err_startfail_cqent_too_small( NVME_CC_IOCQES(n->bar.cc), NVME_CTRL_CQES_MIN(n->bar.cap)); return -1; } if (unlikely(NVME_CC_IOCQES(n->bar.cc) > - NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) { + NVME_CTRL_CQES_MAX(id_ctrl->cqes))) { trace_pci_nvme_err_startfail_cqent_too_large( NVME_CC_IOCQES(n->bar.cc), NVME_CTRL_CQES_MAX(n->bar.cap)); return -1; } if (unlikely(NVME_CC_IOSQES(n->bar.cc) < - NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) { + NVME_CTRL_SQES_MIN(id_ctrl->sqes))) { trace_pci_nvme_err_startfail_sqent_too_small( NVME_CC_IOSQES(n->bar.cc), NVME_CTRL_SQES_MIN(n->bar.cap)); return -1; } if (unlikely(NVME_CC_IOSQES(n->bar.cc) > - NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) { + NVME_CTRL_SQES_MAX(id_ctrl->sqes))) { trace_pci_nvme_err_startfail_sqent_too_large( NVME_CC_IOSQES(n->bar.cc), NVME_CTRL_SQES_MAX(n->bar.cap)); @@ -2774,6 +2897,8 @@ static void nvme_init_state(NvmeCtrl *n) n->features.temp_thresh_hi =3D NVME_TEMPERATURE_WARNING; n->starttime_ms =3D qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); n->aer_reqs =3D g_new0(NvmeRequest *, n->params.aerl + 1); + n->iocscs[0] =3D 1 << NVME_IOCS_NVM; + n->features.iocsci =3D 0; } =20 int nvme_register_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) @@ -2977,7 +3102,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pc= i_dev) NVME_CAP_SET_MQES(n->bar.cap, 0x7ff); NVME_CAP_SET_CQR(n->bar.cap, 1); NVME_CAP_SET_TO(n->bar.cap, 0xf); - NVME_CAP_SET_CSS(n->bar.cap, 1); + NVME_CAP_SET_CSS(n->bar.cap, (NVME_CAP_CSS_NVM | NVME_CAP_CSS_CSI)); NVME_CAP_SET_MPSMAX(n->bar.cap, 4); =20 n->bar.vs =3D NVME_SPEC_VER; @@ -3037,6 +3162,11 @@ static void nvme_exit(PCIDevice *pci_dev) if (n->pmrdev) { host_memory_backend_set_mapped(n->pmrdev, false); } + + for (int i =3D 0; i < 256; i++) { + g_free(n->id_ctrl_iocss[i]); + } + msix_uninit_exclusive_bar(pci_dev); } =20 diff --git a/hw/block/nvme.h b/hw/block/nvme.h index e62bcd12a7a8..69be47963f5d 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -18,28 +18,33 @@ typedef struct NvmeParams { bool use_intel_id; } NvmeParams; =20 -static const NvmeEffectsLog nvme_effects =3D { - .acs =3D { - [NVME_ADM_CMD_DELETE_SQ] =3D NVME_EFFECTS_CSUPP, - [NVME_ADM_CMD_CREATE_SQ] =3D NVME_EFFECTS_CSUPP, - [NVME_ADM_CMD_GET_LOG_PAGE] =3D NVME_EFFECTS_CSUPP, - [NVME_ADM_CMD_DELETE_CQ] =3D NVME_EFFECTS_CSUPP, - [NVME_ADM_CMD_CREATE_CQ] =3D NVME_EFFECTS_CSUPP, - [NVME_ADM_CMD_IDENTIFY] =3D NVME_EFFECTS_CSUPP, - [NVME_ADM_CMD_ABORT] =3D NVME_EFFECTS_CSUPP, - [NVME_ADM_CMD_SET_FEATURES] =3D NVME_EFFECTS_CSUPP | NVME_EFFECTS_= CCC | - NVME_EFFECTS_NIC | NVME_EFFECTS_NCC, - [NVME_ADM_CMD_GET_FEATURES] =3D NVME_EFFECTS_CSUPP, - [NVME_ADM_CMD_FORMAT_NVM] =3D NVME_EFFECTS_CSUPP | NVME_EFFECTS_= LBCC | - NVME_EFFECTS_NCC | NVME_EFFECTS_NIC | NVME_EFFECTS_CSE_MULTI, - [NVME_ADM_CMD_ASYNC_EV_REQ] =3D NVME_EFFECTS_CSUPP, - }, +static const NvmeEffectsLog nvme_effects[] =3D { + [NVME_IOCS_NVM] =3D { + .acs =3D { + [NVME_ADM_CMD_DELETE_SQ] =3D NVME_EFFECTS_CSUPP, + [NVME_ADM_CMD_CREATE_SQ] =3D NVME_EFFECTS_CSUPP, + [NVME_ADM_CMD_GET_LOG_PAGE] =3D NVME_EFFECTS_CSUPP, + [NVME_ADM_CMD_DELETE_CQ] =3D NVME_EFFECTS_CSUPP, + [NVME_ADM_CMD_CREATE_CQ] =3D NVME_EFFECTS_CSUPP, + [NVME_ADM_CMD_IDENTIFY] =3D NVME_EFFECTS_CSUPP, + [NVME_ADM_CMD_ABORT] =3D NVME_EFFECTS_CSUPP, + [NVME_ADM_CMD_SET_FEATURES] =3D NVME_EFFECTS_CSUPP | + NVME_EFFECTS_CCC | NVME_EFFECTS_NIC | NVME_EFFECTS_NCC, + [NVME_ADM_CMD_GET_FEATURES] =3D NVME_EFFECTS_CSUPP, + [NVME_ADM_CMD_FORMAT_NVM] =3D NVME_EFFECTS_CSUPP | + NVME_EFFECTS_LBCC | NVME_EFFECTS_NCC | NVME_EFFECTS_NIC | + NVME_EFFECTS_CSE_MULTI, + [NVME_ADM_CMD_ASYNC_EV_REQ] =3D NVME_EFFECTS_CSUPP, + }, =20 - .iocs =3D { - [NVME_CMD_FLUSH] =3D NVME_EFFECTS_CSUPP, - [NVME_CMD_WRITE] =3D NVME_EFFECTS_CSUPP | NVME_EFFECTS_= LBCC, - [NVME_CMD_READ] =3D NVME_EFFECTS_CSUPP, - [NVME_CMD_WRITE_ZEROES] =3D NVME_EFFECTS_CSUPP | NVME_EFFECTS_= LBCC, + .iocs =3D { + [NVME_CMD_FLUSH] =3D NVME_EFFECTS_CSUPP, + [NVME_CMD_WRITE] =3D NVME_EFFECTS_CSUPP | + NVME_EFFECTS_LBCC, + [NVME_CMD_READ] =3D NVME_EFFECTS_CSUPP, + [NVME_CMD_WRITE_ZEROES] =3D NVME_EFFECTS_CSUPP | + NVME_EFFECTS_LBCC, + }, }, }; =20 @@ -193,6 +198,7 @@ typedef struct NvmeFeatureVal { }; uint32_t async_config; uint32_t vwc; + uint32_t iocsci; } NvmeFeatureVal; =20 static const uint32_t nvme_feature_cap[0x100] =3D { @@ -202,6 +208,7 @@ static const uint32_t nvme_feature_cap[0x100] =3D { [NVME_NUMBER_OF_QUEUES] =3D NVME_FEAT_CAP_CHANGE, [NVME_ASYNCHRONOUS_EVENT_CONF] =3D NVME_FEAT_CAP_CHANGE, [NVME_TIMESTAMP] =3D NVME_FEAT_CAP_CHANGE, + [NVME_COMMAND_SET_PROFILE] =3D NVME_FEAT_CAP_CHANGE, }; =20 static const uint32_t nvme_feature_default[0x100] =3D { @@ -220,6 +227,7 @@ static const bool nvme_feature_support[0x100] =3D { [NVME_WRITE_ATOMICITY] =3D true, [NVME_ASYNCHRONOUS_EVENT_CONF] =3D true, [NVME_TIMESTAMP] =3D true, + [NVME_COMMAND_SET_PROFILE] =3D true, }; =20 typedef struct NvmeCtrl { @@ -247,6 +255,7 @@ typedef struct NvmeCtrl { uint64_t timestamp_set_qemu_clock_ms; /* QEMU clock time */ uint64_t starttime_ms; uint16_t temperature; + uint64_t iocscs[512]; =20 HostMemoryBackend *pmrdev; =20 @@ -262,6 +271,7 @@ typedef struct NvmeCtrl { NvmeSQueue admin_sq; NvmeCQueue admin_cq; NvmeIdCtrl id_ctrl; + void *id_ctrl_iocss[256]; NvmeFeatureVal features; } NvmeCtrl; =20 diff --git a/hw/block/trace-events b/hw/block/trace-events index ed21609f1a4f..4cf0236631d2 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -51,10 +51,12 @@ pci_nvme_create_sq(uint64_t addr, uint16_t sqid, uint16= _t cqid, uint16_t qsize, pci_nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t= size, uint16_t qflags, int ien) "create completion queue, addr=3D0x%"PRIx6= 4", cqid=3D%"PRIu16", vector=3D%"PRIu16", qsize=3D%"PRIu16", qflags=3D%"PRI= u16", ien=3D%d" pci_nvme_del_sq(uint16_t qid) "deleting submission queue sqid=3D%"PRIu16"" pci_nvme_del_cq(uint16_t cqid) "deleted completion queue, cqid=3D%"PRIu16"" +pci_nvme_identify(uint16_t cid, uint32_t nsid, uint16_t cntid, uint8_t cns= , uint8_t csi, uint16_t nvmsetid) "cid %"PRIu16" nsid %"PRIu32" cntid 0x%"P= RIx16" cns 0x%"PRIx8" csi 0x%"PRIx8" nvmsetid %"PRIu16"" pci_nvme_identify_ctrl(void) "identify controller" -pci_nvme_identify_ns(uint32_t ns) "nsid %"PRIu32"" -pci_nvme_identify_nslist(uint32_t ns) "nsid %"PRIu32"" +pci_nvme_identify_ns(uint32_t ns, uint8_t csi) "nsid %"PRIu32" csi 0x%"PRI= x8"" +pci_nvme_identify_nslist(uint32_t ns, uint8_t csi) "nsid %"PRIu32" csi 0x%= "PRIx8"" pci_nvme_identify_ns_descr_list(uint32_t ns) "nsid %"PRIu32"" +pci_nvme_identify_io_cmd_set(uint16_t cid) "cid %"PRIu16"" pci_nvme_get_log(uint16_t cid, uint8_t lid, uint8_t lsp, uint8_t rae, uint= 32_t len, uint64_t off) "cid %"PRIu16" lid 0x%"PRIx8" lsp 0x%"PRIx8" rae 0x= %"PRIx8" len %"PRIu32" off %"PRIu64"" pci_nvme_getfeat(uint16_t cid, uint8_t fid, uint8_t sel, uint32_t cdw11) "= cid %"PRIu16" fid 0x%"PRIx8" sel 0x%"PRIx8" cdw11 0x%"PRIx32"" pci_nvme_setfeat(uint16_t cid, uint8_t fid, uint8_t save, uint32_t cdw11) = "cid %"PRIu16" fid 0x%"PRIx8" save 0x%"PRIx8" cdw11 0x%"PRIx32"" diff --git a/include/block/nvme.h b/include/block/nvme.h index 040e4ef36ddc..637be0ddd2fc 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -93,6 +93,11 @@ enum NvmeCapMask { #define NVME_CAP_SET_CMBS(cap, val) (cap |=3D (uint64_t)(val & CAP_CMBS_= MASK)\ << CAP_CMBS_SH= IFT) =20 +enum NvmeCapCss { + NVME_CAP_CSS_NVM =3D 1 << 0, + NVME_CAP_CSS_CSI =3D 1 << 6, +}; + enum NvmeCcShift { CC_EN_SHIFT =3D 0, CC_CSS_SHIFT =3D 4, @@ -121,6 +126,11 @@ enum NvmeCcMask { #define NVME_CC_IOSQES(cc) ((cc >> CC_IOSQES_SHIFT) & CC_IOSQES_MASK) #define NVME_CC_IOCQES(cc) ((cc >> CC_IOCQES_SHIFT) & CC_IOCQES_MASK) =20 +enum NvmeCcCss { + NVME_CC_CSS_NVM =3D 0x0, + NVME_CC_CSS_ALL =3D 0x6, +}; + enum NvmeCstsShift { CSTS_RDY_SHIFT =3D 0, CSTS_CFS_SHIFT =3D 1, @@ -454,6 +464,10 @@ enum NvmeCmbmscMask { =20 #define NVME_CMBSTS_CBAI(cmbsts) (cmsts & 0x1) =20 +enum NvmeCommandSet { + NVME_IOCS_NVM =3D 0x0, +}; + enum NvmeSglDescriptorType { NVME_SGL_DESCR_TYPE_DATA_BLOCK =3D 0x0, NVME_SGL_DESCR_TYPE_BIT_BUCKET =3D 0x1, @@ -604,7 +618,8 @@ typedef struct NvmeIdentify { uint8_t rsvd3; uint16_t cntid; uint16_t nvmsetid; - uint16_t rsvd4; + uint8_t rsvd4; + uint8_t csi; uint32_t rsvd11[4]; } NvmeIdentify; =20 @@ -697,8 +712,15 @@ typedef struct NvmeAerResult { } NvmeAerResult; =20 typedef struct NvmeCqe { - uint32_t result; - uint32_t rsvd; + union { + struct { + uint32_t dw0; + uint32_t dw1; + }; + + uint64_t qw0; + }; + uint16_t sq_head; uint16_t sq_id; uint16_t cid; @@ -746,6 +768,10 @@ enum NvmeStatusCodes { NVME_FEAT_NOT_CHANGABLE =3D 0x010e, NVME_FEAT_NOT_NS_SPEC =3D 0x010f, NVME_FW_REQ_SUSYSTEM_RESET =3D 0x0110, + NVME_IOCS_NOT_SUPPORTED =3D 0x0127, + NVME_IOCS_NOT_ENABLED =3D 0x0128, + NVME_IOCS_COMB_REJECTED =3D 0x0129, + NVME_INVALID_IOCS =3D 0x0126, NVME_CONFLICTING_ATTRS =3D 0x0180, NVME_INVALID_PROT_INFO =3D 0x0181, NVME_WRITE_TO_RO =3D 0x0182, @@ -890,10 +916,14 @@ typedef struct NvmePSD { #define NVME_IDENTIFY_DATA_SIZE 4096 =20 enum { - NVME_ID_CNS_NS =3D 0x0, - NVME_ID_CNS_CTRL =3D 0x1, - NVME_ID_CNS_NS_ACTIVE_LIST =3D 0x2, - NVME_ID_CNS_NS_DESCR_LIST =3D 0x3, + NVME_ID_CNS_NS =3D 0x00, + NVME_ID_CNS_CTRL =3D 0x01, + NVME_ID_CNS_NS_ACTIVE_LIST =3D 0x02, + NVME_ID_CNS_NS_DESCR_LIST =3D 0x03, + NVME_ID_CNS_NS_IOCS =3D 0x05, + NVME_ID_CNS_CTRL_IOCS =3D 0x06, + NVME_ID_CNS_NS_ACTIVE_LIST_IOCS =3D 0x07, + NVME_ID_CNS_IOCS =3D 0x1c, }; =20 typedef struct NvmeIdCtrl { @@ -1058,6 +1088,7 @@ enum NvmeFeatureIds { NVME_WRITE_ATOMICITY =3D 0xa, NVME_ASYNCHRONOUS_EVENT_CONF =3D 0xb, NVME_TIMESTAMP =3D 0xe, + NVME_COMMAND_SET_PROFILE =3D 0x19, NVME_SOFTWARE_PROGRESS_MARKER =3D 0x80 }; =20 @@ -1105,7 +1136,7 @@ typedef struct NvmeLBAF { =20 #define NVME_NSID_BROADCAST 0xffffffff =20 -typedef struct NvmeIdNs { +typedef struct NvmeIdNsNvm { uint64_t nsze; uint64_t ncap; uint64_t nuse; @@ -1143,7 +1174,7 @@ typedef struct NvmeIdNs { NvmeLBAF lbaf[16]; uint8_t rsvd192[192]; uint8_t vs[3712]; -} NvmeIdNs; +} NvmeIdNsNvm; =20 typedef struct NvmeIdNsDescr { uint8_t nidt; @@ -1154,11 +1185,13 @@ typedef struct NvmeIdNsDescr { #define NVME_NIDT_EUI64_LEN 8 #define NVME_NIDT_NGUID_LEN 16 #define NVME_NIDT_UUID_LEN 16 +#define NVME_NIDT_CSI_LEN 1 =20 enum { NVME_NIDT_EUI64 =3D 0x1, NVME_NIDT_NGUID =3D 0x2, NVME_NIDT_UUID =3D 0x3, + NVME_NIDT_CSI =3D 0x4, }; =20 /*Deallocate Logical Block Features*/ @@ -1211,7 +1244,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) !=3D 512); QEMU_BUILD_BUG_ON(sizeof(NvmeEnduranceGroupLog) !=3D 512); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrl) !=3D 4096); - QEMU_BUILD_BUG_ON(sizeof(NvmeIdNs) !=3D 4096); + QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsNvm) !=3D 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeNvmSetAttributes) !=3D 128); QEMU_BUILD_BUG_ON(sizeof(NvmeIdNvmSetList) !=3D 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeBar) !=3D 4096); --=20 2.27.0