From nobody Wed Apr 9 05:27:23 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 149607071753850.368484799354746; Mon, 29 May 2017 08:11:57 -0700 (PDT) Received: from localhost ([::1]:49130 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dFMKm-0004sJ-SY for importer@patchew.org; Mon, 29 May 2017 11:11:52 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45521) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dFMGE-0000yE-8B for qemu-devel@nongnu.org; Mon, 29 May 2017 11:07:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dFMGB-0006O1-Lq for qemu-devel@nongnu.org; Mon, 29 May 2017 11:07:10 -0400 Received: from mx1.redhat.com ([209.132.183.28]:59156) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dFMG7-0006LX-9s; Mon, 29 May 2017 11:07:03 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 4C1414E4D0; Mon, 29 May 2017 15:07:02 +0000 (UTC) Received: from noname.redhat.com (ovpn-117-181.ams2.redhat.com [10.36.117.181]) by smtp.corp.redhat.com (Postfix) with ESMTP id 51A6278C30; Mon, 29 May 2017 15:07:01 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 4C1414E4D0 Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=kwolf@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 4C1414E4D0 From: Kevin Wolf To: qemu-block@nongnu.org Date: Mon, 29 May 2017 17:06:43 +0200 Message-Id: <1496070414-6744-5-git-send-email-kwolf@redhat.com> In-Reply-To: <1496070414-6744-1-git-send-email-kwolf@redhat.com> References: <1496070414-6744-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Mon, 29 May 2017 15:07:02 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PULL 04/15] nvme: Add support for Controller Memory Buffers X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Stephen Bates Implement NVMe Controller Memory Buffers (CMBs) which were added in version 1.2 of the NVMe Specification. This patch adds an optional argument (cmb_size_mb) which indicates the size of the CMB (in MB). Currently only the Submission Queue Support (SQS) is enabled which aligns with the current Linux driver for NVMe. Signed-off-by: Stephen Bates Acked-by: Keith Busch Signed-off-by: Kevin Wolf --- hw/block/nvme.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++= +--- hw/block/nvme.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++= ++ 2 files changed, 144 insertions(+), 4 deletions(-) diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 7428db9..381dc7c 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -9,7 +9,7 @@ */ =20 /** - * Reference Specs: http://www.nvmexpress.org, 1.1, 1.0e + * Reference Specs: http://www.nvmexpress.org, 1.2, 1.1, 1.0e * * http://www.nvmexpress.org/resources/ */ @@ -17,7 +17,11 @@ /** * Usage: add options: * -drive file=3D,if=3Dnone,id=3D - * -device nvme,drive=3D,serial=3D,id=3D + * -device nvme,drive=3D,serial=3D,id=3D, \ + * cmb_size_mb=3D + * + * Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at + * offset 0 in BAR2 and supports SQS only for now. */ =20 #include "qemu/osdep.h" @@ -34,6 +38,16 @@ =20 static void nvme_process_sq(void *opaque); =20 +static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) +{ + if (n->cmbsz && addr >=3D n->ctrl_mem.addr && + addr < (n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size))= ) { + memcpy(buf, (void *)&n->cmbuf[addr - n->ctrl_mem.addr], size); + } else { + pci_dma_read(&n->parent_obj, addr, buf, size); + } +} + static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid) { return sqid < n->num_queues && n->sq[sqid] !=3D NULL ? 0 : -1; @@ -637,7 +651,7 @@ static void nvme_process_sq(void *opaque) =20 while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) { addr =3D sq->dma_addr + sq->head * n->sqe_size; - pci_dma_read(&n->parent_obj, addr, (void *)&cmd, sizeof(cmd)); + nvme_addr_read(n, addr, (void *)&cmd, sizeof(cmd)); nvme_inc_sq_head(sq); =20 req =3D QTAILQ_FIRST(&sq->req_list); @@ -852,6 +866,32 @@ static const MemoryRegionOps nvme_mmio_ops =3D { }, }; =20 +static void nvme_cmb_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + NvmeCtrl *n =3D (NvmeCtrl *)opaque; + memcpy(&n->cmbuf[addr], &data, size); +} + +static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + NvmeCtrl *n =3D (NvmeCtrl *)opaque; + + memcpy(&val, &n->cmbuf[addr], size); + return val; +} + +static const MemoryRegionOps nvme_cmb_ops =3D { + .read =3D nvme_cmb_read, + .write =3D nvme_cmb_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 2, + .max_access_size =3D 8, + }, +}; + static int nvme_init(PCIDevice *pci_dev) { NvmeCtrl *n =3D NVME(pci_dev); @@ -936,9 +976,31 @@ static int nvme_init(PCIDevice *pci_dev) NVME_CAP_SET_CSS(n->bar.cap, 1); NVME_CAP_SET_MPSMAX(n->bar.cap, 4); =20 - n->bar.vs =3D 0x00010100; + n->bar.vs =3D 0x00010200; n->bar.intmc =3D n->bar.intms =3D 0; =20 + if (n->cmb_size_mb) { + + NVME_CMBLOC_SET_BIR(n->bar.cmbloc, 2); + NVME_CMBLOC_SET_OFST(n->bar.cmbloc, 0); + + NVME_CMBSZ_SET_SQS(n->bar.cmbsz, 1); + NVME_CMBSZ_SET_CQS(n->bar.cmbsz, 0); + NVME_CMBSZ_SET_LISTS(n->bar.cmbsz, 0); + NVME_CMBSZ_SET_RDS(n->bar.cmbsz, 0); + NVME_CMBSZ_SET_WDS(n->bar.cmbsz, 0); + NVME_CMBSZ_SET_SZU(n->bar.cmbsz, 2); /* MBs */ + NVME_CMBSZ_SET_SZ(n->bar.cmbsz, n->cmb_size_mb); + + n->cmbuf =3D g_malloc0(NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); + memory_region_init_io(&n->ctrl_mem, OBJECT(n), &nvme_cmb_ops, n, + "nvme-cmb", NVME_CMBSZ_GETSIZE(n->bar.cmbsz)= ); + pci_register_bar(&n->parent_obj, NVME_CMBLOC_BIR(n->bar.cmbloc), + PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, &n->ctrl_mem); + + } + for (i =3D 0; i < n->num_namespaces; i++) { NvmeNamespace *ns =3D &n->namespaces[i]; NvmeIdNs *id_ns =3D &ns->id_ns; @@ -964,12 +1026,17 @@ static void nvme_exit(PCIDevice *pci_dev) g_free(n->namespaces); g_free(n->cq); g_free(n->sq); + if (n->cmbsz) { + memory_region_unref(&n->ctrl_mem); + } + msix_uninit_exclusive_bar(pci_dev); } =20 static Property nvme_props[] =3D { DEFINE_BLOCK_PROPERTIES(NvmeCtrl, conf), DEFINE_PROP_STRING("serial", NvmeCtrl, serial), + DEFINE_PROP_UINT32("cmb_size_mb", NvmeCtrl, cmb_size_mb, 0), DEFINE_PROP_END_OF_LIST(), }; =20 diff --git a/hw/block/nvme.h b/hw/block/nvme.h index a0d1564..b4961d2 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -14,6 +14,8 @@ typedef struct NvmeBar { uint32_t aqa; uint64_t asq; uint64_t acq; + uint32_t cmbloc; + uint32_t cmbsz; } NvmeBar; =20 enum NvmeCapShift { @@ -138,6 +140,72 @@ enum NvmeAqaMask { #define NVME_AQA_ASQS(aqa) ((aqa >> AQA_ASQS_SHIFT) & AQA_ASQS_MASK) #define NVME_AQA_ACQS(aqa) ((aqa >> AQA_ACQS_SHIFT) & AQA_ACQS_MASK) =20 +enum NvmeCmblocShift { + CMBLOC_BIR_SHIFT =3D 0, + CMBLOC_OFST_SHIFT =3D 12, +}; + +enum NvmeCmblocMask { + CMBLOC_BIR_MASK =3D 0x7, + CMBLOC_OFST_MASK =3D 0xfffff, +}; + +#define NVME_CMBLOC_BIR(cmbloc) ((cmbloc >> CMBLOC_BIR_SHIFT) & \ + CMBLOC_BIR_MASK) +#define NVME_CMBLOC_OFST(cmbloc)((cmbloc >> CMBLOC_OFST_SHIFT) & \ + CMBLOC_OFST_MASK) + +#define NVME_CMBLOC_SET_BIR(cmbloc, val) \ + (cmbloc |=3D (uint64_t)(val & CMBLOC_BIR_MASK) << CMBLOC_BIR_SHIFT) +#define NVME_CMBLOC_SET_OFST(cmbloc, val) \ + (cmbloc |=3D (uint64_t)(val & CMBLOC_OFST_MASK) << CMBLOC_OFST_SHIFT) + +enum NvmeCmbszShift { + CMBSZ_SQS_SHIFT =3D 0, + CMBSZ_CQS_SHIFT =3D 1, + CMBSZ_LISTS_SHIFT =3D 2, + CMBSZ_RDS_SHIFT =3D 3, + CMBSZ_WDS_SHIFT =3D 4, + CMBSZ_SZU_SHIFT =3D 8, + CMBSZ_SZ_SHIFT =3D 12, +}; + +enum NvmeCmbszMask { + CMBSZ_SQS_MASK =3D 0x1, + CMBSZ_CQS_MASK =3D 0x1, + CMBSZ_LISTS_MASK =3D 0x1, + CMBSZ_RDS_MASK =3D 0x1, + CMBSZ_WDS_MASK =3D 0x1, + CMBSZ_SZU_MASK =3D 0xf, + CMBSZ_SZ_MASK =3D 0xfffff, +}; + +#define NVME_CMBSZ_SQS(cmbsz) ((cmbsz >> CMBSZ_SQS_SHIFT) & CMBSZ_SQS_M= ASK) +#define NVME_CMBSZ_CQS(cmbsz) ((cmbsz >> CMBSZ_CQS_SHIFT) & CMBSZ_CQS_M= ASK) +#define NVME_CMBSZ_LISTS(cmbsz)((cmbsz >> CMBSZ_LISTS_SHIFT) & CMBSZ_LISTS= _MASK) +#define NVME_CMBSZ_RDS(cmbsz) ((cmbsz >> CMBSZ_RDS_SHIFT) & CMBSZ_RDS_M= ASK) +#define NVME_CMBSZ_WDS(cmbsz) ((cmbsz >> CMBSZ_WDS_SHIFT) & CMBSZ_WDS_M= ASK) +#define NVME_CMBSZ_SZU(cmbsz) ((cmbsz >> CMBSZ_SZU_SHIFT) & CMBSZ_SZU_M= ASK) +#define NVME_CMBSZ_SZ(cmbsz) ((cmbsz >> CMBSZ_SZ_SHIFT) & CMBSZ_SZ_MA= SK) + +#define NVME_CMBSZ_SET_SQS(cmbsz, val) \ + (cmbsz |=3D (uint64_t)(val & CMBSZ_SQS_MASK) << CMBSZ_SQS_SHIFT) +#define NVME_CMBSZ_SET_CQS(cmbsz, val) \ + (cmbsz |=3D (uint64_t)(val & CMBSZ_CQS_MASK) << CMBSZ_CQS_SHIFT) +#define NVME_CMBSZ_SET_LISTS(cmbsz, val) \ + (cmbsz |=3D (uint64_t)(val & CMBSZ_LISTS_MASK) << CMBSZ_LISTS_SHIFT) +#define NVME_CMBSZ_SET_RDS(cmbsz, val) \ + (cmbsz |=3D (uint64_t)(val & CMBSZ_RDS_MASK) << CMBSZ_RDS_SHIFT) +#define NVME_CMBSZ_SET_WDS(cmbsz, val) \ + (cmbsz |=3D (uint64_t)(val & CMBSZ_WDS_MASK) << CMBSZ_WDS_SHIFT) +#define NVME_CMBSZ_SET_SZU(cmbsz, val) \ + (cmbsz |=3D (uint64_t)(val & CMBSZ_SZU_MASK) << CMBSZ_SZU_SHIFT) +#define NVME_CMBSZ_SET_SZ(cmbsz, val) \ + (cmbsz |=3D (uint64_t)(val & CMBSZ_SZ_MASK) << CMBSZ_SZ_SHIFT) + +#define NVME_CMBSZ_GETSIZE(cmbsz) \ + (NVME_CMBSZ_SZ(cmbsz) * (1 << (12 + 4 * NVME_CMBSZ_SZU(cmbsz)))) + typedef struct NvmeCmd { uint8_t opcode; uint8_t fuse; @@ -688,6 +756,7 @@ typedef struct NvmeNamespace { typedef struct NvmeCtrl { PCIDevice parent_obj; MemoryRegion iomem; + MemoryRegion ctrl_mem; NvmeBar bar; BlockConf conf; =20 @@ -701,6 +770,10 @@ typedef struct NvmeCtrl { uint32_t num_queues; uint32_t max_q_ents; uint64_t ns_size; + uint32_t cmb_size_mb; + uint32_t cmbsz; + uint32_t cmbloc; + uint8_t *cmbuf; =20 char *serial; NvmeNamespace *namespaces; --=20 1.8.3.1