From nobody Sun May 5 11:14:26 2024 Delivered-To: importer@patchew.org Received-SPF: none (zoho.com: 80.81.252.135 is neither permitted nor denied by domain of seabios.org) client-ip=80.81.252.135; envelope-from=seabios-bounces@seabios.org; helo=mail.coreboot.org; Authentication-Results: mx.zohomail.com; dkim=fail header.i=@amazon.de; spf=none (zoho.com: 80.81.252.135 is neither permitted nor denied by domain of seabios.org) smtp.mailfrom=seabios-bounces@seabios.org Return-Path: Received: from mail.coreboot.org (mail.coreboot.org [80.81.252.135]) by mx.zohomail.com with SMTPS id 150703849854025.12788324483313; Tue, 3 Oct 2017 06:48:18 -0700 (PDT) Received: from [127.0.0.1] (helo=ra.coreboot.org) by mail.coreboot.org with esmtp (Exim 4.86_2) (envelope-from ) id 1dzNYD-00012n-PJ; Tue, 03 Oct 2017 15:47:57 +0200 Received: from smtp-fw-6002.amazon.com ([52.95.49.90]) by mail.coreboot.org with esmtps (TLSv1.2:RC4-SHA:128) (Exim 4.86_2) (envelope-from ) id 1dzNY3-00010U-1C for seabios@seabios.org; Tue, 03 Oct 2017 15:47:55 +0200 Received: from iad12-co-svc-p1-lb1-vlan3.amazon.com (HELO email-inbound-relay-1d-2c665b5d.us-east-1.amazon.com) ([10.43.8.6]) by smtp-border-fw-out-6002.iad6.amazon.com with ESMTP/TLS/DHE-RSA-AES256-SHA; 03 Oct 2017 13:47:36 +0000 Received: from u54ee758033e858cfa736.ant.amazon.com (iad7-ws-svc-lb50-vlan3.amazon.com [10.0.93.214]) by email-inbound-relay-1d-2c665b5d.us-east-1.amazon.com (8.14.7/8.14.7) with ESMTP id v93DlWsU006385 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 3 Oct 2017 13:47:34 GMT Received: from u54ee758033e858cfa736.ant.amazon.com (localhost [127.0.0.1]) by u54ee758033e858cfa736.ant.amazon.com (8.15.2/8.15.2/Debian-3) with ESMTP id v93DlVHJ009347; Tue, 3 Oct 2017 15:47:31 +0200 Received: (from jsteckli@localhost) by u54ee758033e858cfa736.ant.amazon.com (8.15.2/8.15.2/Submit) id v93DlVrq009344; Tue, 3 Oct 2017 15:47:31 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amazon.de; i=@amazon.de; q=dns/txt; s=amazon201209; t=1507038467; x=1538574467; h=from:to:cc:subject:date:message-id; bh=oI0N46wN5Pj5B6jiC3wXYw/SoBgAEXApq68jLw7Xx0A=; b=OIoJ6GN0/WhU7rojcu40EfEXJqcE/uH7x5Owh7zhuJ9tUmu0N1wu536J XhZYbH34VFrTLKxIQ7a/BDpjmbVhZFXv534PT/jEo9/X9yghA91jFZrdX e8JJ/PfaXhDfSQNlt4Rmsy3ees9o5p051bjLpnAsKyfXqmF91HwcluuC7 Y=; X-IronPort-AV: E=Sophos;i="5.42,474,1500940800"; d="scan'208";a="309028381" From: Julian Stecklina To: seabios@seabios.org Date: Tue, 3 Oct 2017 15:47:17 +0200 Message-Id: <1507038437-9288-1-git-send-email-jsteckli@amazon.de> X-Mailer: git-send-email 2.7.4 X-Spam-Score: -11.2 (-----------) Subject: [SeaBIOS] [PATCH] nvme: fix out of memory behavior X-BeenThere: seabios@seabios.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: SeaBIOS mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: seabios-bounces@seabios.org Sender: "SeaBIOS" X-Duff: Orig. Duff, Duff Lite, Duff Dry, Duff Dark, Raspberry Duff, Lady Duff, Red Duff, Tartar Control Duff X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" If the allocation of I/O queues ran out of memory, the code would fail to d= etect that and happily use these queues at address zero. For me this happens for systems with more than 7 NVMe controllers. Fix the out of memory handling to gracefully handle this case. Signed-off-by: Julian Stecklina --- src/hw/nvme.c | 147 ++++++++++++++++++++++++++++++++++++++++++++----------= ---- 1 file changed, 111 insertions(+), 36 deletions(-) diff --git a/src/hw/nvme.c b/src/hw/nvme.c index 93c25f5..946487f 100644 --- a/src/hw/nvme.c +++ b/src/hw/nvme.c @@ -38,29 +38,43 @@ nvme_init_queue_common(struct nvme_ctrl *ctrl, struct n= vme_queue *q, u16 q_idx, q->mask =3D length - 1; } =20 -static void +static int nvme_init_sq(struct nvme_ctrl *ctrl, struct nvme_sq *sq, u16 q_idx, u16 le= ngth, struct nvme_cq *cq) { nvme_init_queue_common(ctrl, &sq->common, q_idx, length); sq->sqe =3D zalloc_page_aligned(&ZoneHigh, sizeof(*sq->sqe) * length); + + if (!sq->sqe) { + warn_noalloc(); + return -1; + } + dprintf(3, "sq %p q_idx %u sqe %p\n", sq, q_idx, sq->sqe); sq->cq =3D cq; sq->head =3D 0; sq->tail =3D 0; + + return 0; } =20 -static void +static int nvme_init_cq(struct nvme_ctrl *ctrl, struct nvme_cq *cq, u16 q_idx, u16 le= ngth) { nvme_init_queue_common(ctrl, &cq->common, q_idx, length); cq->cqe =3D zalloc_page_aligned(&ZoneHigh, sizeof(*cq->cqe) * length); + if (!cq->cqe) { + warn_noalloc(); + return -1; + } =20 cq->head =3D 0; =20 /* All CQE phase bits are initialized to zero. This means initially we= wait for the host controller to set these to 1. */ cq->phase =3D 1; + + return 0; } =20 static int @@ -76,7 +90,6 @@ nvme_is_cqe_success(struct nvme_cqe const *cqe) return ((cqe->status >> 1) & 0xFF) =3D=3D 0; } =20 - static struct nvme_cqe nvme_error_cqe(void) { @@ -278,22 +291,44 @@ nvme_probe_ns(struct nvme_ctrl *ctrl, struct nvme_nam= espace *ns, u32 ns_id) dprintf(3, "%s", desc); boot_add_hd(&ns->drive, desc, bootprio_find_pci_device(ctrl->pci)); =20 - free_buffer: +free_buffer: free (id); - } +} + + +/* Release memory allocated for a completion queue */ +static void +nvme_destroy_cq(struct nvme_cq *cq) +{ + free(cq->cqe); + cq->cqe =3D NULL; +} + +/* Release memory allocated for a submission queue */ +static void +nvme_destroy_sq(struct nvme_sq *sq) +{ + free(sq->sqe); + sq->sqe =3D NULL; +} =20 /* Returns 0 on success. */ static int nvme_create_io_cq(struct nvme_ctrl *ctrl, struct nvme_cq *cq, u16 q_idx) { + int rc; struct nvme_sqe *cmd_create_cq; =20 - nvme_init_cq(ctrl, cq, q_idx, NVME_PAGE_SIZE / sizeof(struct nvme_cqe)= ); + rc =3D nvme_init_cq(ctrl, cq, q_idx, NVME_PAGE_SIZE / sizeof(struct nv= me_cqe)); + if (rc) { + goto err; + } + cmd_create_cq =3D nvme_get_next_sqe(&ctrl->admin_sq, NVME_SQE_OPC_ADMIN_CREATE_IO_CQ, NUL= L, cq->cqe); if (!cmd_create_cq) { - return -1; + goto err_destroy_cq; } =20 cmd_create_cq->dword[10] =3D (cq->common.mask << 16) | (q_idx >> 1); @@ -307,24 +342,34 @@ nvme_create_io_cq(struct nvme_ctrl *ctrl, struct nvme= _cq *cq, u16 q_idx) dprintf(2, "create io cq failed: %08x %08x %08x %08x\n", cqe.dword[0], cqe.dword[1], cqe.dword[2], cqe.dword[3]); =20 - return -1; + goto err_destroy_cq; } =20 return 0; + +err_destroy_cq: + nvme_destroy_cq(cq); +err: + return -1; } =20 /* Returns 0 on success. */ static int nvme_create_io_sq(struct nvme_ctrl *ctrl, struct nvme_sq *sq, u16 q_idx, s= truct nvme_cq *cq) { + int rc; struct nvme_sqe *cmd_create_sq; =20 - nvme_init_sq(ctrl, sq, q_idx, NVME_PAGE_SIZE / sizeof(struct nvme_cqe)= , cq); + rc =3D nvme_init_sq(ctrl, sq, q_idx, NVME_PAGE_SIZE / sizeof(struct nv= me_cqe), cq); + if (rc) { + goto err; + } + cmd_create_sq =3D nvme_get_next_sqe(&ctrl->admin_sq, NVME_SQE_OPC_ADMIN_CREATE_IO_SQ, NUL= L, sq->sqe); if (!cmd_create_sq) { - return -1; + goto err_destroy_sq; } =20 cmd_create_sq->dword[10] =3D (sq->common.mask << 16) | (q_idx >> 1); @@ -339,10 +384,15 @@ nvme_create_io_sq(struct nvme_ctrl *ctrl, struct nvme= _sq *sq, u16 q_idx, struct if (!nvme_is_cqe_success(&cqe)) { dprintf(2, "create io sq failed: %08x %08x %08x %08x\n", cqe.dword[0], cqe.dword[1], cqe.dword[2], cqe.dword[3]); - return -1; + goto err_destroy_sq; } =20 return 0; + +err_destroy_sq: + nvme_destroy_sq(sq); +err: + return -1; } =20 /* Reads count sectors into buf. Returns DISK_RET_*. The buffer cannot cro= ss @@ -384,17 +434,28 @@ nvme_io_readwrite(struct nvme_namespace *ns, u64 lba,= char *buf, u16 count, return DISK_RET_SUCCESS; } =20 - static int nvme_create_io_queues(struct nvme_ctrl *ctrl) { if (nvme_create_io_cq(ctrl, &ctrl->io_cq, 3)) - return -1; + goto err; =20 if (nvme_create_io_sq(ctrl, &ctrl->io_sq, 2, &ctrl->io_cq)) - return -1; + goto err_free_cq; =20 return 0; + + err_free_cq: + nvme_destroy_cq(&ctrl->io_cq); + err: + return -1; +} + +static void +nvme_destroy_io_queues(struct nvme_ctrl *ctrl) +{ + nvme_destroy_sq(&ctrl->io_sq); + nvme_destroy_cq(&ctrl->io_cq); } =20 /* Waits for CSTS.RDY to match rdy. Returns 0 on success. */ @@ -426,6 +487,8 @@ nvme_wait_csts_rdy(struct nvme_ctrl *ctrl, unsigned rdy) static int nvme_controller_enable(struct nvme_ctrl *ctrl) { + int rc; + pci_enable_busmaster(ctrl->pci); =20 /* Turn the controller off. */ @@ -437,18 +500,21 @@ nvme_controller_enable(struct nvme_ctrl *ctrl) =20 ctrl->doorbell_stride =3D 4U << ((ctrl->reg->cap >> 32) & 0xF); =20 - nvme_init_cq(ctrl, &ctrl->admin_cq, 1, - NVME_PAGE_SIZE / sizeof(struct nvme_cqe)); + rc =3D nvme_init_cq(ctrl, &ctrl->admin_cq, 1, + NVME_PAGE_SIZE / sizeof(struct nvme_cqe)); + if (rc) { + return -1; + } =20 - nvme_init_sq(ctrl, &ctrl->admin_sq, 0, - NVME_PAGE_SIZE / sizeof(struct nvme_sqe), &ctrl->admin_cq= ); + rc =3D nvme_init_sq(ctrl, &ctrl->admin_sq, 0, + NVME_PAGE_SIZE / sizeof(struct nvme_sqe), &ctrl->adm= in_cq); + if (rc) { + goto err_destroy_admin_cq; + } =20 ctrl->reg->aqa =3D ctrl->admin_cq.common.mask << 16 | ctrl->admin_sq.common.mask; =20 - /* Create the admin queue pair */ - if (!ctrl->admin_sq.sqe || !ctrl->admin_cq.cqe) goto out_of_memory; - ctrl->reg->asq =3D (u32)ctrl->admin_sq.sqe; ctrl->reg->acq =3D (u32)ctrl->admin_cq.cqe; =20 @@ -460,8 +526,9 @@ nvme_controller_enable(struct nvme_ctrl *ctrl) =20 if (nvme_wait_csts_rdy(ctrl, 1)) { dprintf(2, "NVMe fatal error while enabling controller\n"); - goto failed; + goto err_destroy_admin_sq; } + /* The admin queue is set up and the controller is ready. Let's figure= out what namespaces we have. */ =20 @@ -469,10 +536,9 @@ nvme_controller_enable(struct nvme_ctrl *ctrl) =20 if (!identify) { dprintf(2, "NVMe couldn't identify controller.\n"); - goto failed; + goto err_destroy_admin_sq; } =20 - /* TODO Print model/serial info. */ dprintf(3, "NVMe has %u namespace%s.\n", identify->nn, (identify->nn =3D=3D 1) ? "" : "s"); =20 @@ -482,11 +548,14 @@ nvme_controller_enable(struct nvme_ctrl *ctrl) if ((ctrl->ns_count =3D=3D 0) || nvme_create_io_queues(ctrl)) { /* No point to continue, if the controller says it doesn't have namespaces or we couldn't create I/O queues. */ - goto failed; + goto err_destroy_admin_sq; } =20 ctrl->ns =3D malloc_fseg(sizeof(*ctrl->ns) * ctrl->ns_count); - if (!ctrl->ns) goto out_of_memory; + if (!ctrl->ns) { + warn_noalloc(); + goto err_destroy_ioq; + } memset(ctrl->ns, 0, sizeof(*ctrl->ns) * ctrl->ns_count); =20 /* Populate namespace IDs */ @@ -498,12 +567,12 @@ nvme_controller_enable(struct nvme_ctrl *ctrl) dprintf(3, "NVMe initialization complete!\n"); return 0; =20 - out_of_memory: - warn_noalloc(); - failed: - free(ctrl->admin_sq.sqe); - free(ctrl->admin_cq.cqe); - free(ctrl->ns); + err_destroy_ioq: + nvme_destroy_io_queues(ctrl); + err_destroy_admin_sq: + nvme_destroy_sq(&ctrl->admin_sq); + err_destroy_admin_cq: + nvme_destroy_cq(&ctrl->admin_cq); return -1; } =20 @@ -524,13 +593,13 @@ nvme_controller_setup(void *opaque) =20 if (~reg->cap & NVME_CAP_CSS_NVME) { dprintf(3, "Controller doesn't speak NVMe command set. Skipping.\n= "); - return; + goto err; } =20 struct nvme_ctrl *ctrl =3D malloc_high(sizeof(*ctrl)); if (!ctrl) { warn_noalloc(); - return; + goto err; } =20 memset(ctrl, 0, sizeof(*ctrl)); @@ -539,9 +608,15 @@ nvme_controller_setup(void *opaque) ctrl->pci =3D pci; =20 if (nvme_controller_enable(ctrl)) { - /* Initialization failed */ - free(ctrl); + goto err_free_ctrl; } + + return; + + err_free_ctrl: + free(ctrl); + err: + dprintf(2, "Failed to enable NVMe controller.\n"); } =20 // Locate and init NVMe controllers --=20 2.7.4 _______________________________________________ SeaBIOS mailing list SeaBIOS@seabios.org https://mail.coreboot.org/mailman/listinfo/seabios