Merging upstream version 1.10.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
05578a6ab9
commit
a02d194ad0
946 changed files with 4401 additions and 1290 deletions
24
test/config/config-diff.sh
Normal file
24
test/config/config-diff.sh
Normal file
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash -e
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
BUILD_DIR=$1
|
||||
CONFIG_DUMP=$2
|
||||
SYSDIR_INPUT=$3
|
||||
CONFIG_JSON=$4
|
||||
EXPECTED_OUTPUT=$5
|
||||
|
||||
ACTUAL_OUTPUT="${BUILD_DIR}"/$(basename "${EXPECTED_OUTPUT}")
|
||||
|
||||
TEST_NAME="$(basename -s .tar.xz $SYSDIR_INPUT)"
|
||||
TEST_DIR="$BUILD_DIR/$TEST_NAME"
|
||||
|
||||
rm -rf "${TEST_DIR}"
|
||||
mkdir "${TEST_DIR}"
|
||||
tar -x -f "${SYSDIR_INPUT}" -C "${TEST_DIR}"
|
||||
|
||||
LIBNVME_SYSFS_PATH="$TEST_DIR" \
|
||||
LIBNVME_HOSTNQN=nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6 \
|
||||
LIBNVME_HOSTID=ce4fee3e-c02c-11ee-8442-830d068a36c6 \
|
||||
"${CONFIG_DUMP}" "${CONFIG_JSON}" > "${ACTUAL_OUTPUT}" || echo "test failed"
|
||||
|
||||
diff -u "${EXPECTED_OUTPUT}" "${ACTUAL_OUTPUT}"
|
53
test/config/config-dump.c
Normal file
53
test/config/config-dump.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/**
|
||||
* This file is part of libnvme.
|
||||
* Copyright (c) 2024 Daniel Wagner, SUSE LLC
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libnvme.h>
|
||||
|
||||
static bool config_dump(const char *file)
|
||||
{
|
||||
bool pass = false;
|
||||
nvme_root_t r;
|
||||
int err;
|
||||
|
||||
r = nvme_create_root(stderr, LOG_ERR);
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
err = nvme_scan_topology(r, NULL, NULL);
|
||||
if (err) {
|
||||
if (errno != ENOENT)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = nvme_read_config(r, file);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = nvme_dump_config(r);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
pass = true;
|
||||
|
||||
out:
|
||||
nvme_free_tree(r);
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool pass;
|
||||
|
||||
pass = config_dump(argv[1]);
|
||||
fflush(stdout);
|
||||
|
||||
exit(pass ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
48
test/config/data/config-pcie-with-tcp-config.json
Normal file
48
test/config/data/config-pcie-with-tcp-config.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
[
|
||||
{
|
||||
"hostnqn":"nqn.2014-08.org.nvmexpress:uuid:2cd2c43b-a90a-45c1-a8cd-86b33ab273b5",
|
||||
"hostid":"2cd2c43b-a90a-45c1-a8cd-86b33ab273b5",
|
||||
"subsystems":[
|
||||
{
|
||||
"nqn":"nqn.io-1",
|
||||
"ports":[
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4420",
|
||||
"dhchap_key":"none"
|
||||
},
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4421",
|
||||
"dhchap_key":"none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hostnqn":"nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36",
|
||||
"hostid":"befdec4c-2234-11b2-a85c-ca77c773af36",
|
||||
"subsystems":[
|
||||
{
|
||||
"nqn":"nqn.io-1",
|
||||
"ports":[
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4420",
|
||||
"dhchap_key":"none"
|
||||
},
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4421",
|
||||
"dhchap_key":"none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
48
test/config/data/config-pcie-with-tcp-config.out
Normal file
48
test/config/data/config-pcie-with-tcp-config.out
Normal file
|
@ -0,0 +1,48 @@
|
|||
[
|
||||
{
|
||||
"hostnqn":"nqn.2014-08.org.nvmexpress:uuid:2cd2c43b-a90a-45c1-a8cd-86b33ab273b5",
|
||||
"hostid":"2cd2c43b-a90a-45c1-a8cd-86b33ab273b5",
|
||||
"subsystems":[
|
||||
{
|
||||
"nqn":"nqn.io-1",
|
||||
"ports":[
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4420",
|
||||
"dhchap_key":"none"
|
||||
},
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4421",
|
||||
"dhchap_key":"none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hostnqn":"nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36",
|
||||
"hostid":"befdec4c-2234-11b2-a85c-ca77c773af36",
|
||||
"subsystems":[
|
||||
{
|
||||
"nqn":"nqn.io-1",
|
||||
"ports":[
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4420",
|
||||
"dhchap_key":"none"
|
||||
},
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4421",
|
||||
"dhchap_key":"none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
BIN
test/config/data/config-pcie-with-tcp-config.tar.xz
Normal file
BIN
test/config/data/config-pcie-with-tcp-config.tar.xz
Normal file
Binary file not shown.
0
test/config/data/config-pcie.json
Normal file
0
test/config/data/config-pcie.json
Normal file
0
test/config/data/config-pcie.out
Normal file
0
test/config/data/config-pcie.out
Normal file
48
test/config/data/hostnqn-order.json
Normal file
48
test/config/data/hostnqn-order.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
[
|
||||
{
|
||||
"hostnqn":"nqn.2014-08.org.nvmexpress:uuid:2cd2c43b-a90a-45c1-a8cd-86b33ab273b5",
|
||||
"hostid":"2cd2c43b-a90a-45c1-a8cd-86b33ab273b5",
|
||||
"subsystems":[
|
||||
{
|
||||
"nqn":"nqn.io-1",
|
||||
"ports":[
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4420",
|
||||
"dhchap_key":"none"
|
||||
},
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4421",
|
||||
"dhchap_key":"none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hostnqn":"nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36",
|
||||
"hostid":"befdec4c-2234-11b2-a85c-ca77c773af36",
|
||||
"subsystems":[
|
||||
{
|
||||
"nqn":"nqn.io-1",
|
||||
"ports":[
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4420",
|
||||
"dhchap_key":"none"
|
||||
},
|
||||
{
|
||||
"transport":"tcp",
|
||||
"traddr":"192.168.154.144",
|
||||
"trsvcid":"4421",
|
||||
"dhchap_key":"none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
0
test/config/data/hostnqn-order.out
Normal file
0
test/config/data/hostnqn-order.out
Normal file
BIN
test/config/data/hostnqn-order.tar.xz
Normal file
BIN
test/config/data/hostnqn-order.tar.xz
Normal file
Binary file not shown.
164
test/config/hostnqn-order.c
Normal file
164
test/config/hostnqn-order.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/**
|
||||
* This file is part of libnvme.
|
||||
* Copyright (c) 2024 Daniel Wagner, SUSE LLC
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libnvme.h>
|
||||
|
||||
static bool command_line(void)
|
||||
{
|
||||
bool pass = false;
|
||||
nvme_root_t r;
|
||||
int err;
|
||||
char *hostnqn, *hostid, *hnqn, *hid;
|
||||
|
||||
r = nvme_create_root(stderr, LOG_ERR);
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
err = nvme_scan_topology(r, NULL, NULL);
|
||||
if (err) {
|
||||
if (errno != ENOENT)
|
||||
goto out;
|
||||
}
|
||||
|
||||
hostnqn = "nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6";
|
||||
hostid = "ce4fee3e-c02c-11ee-8442-830d068a36c6";
|
||||
|
||||
err = nvme_host_get_ids(r, hostnqn, hostid, &hnqn, &hid);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (strcmp(hostnqn, hnqn)) {
|
||||
printf("json config hostnqn '%s' does not match '%s'\n", hostnqn, hnqn);
|
||||
goto out;
|
||||
}
|
||||
if (strcmp(hostid, hid)) {
|
||||
printf("json config hostid '%s' does not match '%s'\n", hostid, hid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
free(hnqn);
|
||||
free(hid);
|
||||
|
||||
pass = true;
|
||||
|
||||
out:
|
||||
nvme_free_tree(r);
|
||||
return pass;
|
||||
}
|
||||
|
||||
static bool json_config(char *file)
|
||||
{
|
||||
bool pass = false;
|
||||
nvme_root_t r;
|
||||
int err;
|
||||
char *hostnqn, *hostid, *hnqn, *hid;
|
||||
|
||||
setenv("LIBNVME_HOSTNQN", "", 1);
|
||||
setenv("LIBNVME_HOSTID", "", 1);
|
||||
|
||||
r = nvme_create_root(stderr, LOG_ERR);
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
/* We need to read the config in before we scan */
|
||||
err = nvme_read_config(r, file);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = nvme_scan_topology(r, NULL, NULL);
|
||||
if (err) {
|
||||
if (errno != ENOENT)
|
||||
goto out;
|
||||
}
|
||||
|
||||
hostnqn = "nqn.2014-08.org.nvmexpress:uuid:2cd2c43b-a90a-45c1-a8cd-86b33ab273b5";
|
||||
hostid = "2cd2c43b-a90a-45c1-a8cd-86b33ab273b5";
|
||||
|
||||
err = nvme_host_get_ids(r, NULL, NULL, &hnqn, &hid);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (strcmp(hostnqn, hnqn)) {
|
||||
printf("json config hostnqn '%s' does not match '%s'\n", hostnqn, hnqn);
|
||||
goto out;
|
||||
}
|
||||
if (strcmp(hostid, hid)) {
|
||||
printf("json config hostid '%s' does not match '%s'\n", hostid, hid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
free(hnqn);
|
||||
free(hid);
|
||||
|
||||
pass = true;
|
||||
|
||||
out:
|
||||
nvme_free_tree(r);
|
||||
return pass;
|
||||
}
|
||||
|
||||
static bool from_file(void)
|
||||
{
|
||||
bool pass = false;
|
||||
nvme_root_t r;
|
||||
int err;
|
||||
char *hostnqn, *hostid, *hnqn, *hid;
|
||||
|
||||
hostnqn = "nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6";
|
||||
hostid = "ce4fee3e-c02c-11ee-8442-830d068a36c6";
|
||||
|
||||
setenv("LIBNVME_HOSTNQN", hostnqn, 1);
|
||||
setenv("LIBNVME_HOSTID", hostid, 1);
|
||||
|
||||
r = nvme_create_root(stderr, LOG_ERR);
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
err = nvme_scan_topology(r, NULL, NULL);
|
||||
if (err) {
|
||||
if (errno != ENOENT)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = nvme_host_get_ids(r, NULL, NULL, &hnqn, &hid);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (strcmp(hostnqn, hnqn)) {
|
||||
printf("json config hostnqn '%s' does not match '%s'\n", hostnqn, hnqn);
|
||||
goto out;
|
||||
}
|
||||
if (strcmp(hostid, hid)) {
|
||||
printf("json config hostid '%s' does not match '%s'\n", hostid, hid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
free(hnqn);
|
||||
free(hid);
|
||||
|
||||
pass = true;
|
||||
|
||||
out:
|
||||
nvme_free_tree(r);
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool pass;
|
||||
|
||||
pass = command_line();
|
||||
pass &= json_config(argv[1]);
|
||||
pass &= from_file();
|
||||
fflush(stdout);
|
||||
|
||||
exit(pass ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
59
test/config/meson.build
Normal file
59
test/config/meson.build
Normal file
|
@ -0,0 +1,59 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of libnvme.
|
||||
# Copyright (c) 2024 SUSE LLC.
|
||||
#
|
||||
# Authors: Daniel Wagner <dwagner@suse.de>
|
||||
|
||||
diff = find_program('diff', required : false)
|
||||
if diff.found()
|
||||
config_dump = executable(
|
||||
'test-config-dump',
|
||||
['config-dump.c'],
|
||||
dependencies: libnvme_dep,
|
||||
include_directories: [incdir],
|
||||
)
|
||||
|
||||
config_data = [
|
||||
'config-pcie',
|
||||
'config-pcie-with-tcp-config',
|
||||
]
|
||||
|
||||
config_diff = find_program('config-diff.sh')
|
||||
|
||||
foreach t_file : config_data
|
||||
test(
|
||||
t_file,
|
||||
config_diff,
|
||||
args : [
|
||||
meson.current_build_dir(),
|
||||
config_dump.full_path(),
|
||||
files('data'/t_file + '.tar.xz'),
|
||||
files('data'/t_file + '.json'),
|
||||
files('data'/t_file + '.out'),
|
||||
],
|
||||
depends : config_dump,
|
||||
)
|
||||
endforeach
|
||||
|
||||
test_hostnqn_order = executable(
|
||||
'test-hostnqn-order',
|
||||
['hostnqn-order.c'],
|
||||
dependencies: libnvme_dep,
|
||||
include_directories: [incdir],
|
||||
)
|
||||
|
||||
test(
|
||||
'hostnqn-order',
|
||||
config_diff,
|
||||
args : [
|
||||
meson.current_build_dir(),
|
||||
test_hostnqn_order.full_path(),
|
||||
files('data/hostnqn-order.tar.xz'),
|
||||
files('data/hostnqn-order.json'),
|
||||
files('data/hostnqn-order.out'),
|
||||
],
|
||||
depends : test_hostnqn_order,
|
||||
)
|
||||
|
||||
endif
|
643
test/ioctl/ana.c
Normal file
643
test/ioctl/ana.c
Normal file
|
@ -0,0 +1,643 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include <libnvme.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/endian/endian.h>
|
||||
|
||||
#include "mock.h"
|
||||
#include "util.h"
|
||||
|
||||
#define TEST_FD 0xFD
|
||||
#define PDU_SIZE NVME_LOG_PAGE_PDU_SIZE
|
||||
|
||||
static void test_no_retries(void)
|
||||
{
|
||||
struct nvme_ana_log log;
|
||||
__u32 len = sizeof(log);
|
||||
|
||||
/* max_retries = 0 is nonsensical */
|
||||
check(nvme_get_ana_log_atomic(TEST_FD, false, false, 0, &log, &len),
|
||||
"get log page succeeded");
|
||||
check(errno == EINVAL, "unexpected error: %m");
|
||||
}
|
||||
|
||||
static void test_len_too_short(void)
|
||||
{
|
||||
struct nvme_ana_log log;
|
||||
__u32 len = sizeof(log) - 1;
|
||||
|
||||
/* Provided buffer doesn't have enough space to read the header */
|
||||
check(nvme_get_ana_log_atomic(TEST_FD, false, false, 1, &log, &len),
|
||||
"get log page succeeded");
|
||||
check(errno == ENOSPC, "unexpected error: %m");
|
||||
}
|
||||
|
||||
static void test_no_groups(void)
|
||||
{
|
||||
struct nvme_ana_log header;
|
||||
/* The header reports no ANA groups. No additional commands needed. */
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = &header,
|
||||
};
|
||||
struct nvme_ana_log log;
|
||||
__u32 len = sizeof(log);
|
||||
|
||||
arbitrary(&log, sizeof(log));
|
||||
arbitrary(&header, sizeof(header));
|
||||
header.ngrps = cpu_to_le16(0);
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
check(!nvme_get_ana_log_atomic(TEST_FD, false, false, 1, &log, &len),
|
||||
"get log page failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(&log, &header, sizeof(header), "incorrect header");
|
||||
check(len == sizeof(header),
|
||||
"got len %" PRIu32 ", expected %zu", len, sizeof(header));
|
||||
}
|
||||
|
||||
static void test_one_group_rgo(void)
|
||||
{
|
||||
struct nvme_ana_log header;
|
||||
struct nvme_ana_group_desc group;
|
||||
__u8 log_page[sizeof(header) + sizeof(group)];
|
||||
__u32 len = 123;
|
||||
size_t len_dwords = len / 4;
|
||||
/*
|
||||
* Header and group fetched in a single Get Log Page command.
|
||||
* Since only one command was issued, chgcnt doesn't need to be checked.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = len_dwords * 4,
|
||||
.cdw10 = (len_dwords - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY << 8 /* LSP */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page,
|
||||
.out_data_len = sizeof(log_page),
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header, sizeof(header));
|
||||
header.ngrps = cpu_to_le16(1);
|
||||
arbitrary(&group, sizeof(group));
|
||||
group.nnsids = cpu_to_le32(0);
|
||||
memcpy(log_page, &header, sizeof(header));
|
||||
memcpy(log_page + sizeof(header), &group, sizeof(group));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
check(!nvme_get_ana_log_atomic(TEST_FD, true, false, 1, log, &len),
|
||||
"get log page failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, log_page, sizeof(log_page), "incorrect log page");
|
||||
check(len == sizeof(log_page),
|
||||
"got len %" PRIu32 ", expected %zu", len, sizeof(log_page));
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_one_group_nsids(void)
|
||||
{
|
||||
struct nvme_ana_log header;
|
||||
struct nvme_ana_group_desc group;
|
||||
__le32 nsids[3];
|
||||
__u8 log_page[sizeof(header) + sizeof(group) + sizeof(nsids)];
|
||||
__u32 len = 124;
|
||||
size_t len_dwords = len / 4;
|
||||
/*
|
||||
* Header, group, and NSIDs fetched in a single Get Log Page command.
|
||||
* Since only one command was issued, chgcnt doesn't need to be checked.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = len_dwords * 4,
|
||||
.cdw10 = (len_dwords - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page,
|
||||
.out_data_len = sizeof(log_page),
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header, sizeof(header));
|
||||
header.ngrps = cpu_to_le16(1);
|
||||
arbitrary(&group, sizeof(group));
|
||||
group.nnsids = cpu_to_le32(ARRAY_SIZE(nsids));
|
||||
arbitrary(nsids, sizeof(nsids));
|
||||
memcpy(log_page, &header, sizeof(header));
|
||||
memcpy(log_page + sizeof(header), &group, sizeof(group));
|
||||
memcpy(log_page + sizeof(header) + sizeof(group), nsids, sizeof(nsids));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
check(!nvme_get_ana_log_atomic(TEST_FD, false, false, 1, log, &len),
|
||||
"get log page failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, log_page, sizeof(log_page), "incorrect log page");
|
||||
check(len == sizeof(log_page),
|
||||
"got len %" PRIu32 ", expected %zu", len, sizeof(log_page));
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_multiple_groups_rgo(void)
|
||||
{
|
||||
struct nvme_ana_log header;
|
||||
struct nvme_ana_group_desc groups[3];
|
||||
__u8 log_page[sizeof(header) + sizeof(groups)];
|
||||
__u32 len = 125;
|
||||
size_t len_dwords = len / 4;
|
||||
/*
|
||||
* Header and groups fetched in a single Get Log Page command.
|
||||
* Since only one command was issued, chgcnt doesn't need to be checked.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = len_dwords * 4,
|
||||
.cdw10 = (len_dwords - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY << 8 /* LSP */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page,
|
||||
.out_data_len = sizeof(log_page),
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header, sizeof(header));
|
||||
header.ngrps = cpu_to_le16(ARRAY_SIZE(groups));
|
||||
arbitrary(groups, sizeof(groups));
|
||||
for (size_t i = 0; i < ARRAY_SIZE(groups); i++)
|
||||
groups[i].nnsids = cpu_to_le32(0);
|
||||
memcpy(log_page, &header, sizeof(header));
|
||||
memcpy(log_page + sizeof(header), groups, sizeof(groups));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
check(!nvme_get_ana_log_atomic(TEST_FD, true, true, 1, log, &len),
|
||||
"get log page failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, log_page, sizeof(log_page), "incorrect log page");
|
||||
check(len == sizeof(log_page),
|
||||
"got len %" PRIu32 ", expected %zu", len, sizeof(log_page));
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_multiple_groups_nsids(void)
|
||||
{
|
||||
struct nvme_ana_log header;
|
||||
struct nvme_ana_group_desc group1;
|
||||
__le32 nsids1[3];
|
||||
struct nvme_ana_group_desc group2;
|
||||
__le32 nsids2[2];
|
||||
struct nvme_ana_group_desc group3;
|
||||
__le32 nsids3[1];
|
||||
__u8 log_page[sizeof(header) +
|
||||
sizeof(group1) + sizeof(nsids1) +
|
||||
sizeof(group2) + sizeof(nsids2) +
|
||||
sizeof(group3) + sizeof(nsids3)];
|
||||
__u32 len = 456;
|
||||
size_t len_dwords = len / 4;
|
||||
/*
|
||||
* Header, group, and NSIDs fetched in a single Get Log Page command.
|
||||
* Since only one command was issued, chgcnt doesn't need to be checked.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = len_dwords * 4,
|
||||
.cdw10 = (len_dwords - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page,
|
||||
.out_data_len = sizeof(log_page),
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header, sizeof(header));
|
||||
header.ngrps = cpu_to_le16(3);
|
||||
arbitrary(&group1, sizeof(group1));
|
||||
group1.nnsids = cpu_to_le32(ARRAY_SIZE(nsids1));
|
||||
arbitrary(nsids1, sizeof(nsids1));
|
||||
arbitrary(&group2, sizeof(group2));
|
||||
group2.nnsids = cpu_to_le32(ARRAY_SIZE(nsids2));
|
||||
arbitrary(nsids2, sizeof(nsids2));
|
||||
arbitrary(&group3, sizeof(group3));
|
||||
group3.nnsids = cpu_to_le32(ARRAY_SIZE(nsids3));
|
||||
arbitrary(nsids3, sizeof(nsids3));
|
||||
memcpy(log_page, &header, sizeof(header));
|
||||
memcpy(log_page + sizeof(header), &group1, sizeof(group1));
|
||||
memcpy(log_page + sizeof(header) + sizeof(group1),
|
||||
nsids1, sizeof(nsids1));
|
||||
memcpy(log_page + sizeof(header) + sizeof(group1) + sizeof(nsids1),
|
||||
&group2, sizeof(group2));
|
||||
memcpy(log_page + sizeof(header) + sizeof(group1) + sizeof(nsids1) +
|
||||
sizeof(group2),
|
||||
nsids2, sizeof(nsids2));
|
||||
memcpy(log_page + sizeof(header) + sizeof(group1) + sizeof(nsids1) +
|
||||
sizeof(group2) + sizeof(nsids2),
|
||||
&group3, sizeof(group3));
|
||||
memcpy(log_page + sizeof(header) + sizeof(group1) + sizeof(nsids1) +
|
||||
sizeof(group2) + sizeof(nsids2) + sizeof(group3),
|
||||
nsids3, sizeof(nsids3));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
check(!nvme_get_ana_log_atomic(TEST_FD, false, false, 1, log, &len),
|
||||
"get log page failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, log_page, sizeof(log_page), "incorrect log page");
|
||||
check(len == sizeof(log_page),
|
||||
"got len %" PRIu32 ", expected %zu", len, sizeof(log_page));
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_long_log(void)
|
||||
{
|
||||
struct nvme_ana_log header;
|
||||
struct nvme_ana_group_desc group;
|
||||
__le32 nsids[PDU_SIZE * 2 / sizeof(*group.nsids)];
|
||||
__u8 log_page[sizeof(header) + sizeof(group) + sizeof(nsids)];
|
||||
__u32 len = PDU_SIZE * 4;
|
||||
/*
|
||||
* Get Log Page is issued for 4 KB, returning the header (with 1 group),
|
||||
* the group (with 2048 NSIDs) and the start of its NSIDs.
|
||||
* Another Get Log page command is issued for the next 1024 NSIDs.
|
||||
* Another Get Log page command is issued for the last NSIDs.
|
||||
* Header is fetched again to verify chgcnt hasn't changed.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.cdw12 = PDU_SIZE, /* LPOL */
|
||||
.out_data = log_page + PDU_SIZE,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.cdw12 = PDU_SIZE * 2, /* LPOL */
|
||||
.out_data = log_page + PDU_SIZE * 2,
|
||||
.out_data_len = sizeof(log_page) - PDU_SIZE * 2,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page,
|
||||
},
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header, sizeof(header));
|
||||
header.ngrps = cpu_to_le16(1);
|
||||
arbitrary(&group, sizeof(group));
|
||||
group.nnsids = cpu_to_le32(ARRAY_SIZE(nsids));
|
||||
arbitrary(nsids, sizeof(nsids));
|
||||
memcpy(log_page, &header, sizeof(header));
|
||||
memcpy(log_page + sizeof(header), &group, sizeof(group));
|
||||
memcpy(log_page + sizeof(header) + sizeof(group), nsids, sizeof(nsids));
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(!nvme_get_ana_log_atomic(TEST_FD, false, true, 1, log, &len),
|
||||
"get log page failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, log_page, sizeof(log_page), "incorrect log page");
|
||||
check(len == sizeof(log_page),
|
||||
"got len %" PRIu32 ", expected %zu", len, sizeof(log_page));
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_chgcnt_change(void)
|
||||
{
|
||||
struct nvme_ana_log header1;
|
||||
struct nvme_ana_group_desc groups1[PDU_SIZE / sizeof(*header1.descs)];
|
||||
__u8 log_page1[sizeof(header1) + sizeof(groups1)];
|
||||
struct nvme_ana_log header2;
|
||||
struct nvme_ana_group_desc group2;
|
||||
__u8 log_page2[sizeof(header2) + sizeof(group2)];
|
||||
__u32 len = PDU_SIZE + 126;
|
||||
size_t remainder_len_dwords = (len - PDU_SIZE) / 4;
|
||||
/*
|
||||
* Get Log Page is issued for 4 KB,
|
||||
* returning the header (with 128 groups), and the start of the groups.
|
||||
* Get Log Page is issued for the rest of the groups.
|
||||
* Get Log Page is issued for the first 4 KB again to check chgcnt.
|
||||
* chgcnt has changed, but there is only 1 group now,
|
||||
* which was already fetched with the header.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY << 8 /* LSP */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page1,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = remainder_len_dwords * 4,
|
||||
.cdw10 = (remainder_len_dwords - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY << 8 /* LSP */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.cdw12 = PDU_SIZE, /* LPOL */
|
||||
.out_data = log_page1 + PDU_SIZE,
|
||||
.out_data_len = sizeof(log_page1) - PDU_SIZE,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY << 8 /* LSP */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page2,
|
||||
.out_data_len = sizeof(log_page2),
|
||||
},
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header1, sizeof(header1));
|
||||
header1.ngrps = cpu_to_le16(ARRAY_SIZE(groups1));
|
||||
arbitrary(&groups1, sizeof(groups1));
|
||||
for (size_t i = 0; i < ARRAY_SIZE(groups1); i++)
|
||||
groups1[i].nnsids = cpu_to_le32(0);
|
||||
memcpy(log_page1, &header1, sizeof(header1));
|
||||
memcpy(log_page1 + sizeof(header1), groups1, sizeof(groups1));
|
||||
arbitrary(&header2, sizeof(header2));
|
||||
header2.ngrps = cpu_to_le16(1);
|
||||
arbitrary(&group2, sizeof(group2));
|
||||
group2.nnsids = cpu_to_le32(0);
|
||||
memcpy(log_page2, &header2, sizeof(header2));
|
||||
memcpy(log_page2 + sizeof(header2), &group2, sizeof(group2));
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(!nvme_get_ana_log_atomic(TEST_FD, true, true, 2, log, &len),
|
||||
"get log page failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, log_page2, sizeof(log_page2), "incorrect log page");
|
||||
check(len == sizeof(log_page2),
|
||||
"got len %" PRIu32 ", expected %zu", len, sizeof(log_page2));
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_buffer_too_short_chgcnt_change(void)
|
||||
{
|
||||
struct nvme_ana_log header1;
|
||||
struct nvme_ana_group_desc group1_1;
|
||||
__le32 nsids1[PDU_SIZE / sizeof(*group1_1.nsids)];
|
||||
struct nvme_ana_group_desc group1_2;
|
||||
__u8 log_page1[sizeof(header1) +
|
||||
sizeof(group1_1) + sizeof(nsids1) + sizeof(group1_2)];
|
||||
struct nvme_ana_log header2;
|
||||
struct nvme_ana_group_desc group2;
|
||||
__le32 nsid2;
|
||||
uint8_t log_page2[sizeof(header2) + sizeof(group2) + sizeof(nsid2)];
|
||||
__u32 len = PDU_SIZE + 123;
|
||||
size_t remainder_len_dwords = (len - PDU_SIZE) / 4;
|
||||
/*
|
||||
* Get Log Page issued for 4 KB, returning the header (with 2 groups),
|
||||
* the first group (with 1024 NSIDs), and the start of the NSIDs.
|
||||
* Get Log Page is issued for the rest of the NSIDs and the second group.
|
||||
* The second group contains garbage, making the log exceed the buffer.
|
||||
* The first 4 KB is fetched again, returning a header with a new chgcnt
|
||||
* and a group with one NSID.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page1,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = remainder_len_dwords * 4,
|
||||
.cdw10 = (remainder_len_dwords - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.cdw12 = PDU_SIZE, /* LPOL */
|
||||
.out_data = log_page1 + PDU_SIZE,
|
||||
.out_data_len = sizeof(log_page1) - PDU_SIZE,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page2,
|
||||
.out_data_len = sizeof(log_page2),
|
||||
},
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header1, sizeof(header1));
|
||||
header1.ngrps = cpu_to_le16(2);
|
||||
arbitrary(&group1_1, sizeof(group1_1));
|
||||
group1_1.nnsids = cpu_to_le32(ARRAY_SIZE(nsids1));
|
||||
arbitrary(nsids1, sizeof(nsids1));
|
||||
memset(&group1_2, -1, sizeof(group1_2));
|
||||
memcpy(log_page1, &header1, sizeof(header1));
|
||||
memcpy(log_page1 + sizeof(header1), &group1_1, sizeof(group1_1));
|
||||
memcpy(log_page1 + sizeof(header1) + sizeof(group1_1),
|
||||
nsids1, sizeof(nsids1));
|
||||
memcpy(log_page1 + sizeof(header1) + sizeof(group1_1) + sizeof(nsids1),
|
||||
&group1_2, sizeof(group1_2));
|
||||
arbitrary(&header2, sizeof(header2));
|
||||
header2.ngrps = cpu_to_le16(1);
|
||||
arbitrary(&group2, sizeof(group2));
|
||||
group2.nnsids = cpu_to_le32(1);
|
||||
arbitrary(&nsid2, sizeof(nsid2));
|
||||
memcpy(log_page2, &header2, sizeof(header2));
|
||||
memcpy(log_page2 + sizeof(header2), &group2, sizeof(group2));
|
||||
memcpy(log_page2 + sizeof(header2) + sizeof(group2),
|
||||
&nsid2, sizeof(nsid2));
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(!nvme_get_ana_log_atomic(TEST_FD, false, false, 2, log, &len),
|
||||
"get log page failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, log_page2, sizeof(log_page2), "incorrect log page");
|
||||
check(len == sizeof(log_page2),
|
||||
"got len %" PRIu32 ", expected %zu", len, sizeof(log_page2));
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_chgcnt_max_retries(void)
|
||||
{
|
||||
struct nvme_ana_log header1, header2, header3;
|
||||
struct nvme_ana_group_desc group;
|
||||
__le32 nsids[PDU_SIZE / sizeof(*group.nsids)];
|
||||
__u8 log_page1[sizeof(header1) + sizeof(group) + sizeof(nsids)],
|
||||
log_page2[sizeof(header2) + sizeof(group) + sizeof(nsids)];
|
||||
__u32 len = PDU_SIZE * 2;
|
||||
/*
|
||||
* Get Log Page is issued for 4 KB, returning the header (with 1 group),
|
||||
* the group (with 1024 NSIDs), and the start of the NSIDs.
|
||||
* Get Log Page is issued for the rest of the NSIDs.
|
||||
* Get Log Page is issued for the first 4 KB again to check chgcnt.
|
||||
* chgcnt has changed and there is still 1 group with 1024 NSIDs.
|
||||
* Get Log Page is issued for the rest of the NSIDs.
|
||||
* Get Log Page is issued for the first 4 KB again to check chgcnt.
|
||||
* chgcnt has changed again.
|
||||
* This exceeds max_retries = 2 so nvme_get_ana_log() exits with EAGAIN.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page1,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.cdw12 = PDU_SIZE, /* LPOL */
|
||||
.out_data = log_page1 + PDU_SIZE,
|
||||
.out_data_len = sizeof(log_page1) - PDU_SIZE,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page2,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.cdw12 = PDU_SIZE, /* LPOL */
|
||||
.out_data = log_page2 + PDU_SIZE,
|
||||
.out_data_len = sizeof(log_page2) - PDU_SIZE,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = PDU_SIZE,
|
||||
.cdw10 = (PDU_SIZE / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = &header3,
|
||||
.out_data_len = sizeof(header3),
|
||||
},
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header1, sizeof(header1));
|
||||
header1.ngrps = cpu_to_le16(1);
|
||||
arbitrary(&header2, sizeof(header2));
|
||||
header2.ngrps = cpu_to_le16(1);
|
||||
arbitrary(&header3, sizeof(header3));
|
||||
header3.ngrps = cpu_to_le16(0);
|
||||
arbitrary(&group, sizeof(group));
|
||||
group.nnsids = cpu_to_le32(ARRAY_SIZE(nsids));
|
||||
arbitrary(nsids, sizeof(nsids));
|
||||
memcpy(log_page1, &header1, sizeof(header1));
|
||||
memcpy(log_page1 + sizeof(header1), &group, sizeof(group));
|
||||
memcpy(log_page1 + sizeof(header1) + sizeof(group),
|
||||
nsids, sizeof(nsids));
|
||||
memcpy(log_page2, &header2, sizeof(header2));
|
||||
memcpy(log_page2 + sizeof(header2), &group, sizeof(group));
|
||||
memcpy(log_page2 + sizeof(header2) + sizeof(group),
|
||||
nsids, sizeof(nsids));
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvme_get_ana_log_atomic(TEST_FD, false, true, 2, log, &len) == -1,
|
||||
"get log page succeeded");
|
||||
end_mock_cmds();
|
||||
check(errno == EAGAIN, "unexpected error: %m");
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_buffer_too_short(void)
|
||||
{
|
||||
struct nvme_ana_log header;
|
||||
struct nvme_ana_group_desc group;
|
||||
__le32 nsids[20];
|
||||
__u8 log_page[sizeof(header) + sizeof(group) + sizeof(nsids)];
|
||||
__u32 len = 123;
|
||||
__u32 len_dwords = len / 4;
|
||||
/*
|
||||
* Header, group, and NSIDs fetched in a single Get Log Page command.
|
||||
* This length exceeds the provided buffer.
|
||||
* Only one command was issued, so the log page couldn't have changed.
|
||||
* nvme_get_ana_log() returns ENOSPC because the buffer is too small.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = len_dwords * 4,
|
||||
.cdw10 = (len_dwords - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_ANA, /* LID */
|
||||
.out_data = log_page,
|
||||
};
|
||||
struct nvme_ana_log *log = malloc(len);
|
||||
|
||||
arbitrary(log, len);
|
||||
arbitrary(&header, sizeof(header));
|
||||
header.ngrps = cpu_to_le16(1);
|
||||
arbitrary(&group, sizeof(group));
|
||||
group.nnsids = cpu_to_le32(ARRAY_SIZE(nsids));
|
||||
arbitrary(nsids, sizeof(nsids));
|
||||
memcpy(log_page, &header, sizeof(header));
|
||||
memcpy(log_page + sizeof(header), &group, sizeof(group));
|
||||
memcpy(log_page + sizeof(header) + sizeof(group), nsids, sizeof(nsids));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
check(nvme_get_ana_log_atomic(TEST_FD, false, true, 2, log, &len) == -1,
|
||||
"get log page succeeded");
|
||||
end_mock_cmds();
|
||||
check(errno == ENOSPC, "unexpected error: %m");
|
||||
free(log);
|
||||
}
|
||||
|
||||
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(no_retries);
|
||||
RUN_TEST(len_too_short);
|
||||
RUN_TEST(no_groups);
|
||||
RUN_TEST(one_group_rgo);
|
||||
RUN_TEST(one_group_nsids);
|
||||
RUN_TEST(multiple_groups_rgo);
|
||||
RUN_TEST(multiple_groups_nsids);
|
||||
RUN_TEST(long_log);
|
||||
RUN_TEST(chgcnt_change);
|
||||
RUN_TEST(buffer_too_short_chgcnt_change);
|
||||
RUN_TEST(chgcnt_max_retries);
|
||||
RUN_TEST(buffer_too_short);
|
||||
}
|
|
@ -13,6 +13,16 @@ mock_ioctl_env = environment()
|
|||
mock_ioctl_env.append('LD_PRELOAD', mock_ioctl.full_path())
|
||||
mock_ioctl_env.set('ASAN_OPTIONS', 'verify_asan_link_order=0')
|
||||
|
||||
ana = executable(
|
||||
'test-ana',
|
||||
'ana.c',
|
||||
dependencies: libnvme_dep,
|
||||
include_directories: [incdir, internal_incdir],
|
||||
link_with: mock_ioctl,
|
||||
)
|
||||
|
||||
test('ana', ana, env: mock_ioctl_env)
|
||||
|
||||
discovery = executable(
|
||||
'test-discovery',
|
||||
'discovery.c',
|
||||
|
|
|
@ -58,19 +58,19 @@ void end_mock_cmds(void)
|
|||
|
||||
#define execute_ioctl(cmd, mock_cmd) ({ \
|
||||
check((cmd)->opcode == (mock_cmd)->opcode, \
|
||||
"got opcode %" PRIu8 ", expected %" PRIu8, \
|
||||
"got opcode 0x%" PRIx8 ", expected 0x%" PRIx8, \
|
||||
(cmd)->opcode, (mock_cmd)->opcode); \
|
||||
check((cmd)->flags == (mock_cmd)->flags, \
|
||||
"got flags %" PRIu8 ", expected %" PRIu8, \
|
||||
"got flags 0x%" PRIx8 ", expected 0x%" PRIx8, \
|
||||
(cmd)->flags, (mock_cmd)->flags); \
|
||||
check((cmd)->nsid == (mock_cmd)->nsid, \
|
||||
"got nsid %" PRIu32 ", expected %" PRIu32, \
|
||||
"got nsid 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->nsid, (mock_cmd)->nsid); \
|
||||
check((cmd)->cdw2 == (mock_cmd)->cdw2, \
|
||||
"got cdw2 %" PRIu32 ", expected %" PRIu32, \
|
||||
"got cdw2 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->cdw2, (mock_cmd)->cdw2); \
|
||||
check((cmd)->cdw3 == (mock_cmd)->cdw3, \
|
||||
"got cdw3 %" PRIu32 ", expected %" PRIu32, \
|
||||
"got cdw3 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->cdw3, (mock_cmd)->cdw3); \
|
||||
check((cmd)->metadata_len == (mock_cmd)->metadata_len, \
|
||||
"got metadata_len %" PRIu32 ", expected %" PRIu32, \
|
||||
|
@ -90,29 +90,30 @@ void end_mock_cmds(void)
|
|||
cmp(data, (mock_cmd)->in_data, data_len, "incorrect data"); \
|
||||
} \
|
||||
check((cmd)->cdw10 == (mock_cmd)->cdw10, \
|
||||
"got cdw10 %" PRIu32 ", expected %" PRIu32, \
|
||||
"got cdw10 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->cdw10, (mock_cmd)->cdw10); \
|
||||
check((cmd)->cdw11 == (mock_cmd)->cdw11, \
|
||||
"got cdw11 %" PRIu32 ", expected %" PRIu32, \
|
||||
"got cdw11 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->cdw11, (mock_cmd)->cdw11); \
|
||||
check((cmd)->cdw12 == (mock_cmd)->cdw12, \
|
||||
"got cdw12 %" PRIu32 ", expected %" PRIu32, \
|
||||
"got cdw12 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->cdw12, (mock_cmd)->cdw12); \
|
||||
check((cmd)->cdw13 == (mock_cmd)->cdw13, \
|
||||
"got cdw13 %" PRIu32 ", expected %" PRIu32, \
|
||||
"got cdw13 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->cdw13, (mock_cmd)->cdw13); \
|
||||
check((cmd)->cdw14 == (mock_cmd)->cdw14, \
|
||||
"got cdw14 %" PRIu32 ", expected %" PRIu32, \
|
||||
"got cdw14 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->cdw14, (mock_cmd)->cdw14); \
|
||||
check((cmd)->cdw15 == (mock_cmd)->cdw15, \
|
||||
"got cdw15 %" PRIu32 ", expected %" PRIu32, \
|
||||
"got cdw15 0x%" PRIx32 ", expected 0x%" PRIx32, \
|
||||
(cmd)->cdw15, (mock_cmd)->cdw15); \
|
||||
check((cmd)->timeout_ms == (mock_cmd)->timeout_ms, \
|
||||
"got timeout_ms %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->timeout_ms, (mock_cmd)->timeout_ms); \
|
||||
(cmd)->result = (mock_cmd)->result; \
|
||||
if ((mock_cmd)->out_data) { \
|
||||
memcpy(data, (mock_cmd)->out_data, data_len); \
|
||||
const void *out_data = (mock_cmd)->out_data; \
|
||||
if (out_data) { \
|
||||
memcpy(data, out_data, (mock_cmd)->out_data_len ?: data_len); \
|
||||
} \
|
||||
})
|
||||
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
* @cdw14: the expected `cdw14` passed to ioctl()
|
||||
* @cdw15: the expected `cdw15` passed to ioctl()
|
||||
* @timeout_ms: the expected `timeout_ms` passed to ioctl()
|
||||
* @out_data: if not NULL, `data_len` bytes to copy to the caller's `addr`
|
||||
* @out_data: if not NULL, bytes to copy to the caller's `addr`
|
||||
* @out_data_len: length of `out_data` buffer to return.
|
||||
* If 0, `data_len` is used instead.
|
||||
* @result: copied to the caller's `result`.
|
||||
* If `result` doesn't fit in a u32, the ioctl() must be the 64-bit one.
|
||||
* @err: If negative, ioctl() returns -1 and sets `errno` to `-err`.
|
||||
|
@ -50,6 +52,7 @@ struct mock_cmd {
|
|||
uint32_t cdw15;
|
||||
uint32_t timeout_ms;
|
||||
const void *out_data;
|
||||
uint32_t out_data_len;
|
||||
uint64_t result;
|
||||
int err;
|
||||
};
|
||||
|
|
|
@ -66,6 +66,15 @@ uuid = executable(
|
|||
|
||||
test('uuid', uuid)
|
||||
|
||||
uriparser = executable(
|
||||
'test-uriparser',
|
||||
['uriparser.c'],
|
||||
dependencies: libnvme_dep,
|
||||
include_directories: [incdir, internal_incdir]
|
||||
)
|
||||
|
||||
test('uriparser', uriparser)
|
||||
|
||||
if conf.get('HAVE_NETDB')
|
||||
mock_ifaddrs = library(
|
||||
'mock-ifaddrs',
|
||||
|
@ -99,5 +108,6 @@ subdir('ioctl')
|
|||
subdir('nbft')
|
||||
|
||||
if json_c_dep.found()
|
||||
subdir('sysfs')
|
||||
subdir('sysfs')
|
||||
subdir('config')
|
||||
endif
|
||||
|
|
53
test/mi.c
53
test/mi.c
|
@ -115,6 +115,9 @@ nvme_mi_ep_t nvme_mi_open_test(nvme_root_t root)
|
|||
ep = nvme_mi_init_ep(root);
|
||||
assert(ep);
|
||||
|
||||
/* preempt the quirk probe to avoid clutter */
|
||||
ep->quirks_probed = true;
|
||||
|
||||
tpd = malloc(sizeof(*tpd));
|
||||
assert(tpd);
|
||||
|
||||
|
@ -1856,6 +1859,55 @@ static void test_admin_get_log_split(struct nvme_mi_ep *ep)
|
|||
assert(ldata.n == 3);
|
||||
}
|
||||
|
||||
static int test_endpoint_quirk_probe_cb_stage2(struct nvme_mi_ep *ep,
|
||||
struct nvme_mi_req *req,
|
||||
struct nvme_mi_resp *resp,
|
||||
void *data)
|
||||
{
|
||||
return test_read_mi_data_cb(ep, req, resp, data);
|
||||
}
|
||||
|
||||
static int test_endpoint_quirk_probe_cb_stage1(struct nvme_mi_ep *ep,
|
||||
struct nvme_mi_req *req,
|
||||
struct nvme_mi_resp *resp,
|
||||
void *data)
|
||||
{
|
||||
struct nvme_mi_admin_req_hdr *admin_req;
|
||||
__u8 ror, mt;
|
||||
|
||||
assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
|
||||
|
||||
ror = req->hdr->nmp >> 7;
|
||||
mt = req->hdr->nmp >> 3 & 0x7;
|
||||
assert(ror == NVME_MI_ROR_REQ);
|
||||
assert(mt == NVME_MI_MT_ADMIN);
|
||||
|
||||
assert(req->hdr_len == sizeof(struct nvme_mi_admin_req_hdr));
|
||||
|
||||
admin_req = (struct nvme_mi_admin_req_hdr *)req->hdr;
|
||||
assert(admin_req->opcode == nvme_admin_identify);
|
||||
assert(le32_to_cpu(admin_req->doff) == 0);
|
||||
assert(le32_to_cpu(admin_req->dlen) == offsetof(struct nvme_id_ctrl, rab));
|
||||
|
||||
test_set_transport_callback(ep, test_endpoint_quirk_probe_cb_stage2, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_endpoint_quirk_probe(struct nvme_mi_ep *ep)
|
||||
{
|
||||
struct nvme_mi_read_nvm_ss_info ss_info;
|
||||
int rc;
|
||||
|
||||
/* force the probe to occur */
|
||||
ep->quirks_probed = false;
|
||||
|
||||
test_set_transport_callback(ep, test_endpoint_quirk_probe_cb_stage1, NULL);
|
||||
|
||||
rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
#define DEFINE_TEST(name) { #name, test_ ## name }
|
||||
struct test {
|
||||
const char *name;
|
||||
|
@ -1897,6 +1949,7 @@ struct test {
|
|||
DEFINE_TEST(admin_format_nvm),
|
||||
DEFINE_TEST(admin_sanitize_nvm),
|
||||
DEFINE_TEST(admin_get_log_split),
|
||||
DEFINE_TEST(endpoint_quirk_probe),
|
||||
};
|
||||
|
||||
static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep)
|
||||
|
|
|
@ -4,17 +4,6 @@
|
|||
"hostnqn":"nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6",
|
||||
"hostid":"ce4fee3e-c02c-11ee-8442-830d068a36c6",
|
||||
"subsystems":[
|
||||
{
|
||||
"name":"nvme-subsys1",
|
||||
"nqn":"nqn.2019-08.org.qemu:nvme-0",
|
||||
"controllers":[
|
||||
{
|
||||
"name":"nvme1",
|
||||
"transport":"pcie",
|
||||
"traddr":"0000:00:05.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"nvme-subsys0",
|
||||
"nqn":"nqn.2019-08.org.qemu:subsys1",
|
||||
|
@ -25,6 +14,17 @@
|
|||
"traddr":"0000:0f:00.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"nvme-subsys1",
|
||||
"nqn":"nqn.2019-08.org.qemu:nvme-0",
|
||||
"controllers":[
|
||||
{
|
||||
"name":"nvme1",
|
||||
"transport":"pcie",
|
||||
"traddr":"0000:00:05.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
BIN
test/sysfs/data/tree-pcie.tar.xz
Normal file
BIN
test/sysfs/data/tree-pcie.tar.xz
Normal file
Binary file not shown.
|
@ -7,30 +7,30 @@
|
|||
|
||||
diff = find_program('diff', required : false)
|
||||
if diff.found()
|
||||
sysfs_tree_print = executable(
|
||||
'sysfs-tree-print',
|
||||
['sysfs.c'],
|
||||
tree_dump = executable(
|
||||
'test-tree-dump',
|
||||
['tree-dump.c'],
|
||||
dependencies: libnvme_dep,
|
||||
include_directories: [incdir],
|
||||
)
|
||||
|
||||
sysfs_files= [
|
||||
'nvme-sysfs-tw-carbon-6.8.0-rc1+'
|
||||
tree_data = [
|
||||
'tree-pcie',
|
||||
]
|
||||
|
||||
sysfs_tree_diff = find_program('sysfs-tree-diff.sh')
|
||||
tree_diff = find_program('tree-diff.sh')
|
||||
|
||||
foreach t_file : sysfs_files
|
||||
foreach t_file : tree_data
|
||||
test(
|
||||
'sysfs',
|
||||
sysfs_tree_diff,
|
||||
t_file,
|
||||
tree_diff,
|
||||
args : [
|
||||
meson.current_build_dir(),
|
||||
sysfs_tree_print.full_path(),
|
||||
tree_dump.full_path(),
|
||||
files('data'/t_file + '.tar.xz'),
|
||||
files('data'/t_file + '.out'),
|
||||
],
|
||||
depends : sysfs_tree_print,
|
||||
depends : tree_dump,
|
||||
)
|
||||
endforeach
|
||||
endif
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
#!/bin/bash -e
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
BUILD_DIR=$1
|
||||
SYSFS_TREE_PRINT=$2
|
||||
INPUT=$3
|
||||
EXPECTED_OUTPUT=$4
|
||||
|
||||
TEST_NAME="$(basename -s .tar.xz $INPUT)"
|
||||
TEST_DIR="$BUILD_DIR/$TEST_NAME"
|
||||
ACTUAL_OUTPUT="$TEST_DIR.out"
|
||||
|
||||
rm -rf "$TEST_DIR"
|
||||
mkdir "$TEST_DIR"
|
||||
tar -x -f "$INPUT" -C "$TEST_DIR"
|
||||
|
||||
LIBNVME_SYSFS_PATH="$TEST_DIR" \
|
||||
LIBNVME_HOSTNQN=nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6 \
|
||||
LIBNVME_HOSTID=ce4fee3e-c02c-11ee-8442-830d068a36c6 \
|
||||
"$SYSFS_TREE_PRINT" > "$ACTUAL_OUTPUT"
|
||||
|
||||
diff -u "$EXPECTED_OUTPUT" "$ACTUAL_OUTPUT"
|
|
@ -1,24 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/**
|
||||
* This file is part of libnvme.
|
||||
* Copyright (c) 2024 Daniel Wagner, SUSE LLC
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <libnvme.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
nvme_root_t r;
|
||||
|
||||
r = nvme_create_root(stdout, LOG_ERR);
|
||||
assert(r);
|
||||
|
||||
assert(nvme_scan_topology(r, NULL, NULL) == 0);
|
||||
|
||||
assert(nvme_dump_tree(r) == 0);
|
||||
printf("\n");
|
||||
|
||||
nvme_free_tree(r);
|
||||
}
|
22
test/sysfs/tree-diff.sh
Normal file
22
test/sysfs/tree-diff.sh
Normal file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash -e
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
BUILD_DIR=$1
|
||||
TREE_DUMP=$2
|
||||
SYSFS_INPUT=$3
|
||||
EXPECTED_OUTPUT=$4
|
||||
|
||||
TEST_NAME="$(basename -s .tar.xz ${SYSFS_INPUT})"
|
||||
TEST_DIR="${BUILD_DIR}/${TEST_NAME}"
|
||||
ACTUAL_OUTPUT="${TEST_DIR}.out"
|
||||
|
||||
rm -rf "${TEST_DIR}"
|
||||
mkdir "${TEST_DIR}"
|
||||
tar -x -f "${SYSFS_INPUT}" -C "${TEST_DIR}"
|
||||
|
||||
LIBNVME_SYSFS_PATH="${TEST_DIR}" \
|
||||
LIBNVME_HOSTNQN=nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6 \
|
||||
LIBNVME_HOSTID=ce4fee3e-c02c-11ee-8442-830d068a36c6 \
|
||||
"${TREE_DUMP}" > "${ACTUAL_OUTPUT}" || echo "test failed"
|
||||
|
||||
diff -u "${EXPECTED_OUTPUT}" "${ACTUAL_OUTPUT}"
|
49
test/sysfs/tree-dump.c
Normal file
49
test/sysfs/tree-dump.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/**
|
||||
* This file is part of libnvme.
|
||||
* Copyright (c) 2024 Daniel Wagner, SUSE LLC
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libnvme.h>
|
||||
|
||||
static bool tree_dump(void)
|
||||
{
|
||||
bool pass = false;
|
||||
nvme_root_t r;
|
||||
int err;
|
||||
|
||||
r = nvme_create_root(stdout, LOG_ERR);
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
err = nvme_scan_topology(r, NULL, NULL);
|
||||
if (err) {
|
||||
if (errno != ENOENT)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nvme_dump_tree(r))
|
||||
goto out;
|
||||
printf("\n");
|
||||
|
||||
pass = true;
|
||||
|
||||
out:
|
||||
nvme_free_tree(r);
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool pass = true;
|
||||
|
||||
pass = tree_dump();
|
||||
fflush(stdout);
|
||||
|
||||
exit(pass ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
|
@ -48,7 +48,7 @@ static int test_ctrl(nvme_ctrl_t c)
|
|||
struct nvme_self_test_log st = { 0 };
|
||||
struct nvme_telemetry_log *telem = (void *)buf;
|
||||
struct nvme_endurance_group_log eglog = { 0 };
|
||||
struct nvme_ana_group_desc *analog = (void *)buf;
|
||||
struct nvme_ana_log *analog = (void *)buf;
|
||||
struct nvme_resv_notification_log resvnotify = { 0 };
|
||||
struct nvme_sanitize_log_page sanlog = { 0 };
|
||||
struct nvme_id_uuid_list uuid = { 0 };
|
||||
|
|
221
test/uriparser.c
Normal file
221
test/uriparser.c
Normal file
|
@ -0,0 +1,221 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/**
|
||||
* This file is part of libnvme.
|
||||
* Copyright (c) 2024 Tomas Bzatek <tbzatek@redhat.com>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <ccan/array_size/array_size.h>
|
||||
|
||||
#include <libnvme.h>
|
||||
#include <nvme/private.h>
|
||||
|
||||
struct test_data {
|
||||
const char *uri;
|
||||
/* parsed data */
|
||||
const char *scheme;
|
||||
const char *host;
|
||||
const char *user;
|
||||
const char *proto;
|
||||
int port;
|
||||
const char *path[7];
|
||||
const char *query;
|
||||
const char *frag;
|
||||
};
|
||||
|
||||
static struct test_data test_data[] = {
|
||||
{ "nvme://192.168.1.1", "nvme", "192.168.1.1" },
|
||||
{ "nvme://192.168.1.1/", "nvme", "192.168.1.1" },
|
||||
{ "nvme://192.168.1.1:1234", "nvme", "192.168.1.1", .port = 1234 },
|
||||
{ "nvme://192.168.1.1:1234/", "nvme", "192.168.1.1", .port = 1234 },
|
||||
{ "nvme+tcp://192.168.1.1", "nvme", "192.168.1.1", .proto = "tcp" },
|
||||
{ "nvme+rdma://192.168.1.1/", "nvme", "192.168.1.1", .proto = "rdma" },
|
||||
{ "nvme+tcp://192.168.1.1:1234",
|
||||
"nvme", "192.168.1.1", .proto = "tcp", .port = 1234 },
|
||||
{ "nvme+tcp://192.168.1.1:1234/",
|
||||
"nvme", "192.168.1.1", .proto = "tcp", .port = 1234 },
|
||||
{ "nvme+tcp://192.168.1.1:4420/path",
|
||||
"nvme", "192.168.1.1", .proto = "tcp", .port = 4420,
|
||||
.path = { "path", NULL }},
|
||||
{ "nvme+tcp://192.168.1.1/path/",
|
||||
"nvme", "192.168.1.1", .proto = "tcp", .path = { "path", NULL }},
|
||||
{ "nvme+tcp://192.168.1.1:4420/p1/p2/p3",
|
||||
"nvme", "192.168.1.1", .proto = "tcp", .port = 4420,
|
||||
.path = { "p1", "p2", "p3", NULL }},
|
||||
{ "nvme+tcp://192.168.1.1:4420/p1/p2/p3/",
|
||||
"nvme", "192.168.1.1", .proto = "tcp", .port = 4420,
|
||||
.path = { "p1", "p2", "p3", NULL }},
|
||||
{ "nvme+tcp://192.168.1.1:4420//p1//p2/////p3",
|
||||
"nvme", "192.168.1.1", .proto = "tcp", .port = 4420,
|
||||
.path = { "p1", "p2", "p3", NULL }},
|
||||
{ "nvme+tcp://192.168.1.1:4420//p1//p2/////p3/",
|
||||
"nvme", "192.168.1.1", .proto = "tcp", .port = 4420,
|
||||
.path = { "p1", "p2", "p3", NULL }},
|
||||
{ "nvme://[fe80::1010]", "nvme", "fe80::1010" },
|
||||
{ "nvme://[fe80::1010]/", "nvme", "fe80::1010" },
|
||||
{ "nvme://[fe80::1010]:1234", "nvme", "fe80::1010", .port = 1234 },
|
||||
{ "nvme://[fe80::1010]:1234/", "nvme", "fe80::1010", .port = 1234 },
|
||||
{ "nvme+tcp://[fe80::1010]", "nvme", "fe80::1010", .proto = "tcp" },
|
||||
{ "nvme+rdma://[fe80::1010]/", "nvme", "fe80::1010", .proto = "rdma" },
|
||||
{ "nvme+tcp://[fe80::1010]:1234",
|
||||
"nvme", "fe80::1010", .proto = "tcp", .port = 1234 },
|
||||
{ "nvme+tcp://[fe80::1010]:1234/",
|
||||
"nvme", "fe80::1010", .proto = "tcp", .port = 1234 },
|
||||
{ "nvme+tcp://[fe80::1010]:4420/path",
|
||||
"nvme", "fe80::1010", .proto = "tcp", .port = 4420,
|
||||
.path = { "path", NULL }},
|
||||
{ "nvme+tcp://[fe80::1010]/path/",
|
||||
"nvme", "fe80::1010", .proto = "tcp", .path = { "path", NULL }},
|
||||
{ "nvme+tcp://[fe80::1010]:4420/p1/p2/p3",
|
||||
"nvme", "fe80::1010", .proto = "tcp", .port = 4420,
|
||||
.path = { "p1", "p2", "p3", NULL }},
|
||||
{ "nvme+tcp://[fe80::fc7d:8cff:fe5b:962e]:666/p1/p2/p3/",
|
||||
"nvme", "fe80::fc7d:8cff:fe5b:962e", .proto = "tcp", .port = 666,
|
||||
.path = { "p1", "p2", "p3", NULL }},
|
||||
{ "nvme://h?query", "nvme", "h", .query = "query" },
|
||||
{ "nvme://h/?query", "nvme", "h", .query = "query" },
|
||||
{ "nvme://h/x?query",
|
||||
"nvme", "h", .path = { "x" }, .query = "query" },
|
||||
{ "nvme://h/p1/?query",
|
||||
"nvme", "h", .path = { "p1" }, .query = "query" },
|
||||
{ "nvme://h/p1/x?query",
|
||||
"nvme", "h", .path = { "p1", "x" }, .query = "query" },
|
||||
{ "nvme://h#fragment", "nvme", "h", .frag = "fragment" },
|
||||
{ "nvme://h/#fragment", "nvme", "h", .frag = "fragment" },
|
||||
{ "nvme://h/x#fragment",
|
||||
"nvme", "h", .path = { "x" }, .frag = "fragment" },
|
||||
{ "nvme://h/p1/#fragment",
|
||||
"nvme", "h", .path = { "p1" }, .frag = "fragment" },
|
||||
{ "nvme://h/p1/x#fragment",
|
||||
"nvme", "h", .path = { "p1", "x" }, .frag = "fragment" },
|
||||
{ "nvme://h/?query#fragment",
|
||||
"nvme", "h", .query = "query", .frag = "fragment" },
|
||||
{ "nvme://h/x?query#fragment",
|
||||
"nvme", "h", .path = { "x" }, .query = "query", .frag = "fragment" },
|
||||
{ "nvme://h/p1/?query#fragment",
|
||||
"nvme", "h", .path = { "p1" }, .query = "query", .frag = "fragment" },
|
||||
{ "nvme://h/p1/x?query#fragment",
|
||||
"nvme", "h", .path = { "p1", "x" }, .query = "query",
|
||||
.frag = "fragment" },
|
||||
{ "nvme://h/#fragment?query",
|
||||
"nvme", "h", .frag = "fragment?query" },
|
||||
{ "nvme://h/x#fragment?query",
|
||||
"nvme", "h", .path = { "x" }, .frag = "fragment?query" },
|
||||
{ "nvme://h/p1/#fragment?query",
|
||||
"nvme", "h", .path = { "p1" }, .frag = "fragment?query" },
|
||||
{ "nvme://h/p1/x#fragment?query",
|
||||
"nvme", "h", .path = { "p1", "x" }, .frag = "fragment?query" },
|
||||
{ "nvme://user@h", "nvme", "h", .user = "user" },
|
||||
{ "nvme://user@h/", "nvme", "h", .user = "user" },
|
||||
{ "nvme://user:pass@h/", "nvme", "h", .user = "user:pass" },
|
||||
{ "nvme://[fe80::1010]@h/", "nvme", "h", .user = "[fe80::1010]" },
|
||||
{ "nvme://u[fe80::1010]@h/", "nvme", "h", .user = "u[fe80::1010]" },
|
||||
{ "nvme://u[aa:bb::cc]@h/", "nvme", "h", .user = "u[aa:bb::cc]" },
|
||||
{ "nvme+rdma://u[aa:bb::cc]@[aa:bb::cc]:12345/p1/x?q=val#fr",
|
||||
"nvme", "aa:bb::cc", .proto = "rdma", .port = 12345,
|
||||
.user = "u[aa:bb::cc]", .path = { "p1", "x" },
|
||||
.query = "q=val", .frag = "fr" },
|
||||
{ "nvme://ex%5Cmp%3Ae", "nvme", "ex\\mp:e" },
|
||||
{ "nvme://ex%5Cmp%3Ae.com/", "nvme", "ex\\mp:e.com" },
|
||||
{ "nvme://u%24er@ex%5Cmp%3Ae.com/", "nvme", "ex\\mp:e.com",
|
||||
.user = "u$er" },
|
||||
{ "nvme+tcp://ex%5Cmp%3Ae.com:1234",
|
||||
"nvme", "ex\\mp:e.com", .proto = "tcp", .port = 1234 },
|
||||
{ "nvme+tcp://ex%5Cmp%3Ae.com:1234/p1/ex%3Camp%3Ele/p3",
|
||||
"nvme", "ex\\mp:e.com", .proto = "tcp", .port = 1234,
|
||||
.path = { "p1", "ex<amp>le", "p3", NULL } },
|
||||
{ "nvme+tcp://ex%5Cmp%3Ae.com:1234/p1/%3C%3E/p3?q%5E%24ry#fr%26gm%23nt",
|
||||
"nvme", "ex\\mp:e.com", .proto = "tcp", .port = 1234,
|
||||
.path = { "p1", "<>", "p3", NULL }, .query = "q^$ry",
|
||||
.frag = "fr&gm#nt" },
|
||||
};
|
||||
|
||||
const char *test_data_bad[] = {
|
||||
"",
|
||||
" ",
|
||||
"nonsense",
|
||||
"vnme:",
|
||||
"vnme:/",
|
||||
"vnme://",
|
||||
"vnme:///",
|
||||
"vnme+foo://",
|
||||
"nvme:hostname/",
|
||||
"nvme:/hostname/",
|
||||
"nvme:///hostname/",
|
||||
"nvme+foo:///hostname/",
|
||||
};
|
||||
|
||||
static void test_uriparser(void)
|
||||
{
|
||||
printf("Testing URI parser:\n");
|
||||
for (int i = 0; i < ARRAY_SIZE(test_data); i++) {
|
||||
const struct test_data *d = &test_data[i];
|
||||
struct nvme_fabrics_uri *parsed_data;
|
||||
char **s;
|
||||
int i;
|
||||
|
||||
printf(" '%s'...", d->uri);
|
||||
parsed_data = nvme_parse_uri(d->uri);
|
||||
assert(parsed_data);
|
||||
|
||||
assert(strcmp(d->scheme, parsed_data->scheme) == 0);
|
||||
if (d->proto) {
|
||||
assert(parsed_data->protocol != NULL);
|
||||
assert(strcmp(d->proto, parsed_data->protocol) == 0);
|
||||
} else
|
||||
assert(d->proto == parsed_data->protocol);
|
||||
assert(strcmp(d->host, parsed_data->host) == 0);
|
||||
assert(d->port == parsed_data->port);
|
||||
|
||||
if (!parsed_data->path_segments)
|
||||
assert(d->path[0] == NULL);
|
||||
else {
|
||||
for (i = 0, s = parsed_data->path_segments;
|
||||
s && *s; s++, i++) {
|
||||
assert(d->path[i] != NULL);
|
||||
assert(strcmp(d->path[i], *s) == 0);
|
||||
}
|
||||
/* trailing NULL element */
|
||||
assert(d->path[i] == parsed_data->path_segments[i]);
|
||||
}
|
||||
if (d->query) {
|
||||
assert(parsed_data->query != NULL);
|
||||
assert(strcmp(d->query, parsed_data->query) == 0);
|
||||
} else
|
||||
assert(d->query == parsed_data->query);
|
||||
if (d->frag) {
|
||||
assert(parsed_data->fragment != NULL);
|
||||
assert(strcmp(d->frag, parsed_data->fragment) == 0);
|
||||
} else
|
||||
assert(d->frag == parsed_data->fragment);
|
||||
nvme_free_uri(parsed_data);
|
||||
printf(" OK\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void test_uriparser_bad(void)
|
||||
{
|
||||
printf("Testing malformed URI strings:\n");
|
||||
for (int i = 0; i < ARRAY_SIZE(test_data_bad); i++) {
|
||||
struct nvme_fabrics_uri *parsed_data;
|
||||
|
||||
printf(" '%s'...", test_data_bad[i]);
|
||||
parsed_data = nvme_parse_uri(test_data_bad[i]);
|
||||
assert(parsed_data == NULL);
|
||||
printf(" OK\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
test_uriparser();
|
||||
test_uriparser_bad();
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue