diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 19:22:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 19:22:29 +0000 |
commit | 068a45420f2c98887e220b45e946cc7074da550e (patch) | |
tree | c5b54e8b4b235232b057a9c534d9a16d2208463d /test/ioctl/features.c | |
parent | Initial commit. (diff) | |
download | libnvme-068a45420f2c98887e220b45e946cc7074da550e.tar.xz libnvme-068a45420f2c98887e220b45e946cc7074da550e.zip |
Adding upstream version 1.8.upstream/1.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/ioctl/features.c')
-rw-r--r-- | test/ioctl/features.c | 1604 |
1 files changed, 1604 insertions, 0 deletions
diff --git a/test/ioctl/features.c b/test/ioctl/features.c new file mode 100644 index 0000000..7386497 --- /dev/null +++ b/test/ioctl/features.c @@ -0,0 +1,1604 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include <libnvme.h> + +#include <errno.h> +#include <inttypes.h> + +#include "mock.h" +#include "util.h" + +#define TEST_FD 0xFD +#define TEST_TIMEOUT 1234 +#define TEST_NSID 0x89ABCDEF +#define TEST_CDW11 0x11111111 +#define TEST_CDW12 0x12121212 +#define TEST_CDW13 0x13131313 +#define TEST_CDW15 0x15151515 +#define TEST_UUIDX 0b1001110 +#define TEST_FID 0xFE +#define TEST_RESULT 0x12345678 +#define TEST_SEL NVME_GET_FEATURES_SEL_SAVED +#define TEST_SC NVME_SC_INVALID_FIELD + +static void test_set_features(void) +{ + uint32_t result = 0; + uint8_t data[256]; + struct nvme_set_features_args args = { + .result = &result, + .data = data, + .args_size = sizeof(args), + .fd = TEST_FD, + .timeout = TEST_TIMEOUT, + .nsid = TEST_NSID, + .cdw11 = TEST_CDW11, + .cdw12 = TEST_CDW12, + .cdw13 = TEST_CDW13, + .cdw15 = TEST_CDW15, + .data_len = sizeof(data), + .save = true, + .uuidx = TEST_UUIDX, + .fid = TEST_FID, + }; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .in_data = data, + .data_len = sizeof(data), + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | TEST_FID, + .cdw11 = TEST_CDW11, + .cdw12 = TEST_CDW12, + .cdw13 = TEST_CDW13, + .cdw14 = TEST_UUIDX, + .cdw15 = TEST_CDW15, + .timeout_ms = TEST_TIMEOUT, + .result = TEST_RESULT, + }; + int err; + + arbitrary(data, sizeof(data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features(&args); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_features(void) +{ + uint32_t result = 0; + uint8_t data[256], get_data[sizeof(data)] = {}; + struct nvme_get_features_args args = { + .result = &result, + .data = get_data, + .args_size = sizeof(args), + .fd = TEST_FD, + .timeout = TEST_TIMEOUT, + .nsid = TEST_NSID, + .sel = TEST_SEL, + .cdw11 = TEST_CDW11, + .data_len = sizeof(data), + .fid = TEST_FID, + .uuidx = TEST_UUIDX, + }; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .nsid = TEST_NSID, + .data_len = sizeof(data), + .cdw10 = TEST_SEL << 8 | TEST_FID, + .cdw11 = TEST_CDW11, + .cdw14 = TEST_UUIDX, + .timeout_ms = TEST_TIMEOUT, + .out_data = data, + .result = TEST_RESULT, + }; + int err; + + arbitrary(data, sizeof(data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features(&args); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); + cmp(get_data, data, sizeof(data), "incorrect data"); +} + +static void test_set_features_data(void) +{ + uint8_t data[128]; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .in_data = data, + .data_len = sizeof(data), + .cdw10 = TEST_FID, + .cdw11 = TEST_CDW11, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(data, sizeof(data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_data( + TEST_FD, TEST_FID, TEST_NSID, TEST_CDW11, false, + sizeof(data), data, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_features_data(void) +{ + uint8_t data[128], get_data[sizeof(data)] = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .nsid = TEST_NSID, + .data_len = sizeof(data), + .cdw10 = NVME_GET_FEATURES_SEL_CURRENT << 8 | TEST_FID, + .out_data = data, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(data, sizeof(data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_data( + TEST_FD, TEST_FID, TEST_NSID, sizeof(data), get_data, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); + cmp(get_data, data, sizeof(data), "incorrect data"); +} + +static void test_set_features_simple(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | TEST_FID, + .cdw11 = TEST_CDW11, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_simple( + TEST_FD, TEST_FID, TEST_NSID, TEST_CDW11, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_features_simple(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .nsid = TEST_NSID, + .cdw10 = NVME_GET_FEATURES_SEL_CURRENT << 8 | TEST_FID, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_simple(TEST_FD, TEST_FID, TEST_NSID, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_arbitration(void) +{ + uint8_t HPW = 0xAA, MPW = 0xBB, LPW = 0xCC, AB = 0b111; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_ARBITRATION, + .cdw11 = (uint32_t)HPW << 24 | MPW << 16 | LPW << 8 | AB, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_arbitration( + TEST_FD, AB, LPW, MPW, HPW, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_arbitration(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_ARBITRATION, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_arbitration(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_power_mgmt(void) +{ + uint8_t PS = 0b10101, WH = 0b101; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_POWER_MGMT, + .cdw11 = WH << 5 | PS, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_power_mgmt(TEST_FD, PS, WH, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_power_mgmt(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_POWER_MGMT, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_power_mgmt(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_lba_range(void) +{ + uint8_t NUM = 64; + struct nvme_lba_range_type range_types; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .in_data = &range_types, + .data_len = sizeof(range_types), + .cdw10 = NVME_FEAT_FID_LBA_RANGE, + .cdw11 = NUM - 1, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(&range_types, sizeof(range_types)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_lba_range( + TEST_FD, TEST_NSID, NUM, false, &range_types, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_lba_range(void) +{ + struct nvme_lba_range_type range_types, get_range_types = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .nsid = TEST_NSID, + .data_len = sizeof(range_types), + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_LBA_RANGE, + .out_data = &range_types, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(&range_types, sizeof(range_types)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_lba_range2( + TEST_FD, TEST_SEL, TEST_NSID, &get_range_types, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); + cmp(&get_range_types, &range_types, sizeof(range_types), + "incorrect LBA range types"); +} + +static void test_set_temp_thresh(void) +{ + uint16_t TMPTH = 0xFEDC; + uint8_t TMPSEL = 0x8; + enum nvme_feat_tmpthresh_thsel THSEL = + NVME_FEATURE_TEMPTHRESH_THSEL_UNDER; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_TEMP_THRESH, + .cdw11 = THSEL << 20 | TMPSEL << 16 | TMPTH, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_temp_thresh( + TEST_FD, TMPTH, TMPSEL, THSEL, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_temp_thresh(void) +{ + /* + * nvme_get_features_temp_thresh() doesn't support + * specifying TMPSEL and THSEL + */ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_TEMP_THRESH, + .cdw11 = NVME_FEATURE_TEMPTHRESH_THSEL_OVER << 20, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_temp_thresh(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_err_recovery(void) +{ + uint16_t TLER = 0xCDEF; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .cdw10 = NVME_FEAT_FID_ERR_RECOVERY, + .cdw11 = 1 << 16 /* DULBE */ + | TLER, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_err_recovery( + TEST_FD, TEST_NSID, TLER, true, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_err_recovery(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .nsid = TEST_NSID, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_ERR_RECOVERY, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_err_recovery2( + TEST_FD, TEST_SEL, TEST_NSID, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_volatile_wc(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_VOLATILE_WC, + .cdw11 = 1 << 0, /* WCE */ + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_volatile_wc(TEST_FD, true, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_volatile_wc(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 + | NVME_FEAT_FID_VOLATILE_WC, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_volatile_wc(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_num_queues(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_NUM_QUEUES, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_num_queues(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_irq_coalesce(void) +{ + uint8_t THR = 0xAB, TIME = 0xCD; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_IRQ_COALESCE, + .cdw11 = TIME << 8 | THR, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_irq_coalesce( + TEST_FD, THR, TIME, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_irq_coalesce(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_IRQ_COALESCE, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_irq_coalesce(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_irq_config(void) +{ + uint16_t IV = 0x1234; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_IRQ_CONFIG, + .cdw11 = 1 << 16 /* CD */ + | IV, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_irq_config(TEST_FD, IV, true, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_irq_config(void) +{ + uint16_t IV = 0x5678; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_IRQ_CONFIG, + .cdw11 = IV, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_irq_config(TEST_FD, TEST_SEL, IV, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_write_atomic(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_WRITE_ATOMIC, + .cdw11 = 1 << 0, /* DN */ + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_write_atomic(TEST_FD, true, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_write_atomic(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_WRITE_ATOMIC, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_write_atomic(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_async_event(void) +{ + uint32_t EVENTS = 0x87654321; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_ASYNC_EVENT, + .cdw11 = EVENTS, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_async_event(TEST_FD, EVENTS, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_async_event(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_ASYNC_EVENT, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_async_event(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_auto_pst(void) +{ + struct nvme_feat_auto_pst apst; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .in_data = &apst, + .data_len = sizeof(apst), + .cdw10 = NVME_FEAT_FID_AUTO_PST, + .cdw11 = 1 << 0, /* APSTE */ + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(&apst, sizeof(apst)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_auto_pst(TEST_FD, true, false, &apst, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_auto_pst(void) +{ + struct nvme_feat_auto_pst apst, get_apst = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .data_len = sizeof(apst), + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_AUTO_PST, + .out_data = &apst, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(&apst, sizeof(apst)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_auto_pst(TEST_FD, TEST_SEL, &get_apst, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); + cmp(&get_apst, &apst, sizeof(apst), "incorrect apst"); +} + +static void test_get_host_mem_buf(void) +{ + struct nvme_host_mem_buf_attrs attrs, get_attrs = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .data_len = sizeof(attrs), + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HOST_MEM_BUF, + .out_data = &attrs, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(&attrs, sizeof(attrs)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_host_mem_buf2( + TEST_FD, TEST_SEL, &get_attrs, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); + cmp(&get_attrs, &attrs, sizeof(attrs), "incorrect attrs"); +} + +static void test_set_timestamp(void) +{ + struct nvme_timestamp ts = {.timestamp = {1, 2, 3, 4, 5, 6}}; + uint64_t timestamp = ts.timestamp[0] + | (uint64_t) ts.timestamp[1] << 8 + | (uint64_t) ts.timestamp[2] << 16 + | (uint64_t) ts.timestamp[3] << 24 + | (uint64_t) ts.timestamp[4] << 32 + | (uint64_t) ts.timestamp[5] << 40; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .in_data = &ts, + .data_len = sizeof(ts), + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_TIMESTAMP, + }; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_timestamp(TEST_FD, true, timestamp); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); +} + +static void test_get_timestamp(void) +{ + struct nvme_timestamp ts, get_ts = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .data_len = sizeof(ts), + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_TIMESTAMP, + .out_data = &ts, + }; + int err; + + arbitrary(&ts, sizeof(ts)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_timestamp(TEST_FD, TEST_SEL, &get_ts); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + cmp(&get_ts, &ts, sizeof(ts), "incorrect timestamp"); +} + +static void test_get_kato(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_KATO, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_kato(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_hctm(void) +{ + uint16_t TMT2 = 0x4321, TMT1 = 0x8765; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_HCTM, + .cdw11 = (uint32_t)TMT1 << 16 | TMT2, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_hctm(TEST_FD, TMT2, TMT1, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_hctm(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HCTM, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_hctm(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_nopsc(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_NOPSC, + .cdw11 = 1 << 0 /* NOPPME */, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_nopsc(TEST_FD, true, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_nopsc(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_NOPSC, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_nopsc(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_rrl(void) +{ + uint8_t RRL = 0xA; + uint16_t NVMSETID = 0x1234; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_RRL, + .cdw11 = NVMSETID, + .cdw12 = RRL, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_rrl(TEST_FD, RRL, NVMSETID, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_rrl(void) +{ + /* nvme_get_features_rrl() doesn't support specifying the NVMSETID */ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_RRL, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_rrl(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_plm_config(void) +{ + uint16_t NVMSETID = 0xFEDC; + struct nvme_plm_config config; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .in_data = &config, + .data_len = sizeof(config), + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_PLM_CONFIG, + .cdw11 = NVMSETID, + .cdw12 = 1 << 0 /* Predictable Latency Enable */, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(&config, sizeof(config)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_plm_config( + TEST_FD, true, NVMSETID, true, &config, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_plm_config(void) +{ + uint16_t NVMSETID = 0xABCD; + struct nvme_plm_config config, get_config = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .data_len = sizeof(config), + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_PLM_CONFIG, + .cdw11 = NVMSETID, + .out_data = &config, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(&config, sizeof(config)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_plm_config( + TEST_FD, TEST_SEL, NVMSETID, &get_config, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); + cmp(&get_config, &config, sizeof(config), "incorrect PLM config"); +} + +static void test_set_plm_window(void) +{ + enum nvme_feat_plm_window_select SEL = NVME_FEATURE_PLM_NDWIN; + uint16_t NVMSETID = 0x4321; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_PLM_WINDOW, + .cdw11 = NVMSETID, + .cdw12 = SEL, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_plm_window( + TEST_FD, SEL, NVMSETID, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_plm_window(void) +{ + uint16_t NVMSETID = 0x8765; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_PLM_WINDOW, + .cdw11 = NVMSETID, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_plm_window( + TEST_FD, TEST_SEL, NVMSETID, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_lba_sts_interval(void) +{ + uint16_t LSIRI = 0x1234, LSIPI = 0x5678; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_LBA_STS_INTERVAL, + .cdw11 = LSIPI << 16 | LSIRI, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_lba_sts_interval( + TEST_FD, LSIRI, LSIPI, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_lba_sts_interval(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_LBA_STS_INTERVAL, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_lba_sts_interval(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_host_behavior(void) +{ + /* nvme_set_features_host_behavior() ignores SAVE */ + struct nvme_feat_host_behavior behavior; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .in_data = &behavior, + .data_len = sizeof(behavior), + .cdw10 = NVME_FEAT_FID_HOST_BEHAVIOR, + }; + int err; + + arbitrary(&behavior, sizeof(behavior)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_host_behavior(TEST_FD, true, &behavior); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); +} + +static void test_get_host_behavior(void) +{ + struct nvme_feat_host_behavior behavior, get_behavior = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .data_len = sizeof(behavior), + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HOST_BEHAVIOR, + .out_data = &behavior, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + arbitrary(&behavior, sizeof(behavior)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_host_behavior( + TEST_FD, TEST_SEL, &get_behavior, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); + cmp(&get_behavior, &behavior, sizeof(behavior), "incorrect behavior"); +} + +static void test_set_sanitize(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_SANITIZE, + .cdw11 = 1 << 0, /* NODRM */ + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_sanitize(TEST_FD, true, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_sanitize(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_SANITIZE, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_sanitize(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_endurance_evt_cfg(void) +{ + uint16_t ENDGID = 0x9876; + uint8_t EGWARN = 0xCD; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_ENDURANCE_EVT_CFG, + .cdw11 = EGWARN << 16 | ENDGID, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_endurance_evt_cfg( + TEST_FD, ENDGID, EGWARN, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_endurance_event_cfg(void) +{ + uint16_t ENDGID = 0x6789; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_ENDURANCE_EVT_CFG, + .cdw11 = ENDGID, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_endurance_event_cfg( + TEST_FD, TEST_SEL, ENDGID, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_iocs_profile(void) +{ + uint16_t IOCSI = 0b101100111; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_IOCS_PROFILE, + .cdw11 = IOCSI, + }; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_iocs_profile(TEST_FD, IOCSI, false); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); +} + +static void test_get_iocs_profile(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_IOCS_PROFILE, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_iocs_profile(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_sw_progress(void) +{ + uint8_t PBSLC = 0xBA; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_SW_PROGRESS, + .cdw11 = PBSLC, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_sw_progress(TEST_FD, PBSLC, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_sw_progress(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_SW_PROGRESS, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_sw_progress(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_host_id(void) +{ + uint8_t hostid[8]; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .in_data = hostid, + .data_len = sizeof(hostid), + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_HOST_ID, + .result = TEST_RESULT, + }; + int err; + + arbitrary(hostid, sizeof(hostid)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_host_id(TEST_FD, false, true, hostid); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); +} + +static void test_set_host_id_extended(void) +{ + uint8_t hostid[16]; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .in_data = hostid, + .data_len = sizeof(hostid), + .cdw10 = NVME_FEAT_FID_HOST_ID, + .cdw11 = 1 << 0, /* EXHID */ + .result = TEST_RESULT, + }; + int err; + + arbitrary(hostid, sizeof(hostid)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_host_id(TEST_FD, true, false, hostid); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); +} + +static void test_get_host_id(void) +{ + uint8_t hostid[8], get_hostid[sizeof(hostid)] = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .data_len = sizeof(hostid), + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HOST_ID, + .out_data = hostid, + .result = TEST_RESULT, + }; + int err; + + arbitrary(hostid, sizeof(hostid)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_host_id( + TEST_FD, TEST_SEL, false, sizeof(hostid), get_hostid); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + cmp(get_hostid, hostid, sizeof(hostid), "incorrect host identifier"); +} + +static void test_get_host_id_extended(void) +{ + uint8_t hostid[16], get_hostid[sizeof(hostid)] = {}; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .data_len = sizeof(hostid), + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HOST_ID, + .cdw11 = 1 << 0, /* EXHID */ + .out_data = hostid, + .result = TEST_RESULT, + }; + int err; + + arbitrary(hostid, sizeof(hostid)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_host_id( + TEST_FD, TEST_SEL, true, sizeof(hostid), get_hostid); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + cmp(get_hostid, hostid, sizeof(hostid), "incorrect host identifier"); +} + +static void test_set_resv_mask(void) +{ + uint32_t MASK = 0x23456789; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .cdw10 = (uint32_t)1 << 31 /* SAVE */ + | NVME_FEAT_FID_RESV_MASK, + .cdw11 = MASK, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_resv_mask2( + TEST_FD, TEST_NSID, MASK, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_resv_mask(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .nsid = TEST_NSID, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_RESV_MASK, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_resv_mask2( + TEST_FD, TEST_SEL, TEST_NSID, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_resv_persist(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .cdw10 = NVME_FEAT_FID_RESV_PERSIST, + .cdw11 = 1 << 0, /* PTPL */ + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_resv_persist2( + TEST_FD, TEST_NSID, true, false, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_resv_persist(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .nsid = TEST_NSID, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_RESV_PERSIST, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_resv_persist2( + TEST_FD, TEST_SEL, TEST_NSID, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_write_protect(void) +{ + /* nvme_set_features_write_protect() ignores SAVE */ + enum nvme_feat_nswpcfg_state STATE = + NVME_FEAT_NS_WRITE_PROTECT_PERMANENT; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .cdw10 = NVME_FEAT_FID_WRITE_PROTECT, + .cdw11 = STATE, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_write_protect2( + TEST_FD, TEST_NSID, STATE, true, &result); + end_mock_cmds(); + check(err == 0, "set features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_write_protect(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .nsid = TEST_NSID, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_WRITE_PROTECT, + .result = TEST_RESULT, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_write_protect( + TEST_FD, TEST_NSID, TEST_SEL, &result); + end_mock_cmds(); + check(err == 0, "get features returned error %d, errno %m", err); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +/* + * All set_features functions tail-call nvme_set_features(), + * so testing errors in any of them will do + */ + +static void test_set_status_code_error(void) +{ + uint32_t EVENTS = 0x12345678; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .cdw10 = NVME_FEAT_FID_ASYNC_EVENT, + .cdw11 = EVENTS, + .result = TEST_RESULT, + .err = TEST_SC, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_async_event(TEST_FD, EVENTS, false, &result); + end_mock_cmds(); + check(err == TEST_SC, "got error %d, expected %d", err, TEST_SC); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_set_kernel_error(void) +{ + uint32_t MASK = 0x87654321; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_set_features, + .nsid = TEST_NSID, + .cdw10 = NVME_FEAT_FID_RESV_MASK, + .cdw11 = MASK, + .result = TEST_RESULT, + .err = -EIO, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_features_resv_mask2( + TEST_FD, TEST_NSID, MASK, false, &result); + end_mock_cmds(); + check(err == -1, "got error %d, expected -1", err); + check(errno == EIO, "unexpected error %m"); + check(!result, "result unexpectedly set to %" PRIu32, result); +} + +/* + * All get_features functions tail-call nvme_get_features(), + * so testing errors in any of them will do + */ + +static void test_get_status_code_error(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_KATO, + .result = TEST_RESULT, + .err = TEST_SC, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_kato(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == TEST_SC, "got error %d, expected %d", err, TEST_SC); + check(result == TEST_RESULT, + "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT); +} + +static void test_get_kernel_error(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_features, + .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_NUM_QUEUES, + .result = TEST_RESULT, + .err = -EBUSY, + }; + uint32_t result = 0; + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_features_num_queues(TEST_FD, TEST_SEL, &result); + end_mock_cmds(); + check(err == -1, "got error %d, expected -1", err); + check(errno == EBUSY, "unexpected error %m"); + check(!result, "result unexpectedly set to %" PRIu32, result); +} + +static void run_test(const char *test_name, void (*test_fn)(void)) +{ + printf("Running test %s...", test_name); + fflush(stdout); + test_fn(); + puts(" OK"); +} + +#define RUN_TEST(name) run_test(#name, test_ ## name) + +int main(void) +{ + set_mock_fd(TEST_FD); + RUN_TEST(set_features); + RUN_TEST(get_features); + RUN_TEST(set_features_data); + RUN_TEST(get_features_data); + RUN_TEST(set_features_simple); + RUN_TEST(get_features_simple); + RUN_TEST(set_arbitration); + RUN_TEST(get_arbitration); + RUN_TEST(set_power_mgmt); + RUN_TEST(get_power_mgmt); + RUN_TEST(set_lba_range); + RUN_TEST(get_lba_range); + RUN_TEST(set_temp_thresh); + RUN_TEST(get_temp_thresh); + RUN_TEST(set_err_recovery); + RUN_TEST(get_err_recovery); + RUN_TEST(set_volatile_wc); + RUN_TEST(get_volatile_wc); + RUN_TEST(get_num_queues); + RUN_TEST(set_irq_coalesce); + RUN_TEST(get_irq_coalesce); + RUN_TEST(set_irq_config); + RUN_TEST(get_irq_config); + RUN_TEST(set_write_atomic); + RUN_TEST(get_write_atomic); + RUN_TEST(set_async_event); + RUN_TEST(get_async_event); + RUN_TEST(set_auto_pst); + RUN_TEST(get_auto_pst); + RUN_TEST(get_host_mem_buf); + RUN_TEST(set_timestamp); + RUN_TEST(get_timestamp); + RUN_TEST(get_kato); + RUN_TEST(set_hctm); + RUN_TEST(get_hctm); + RUN_TEST(set_nopsc); + RUN_TEST(get_nopsc); + RUN_TEST(set_rrl); + RUN_TEST(get_rrl); + RUN_TEST(set_plm_config); + RUN_TEST(get_plm_config); + RUN_TEST(set_plm_window); + RUN_TEST(get_plm_window); + RUN_TEST(set_lba_sts_interval); + RUN_TEST(get_lba_sts_interval); + RUN_TEST(set_host_behavior); + RUN_TEST(get_host_behavior); + RUN_TEST(set_sanitize); + RUN_TEST(get_sanitize); + RUN_TEST(set_endurance_evt_cfg); + RUN_TEST(get_endurance_event_cfg); + RUN_TEST(set_iocs_profile); + RUN_TEST(get_iocs_profile); + RUN_TEST(set_sw_progress); + RUN_TEST(get_sw_progress); + RUN_TEST(set_host_id); + RUN_TEST(set_host_id_extended); + RUN_TEST(get_host_id); + RUN_TEST(get_host_id_extended); + RUN_TEST(set_resv_mask); + RUN_TEST(get_resv_mask); + RUN_TEST(set_resv_persist); + RUN_TEST(get_resv_persist); + RUN_TEST(set_write_protect); + RUN_TEST(get_write_protect); + RUN_TEST(set_status_code_error); + RUN_TEST(set_kernel_error); + RUN_TEST(get_status_code_error); + RUN_TEST(get_kernel_error); +} |