root/usr/src/test/nvme-tests/tests/libnvme/pev.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2024 Oxide Computer Company
 */

/*
 * Test various aspects of reading the persistent event log from a device and
 * verify that we properly handle certain error conditions. In particular, we
 * want to verify the following:
 *
 * 1) Issuing a reset context while it's already reset is fine.
 * 2) Issuing a read without establish fails
 * 3) Issuing an establish context with one fails
 * 4) Otherwise basic read / establish / release works
 *
 * Because this requires device specific behavior is it not part of the default
 * non-destructive run.
 */

#include <err.h>

#include "libnvme_test_common.h"

static bool
pev_action(nvme_t *nvme, nvme_ctrl_t *ctrl, nvme_pev_log_lsp_t pev, bool exp,
    const char *desc)
{
        nvme_log_disc_t *disc;
        nvme_log_req_t *req;
        uint8_t *buf;
        size_t buflen = 512;
        bool ret = true, lret;

        if ((buf = calloc(buflen, sizeof (uint8_t))) == NULL) {
                err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to allocate "
                    "%zu bytes for internal buffer", buflen);
        }

        if (!nvme_log_req_init_by_name(ctrl, "pev", 0, &disc, &req)) {
                libnvme_test_ctrl_fatal(ctrl, "%s: failed to initialize "
                    "persistent event log request", desc);
        }

        if (!nvme_log_req_set_output(req, buf, buflen)) {
                libnvme_test_ctrl_warn(ctrl, "%s: failed to set output buffer",
                    desc);
                ret = false;
                goto done;
        }

        if (!nvme_log_req_set_lsp(req, pev)) {
                libnvme_test_ctrl_warn(ctrl, "%s: failed to set lsp to 0x%x",
                    desc, pev);
                ret = false;
                goto done;
        }

        lret = nvme_log_req_exec(req);
        if (exp && !lret) {
                libnvme_test_ctrl_warn(ctrl, "%s: log request failed, but "
                    "expected success", desc);
                ret = false;
        } else if (!exp && lret) {
                warnx("TEST FAILED: %s: log request succeeded, but expected "
                    "failure", desc);
                ret = false;
        } else if (!lret) {
                nvme_err_t err;
                uint32_t sct, sc;

                err = nvme_ctrl_err(ctrl);
                nvme_ctrl_deverr(ctrl, &sct, &sc);

                if (err != NVME_ERR_CONTROLLER) {
                        warnx("TEST FAILED: %s: found controller error %s "
                            "(0x%x), but expected %s (0x%x)", desc,
                            nvme_errtostr(nvme, err), err,
                            nvme_errtostr(nvme, NVME_ERR_CONTROLLER),
                            NVME_ERR_CONTROLLER);
                        ret = false;
                } else if (sct != NVME_CQE_SCT_GENERIC) {
                        warnx("TEST FAILED: %s: found device sct %s (0x%x), "
                            "but expected %s (0x%x)", desc,
                            nvme_scttostr(ctrl, sct), sct,
                            nvme_scttostr(ctrl, NVME_CQE_SCT_GENERIC),
                            NVME_CQE_SCT_GENERIC);
                        ret = false;
                } else if (sc != NVME_CQE_SC_GEN_CMD_SEQ_ERR) {
                        warnx("TEST FAILED: %s: found device sc %s (0x%x), "
                            "but expected %s (0x%x)", desc,
                            nvme_sctostr(ctrl, NVME_CSI_NVM, sct, sc), sc,
                            nvme_sctostr(ctrl, NVME_CSI_NVM, sct,
                            NVME_CQE_SC_GEN_CMD_SEQ_ERR),
                            NVME_CQE_SC_GEN_CMD_SEQ_ERR);
                        ret = false;
                }
        } else if (pev == NVME_PEV_LSP_READ ||
            pev == NVME_PEV_LSP_EST_CTX_READ) {
                if (*buf != NVME_LOGPAGE_PEV) {
                        warnx("TEST FAILED: %s: returned data does not have "
                            "correct LID in byte 0, found 0x%x, expected 0x%x",
                            desc, *buf, NVME_LOGPAGE_PEV);
                        ret = false;
                }
        }

        if (ret) {
                (void) printf("TEST PASSED: %s\n", desc);
        }
done:
        nvme_log_disc_free(disc);
        nvme_log_req_fini(req);
        free(buf);
        return (ret);
}

int
main(void)
{
        int ret = EXIT_SUCCESS;
        nvme_t *nvme;
        nvme_ctrl_t *ctrl;

        libnvme_test_init(&nvme, &ctrl);
        if (!pev_action(nvme, ctrl, NVME_PEV_LSP_REL_CTX, true,
            "initial release context works")) {
                ret = EXIT_FAILURE;
        }

        if (!pev_action(nvme, ctrl, NVME_PEV_LSP_REL_CTX, true,
            "second release context works")) {
                ret = EXIT_FAILURE;
        }

        if (!pev_action(nvme, ctrl, NVME_PEV_LSP_READ, false,
            "read without context fails")) {
                ret = EXIT_FAILURE;
        }

        if (!pev_action(nvme, ctrl, NVME_PEV_LSP_EST_CTX_READ, true,
            "establish context and read works")) {
                ret = EXIT_FAILURE;
        }

        if (!pev_action(nvme, ctrl, NVME_PEV_LSP_EST_CTX_READ, false,
            "second establish context and read fails")) {
                ret = EXIT_FAILURE;
        }

        if (!pev_action(nvme, ctrl, NVME_PEV_LSP_READ, true,
            "read with context works")) {
                ret = EXIT_FAILURE;
        }

        if (!pev_action(nvme, ctrl, NVME_PEV_LSP_REL_CTX, true,
            "release after read works")) {
                ret = EXIT_FAILURE;
        }

        if (ret == EXIT_SUCCESS) {
                (void) printf("All tests passed successfully\n");
        }

        return (ret);
}