Merging upstream version 1.6.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
adbb3a10cc
commit
6add9877e4
871 changed files with 8481 additions and 1502 deletions
460
test/ioctl/discovery.c
Normal file
460
test/ioctl/discovery.c
Normal file
|
@ -0,0 +1,460 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include <libnvme.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/endian/endian.h>
|
||||
|
||||
#include "../../src/nvme/private.h"
|
||||
#include "mock.h"
|
||||
#include "util.h"
|
||||
|
||||
#define TEST_FD 0xFD
|
||||
|
||||
static void arbitrary_ascii_string(size_t max_len, char *str, char *log_str)
|
||||
{
|
||||
size_t len;
|
||||
size_t i;
|
||||
|
||||
len = arbitrary_range(max_len + 1);
|
||||
for (i = 0; i < len; i++) {
|
||||
/*
|
||||
* ASCII strings shall contain only code values 20h through 7Eh.
|
||||
* Exclude 20h (space) because it ends the string.
|
||||
*/
|
||||
str[i] = log_str[i] = arbitrary_range(0x7E - 0x20) + 0x20 + 1;
|
||||
}
|
||||
for (i = len; i < max_len; i++) {
|
||||
str[i] = '\0';
|
||||
log_str[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
static void arbitrary_entry(struct nvmf_disc_log_entry *entry,
|
||||
struct nvmf_disc_log_entry *log_entry)
|
||||
{
|
||||
arbitrary(entry, sizeof(*entry));
|
||||
memcpy(log_entry, entry, sizeof(*entry));
|
||||
arbitrary_ascii_string(
|
||||
sizeof(entry->trsvcid), entry->trsvcid, log_entry->trsvcid);
|
||||
arbitrary_ascii_string(
|
||||
sizeof(entry->traddr), entry->traddr, log_entry->traddr);
|
||||
}
|
||||
|
||||
static void arbitrary_entries(size_t len,
|
||||
struct nvmf_disc_log_entry *entries,
|
||||
struct nvmf_disc_log_entry *log_entries)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
arbitrary_entry(&entries[i], &log_entries[i]);
|
||||
}
|
||||
|
||||
static void test_no_entries(nvme_ctrl_t c)
|
||||
{
|
||||
struct nvmf_discovery_log header = {};
|
||||
/* No entries to fetch after fetching the header */
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header,
|
||||
},
|
||||
};
|
||||
struct nvmf_discovery_log *log = NULL;
|
||||
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvmf_get_discovery_log(c, &log, 1) == 0, "discovery failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, &header, sizeof(header), "incorrect header");
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_four_entries(nvme_ctrl_t c)
|
||||
{
|
||||
size_t num_entries = 4;
|
||||
struct nvmf_disc_log_entry entries[num_entries];
|
||||
struct nvmf_disc_log_entry log_entries[num_entries];
|
||||
struct nvmf_discovery_log header = {.numrec = cpu_to_le64(num_entries)};
|
||||
/*
|
||||
* All 4 entries should be fetched at once
|
||||
* followed by the header again (to ensure genctr hasn't changed)
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(entries),
|
||||
.cdw10 = (sizeof(entries) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.cdw12 = sizeof(header), /* LPOL */
|
||||
.out_data = log_entries,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header,
|
||||
},
|
||||
};
|
||||
struct nvmf_discovery_log *log = NULL;
|
||||
|
||||
arbitrary_entries(num_entries, entries, log_entries);
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvmf_get_discovery_log(c, &log, 1) == 0, "discovery failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, &header, sizeof(header), "incorrect header");
|
||||
cmp(log->entries, entries, sizeof(entries), "incorrect entries");
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_five_entries(nvme_ctrl_t c)
|
||||
{
|
||||
size_t num_entries = 5;
|
||||
struct nvmf_disc_log_entry entries[num_entries];
|
||||
struct nvmf_disc_log_entry log_entries[num_entries];
|
||||
size_t first_entries = 4;
|
||||
size_t first_data_len = first_entries * sizeof(*entries);
|
||||
size_t second_entries = num_entries - first_entries;
|
||||
size_t second_data_len = second_entries * sizeof(*entries);
|
||||
struct nvmf_discovery_log header = {.numrec = cpu_to_le64(num_entries)};
|
||||
/*
|
||||
* The first 4 entries (4 KB) are fetched together,
|
||||
* followed by last entry separately.
|
||||
* Finally, the header is fetched again to check genctr.
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = first_data_len,
|
||||
.cdw10 = (first_data_len / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.cdw12 = sizeof(header), /* LPOL */
|
||||
.out_data = log_entries,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = second_data_len,
|
||||
.cdw10 = (second_data_len / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.cdw12 = sizeof(header) + first_data_len, /* LPOL */
|
||||
.out_data = log_entries + first_entries,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header,
|
||||
},
|
||||
};
|
||||
struct nvmf_discovery_log *log = NULL;
|
||||
|
||||
arbitrary_entries(num_entries, entries, log_entries);
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvmf_get_discovery_log(c, &log, 1) == 0, "discovery failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, &header, sizeof(header), "incorrect header");
|
||||
cmp(log->entries, entries, sizeof(entries), "incorrect entries");
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_genctr_change(nvme_ctrl_t c)
|
||||
{
|
||||
struct nvmf_disc_log_entry entries1[1];
|
||||
struct nvmf_discovery_log header1 = {
|
||||
.numrec = cpu_to_le64(ARRAY_SIZE(entries1)),
|
||||
};
|
||||
size_t num_entries2 = 2;
|
||||
struct nvmf_disc_log_entry entries2[num_entries2];
|
||||
struct nvmf_disc_log_entry log_entries2[num_entries2];
|
||||
struct nvmf_discovery_log header2 = {
|
||||
.genctr = cpu_to_le64(1),
|
||||
.numrec = cpu_to_le64(num_entries2),
|
||||
};
|
||||
/*
|
||||
* genctr changes after the entries are fetched the first time,
|
||||
* so the log page fetch is retried
|
||||
*/
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header1),
|
||||
.cdw10 = (sizeof(header1) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header1,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(entries1),
|
||||
.cdw10 = (sizeof(entries1) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* NUMDL */
|
||||
.cdw12 = sizeof(header1), /* LPOL */
|
||||
.out_data = entries1,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header2),
|
||||
.cdw10 = (sizeof(header2) / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header2,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header2),
|
||||
.cdw10 = (sizeof(header2) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header2,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(entries2),
|
||||
.cdw10 = (sizeof(entries2) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.cdw12 = sizeof(header2), /* LPOL */
|
||||
.out_data = log_entries2,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header2),
|
||||
.cdw10 = (sizeof(header2) / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header2,
|
||||
},
|
||||
};
|
||||
struct nvmf_discovery_log *log = NULL;
|
||||
|
||||
arbitrary(entries1, sizeof(entries1));
|
||||
arbitrary_entries(num_entries2, entries2, log_entries2);
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvmf_get_discovery_log(c, &log, 2) == 0, "discovery failed: %m");
|
||||
end_mock_cmds();
|
||||
cmp(log, &header2, sizeof(header2), "incorrect header");
|
||||
cmp(log->entries, entries2, sizeof(entries2), "incorrect entries");
|
||||
free(log);
|
||||
}
|
||||
|
||||
static void test_max_retries(nvme_ctrl_t c)
|
||||
{
|
||||
struct nvmf_disc_log_entry entry;
|
||||
struct nvmf_discovery_log header1 = {.numrec = cpu_to_le64(1)};
|
||||
struct nvmf_discovery_log header2 = {
|
||||
.genctr = cpu_to_le64(1),
|
||||
.numrec = cpu_to_le64(1),
|
||||
};
|
||||
struct nvmf_discovery_log header3 = {
|
||||
.genctr = cpu_to_le64(2),
|
||||
.numrec = cpu_to_le64(1),
|
||||
};
|
||||
/* genctr changes in both attempts, hitting the max retries (2) */
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header1),
|
||||
.cdw10 = (sizeof(header1) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header1,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(entry),
|
||||
.cdw10 = (sizeof(entry) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.cdw12 = sizeof(header1), /* LPOL */
|
||||
.out_data = &entry,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header2),
|
||||
.cdw10 = (sizeof(header2) / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header2,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header2),
|
||||
.cdw10 = (sizeof(header2) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header2,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(entry),
|
||||
.cdw10 = (sizeof(entry) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.cdw12 = sizeof(header2), /* LPOL */
|
||||
.out_data = &entry,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header3),
|
||||
.cdw10 = (sizeof(header3) / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header3,
|
||||
},
|
||||
};
|
||||
struct nvmf_discovery_log *log = NULL;
|
||||
|
||||
arbitrary(&entry, sizeof(entry));
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvmf_get_discovery_log(c, &log, 2) == -1, "discovery succeeded");
|
||||
end_mock_cmds();
|
||||
check(errno == EAGAIN, "discovery failed: %m");
|
||||
check(!log, "unexpected log page returned");
|
||||
}
|
||||
|
||||
static void test_header_error(nvme_ctrl_t c)
|
||||
{
|
||||
size_t header_size = sizeof(struct nvmf_discovery_log);
|
||||
/* Stop after an error in fetching the header the first time */
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = header_size,
|
||||
.cdw10 = (header_size / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.err = NVME_SC_INVALID_OPCODE,
|
||||
},
|
||||
};
|
||||
struct nvmf_discovery_log *log = NULL;
|
||||
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvmf_get_discovery_log(c, &log, 1) == -1, "discovery succeeded");
|
||||
end_mock_cmds();
|
||||
check(!log, "unexpected log page returned");
|
||||
}
|
||||
|
||||
static void test_entries_error(nvme_ctrl_t c)
|
||||
{
|
||||
struct nvmf_discovery_log header = {.numrec = cpu_to_le64(1)};
|
||||
size_t entry_size = sizeof(struct nvmf_disc_log_entry);
|
||||
/* Stop after an error in fetching the entries */
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = entry_size,
|
||||
.cdw10 = (entry_size / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.cdw12 = sizeof(header), /* LPOL */
|
||||
.err = -EIO,
|
||||
},
|
||||
};
|
||||
struct nvmf_discovery_log *log = NULL;
|
||||
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvmf_get_discovery_log(c, &log, 1) == -1, "discovery succeeded");
|
||||
end_mock_cmds();
|
||||
check(errno == EIO, "discovery failed: %m");
|
||||
check(!log, "unexpected log page returned");
|
||||
}
|
||||
|
||||
static void test_genctr_error(nvme_ctrl_t c)
|
||||
{
|
||||
struct nvmf_disc_log_entry entry;
|
||||
struct nvmf_discovery_log header = {.numrec = cpu_to_le64(1)};
|
||||
/* Stop after an error in refetching the header */
|
||||
struct mock_cmd mock_admin_cmds[] = {
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.out_data = &header,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(entry),
|
||||
.cdw10 = (sizeof(entry) / 4 - 1) << 16 /* NUMDL */
|
||||
| 1 << 15 /* RAE */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.cdw12 = sizeof(header), /* LPOL */
|
||||
.out_data = &entry,
|
||||
},
|
||||
{
|
||||
.opcode = nvme_admin_get_log_page,
|
||||
.data_len = sizeof(header),
|
||||
.cdw10 = (sizeof(header) / 4 - 1) << 16 /* NUMDL */
|
||||
| NVME_LOG_LID_DISCOVER, /* LID */
|
||||
.err = NVME_SC_INTERNAL,
|
||||
},
|
||||
};
|
||||
struct nvmf_discovery_log *log = NULL;
|
||||
|
||||
arbitrary(&entry, sizeof(entry));
|
||||
set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
|
||||
check(nvmf_get_discovery_log(c, &log, 1) == -1, "discovery succeeded");
|
||||
end_mock_cmds();
|
||||
check(!log, "unexpected log page returned");
|
||||
}
|
||||
|
||||
static void run_test(const char *test_name, void (*test_fn)(nvme_ctrl_t))
|
||||
{
|
||||
struct nvme_ctrl c = {.fd = TEST_FD};
|
||||
|
||||
printf("Running test %s...", test_name);
|
||||
fflush(stdout);
|
||||
check(asprintf(&c.name, "%s_ctrl", test_name) >= 0, "asprintf() failed");
|
||||
test_fn(&c);
|
||||
free(c.name);
|
||||
puts(" OK");
|
||||
}
|
||||
|
||||
#define RUN_TEST(name) run_test(#name, test_ ## name)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
set_mock_fd(TEST_FD);
|
||||
RUN_TEST(no_entries);
|
||||
RUN_TEST(four_entries);
|
||||
RUN_TEST(five_entries);
|
||||
RUN_TEST(genctr_change);
|
||||
RUN_TEST(max_retries);
|
||||
RUN_TEST(header_error);
|
||||
RUN_TEST(entries_error);
|
||||
RUN_TEST(genctr_error);
|
||||
}
|
1604
test/ioctl/features.c
Normal file
1604
test/ioctl/features.c
Normal file
File diff suppressed because it is too large
Load diff
572
test/ioctl/identify.c
Normal file
572
test/ioctl/identify.c
Normal file
|
@ -0,0 +1,572 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include <libnvme.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mock.h"
|
||||
#include "util.h"
|
||||
|
||||
#define TEST_FD 0xFD
|
||||
#define TEST_NSID 0x12345678
|
||||
#define TEST_NVMSETID 0xABCD
|
||||
#define TEST_UUID 123
|
||||
#define TEST_CSI NVME_CSI_KV
|
||||
#define TEST_CNTID 0x4321
|
||||
#define TEST_DOMID 0xFEDC
|
||||
#define TEST_ENDGID 0x0123
|
||||
#define TEST_SC NVME_SC_INVALID_FIELD
|
||||
|
||||
static void test_ns(void)
|
||||
{
|
||||
struct nvme_id_ns expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_NS,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_ns(TEST_FD, TEST_NSID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_ctrl(void)
|
||||
{
|
||||
struct nvme_id_ctrl expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_CTRL,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_ctrl(TEST_FD, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_active_ns_list(void)
|
||||
{
|
||||
struct nvme_ns_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_NS_ACTIVE_LIST,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_active_ns_list(TEST_FD, TEST_NSID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_ns_descs(void)
|
||||
{
|
||||
uint8_t expected_id[NVME_IDENTIFY_DATA_SIZE];
|
||||
struct nvme_ns_id_desc *id;
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_NS_DESC_LIST,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(expected_id, sizeof(expected_id));
|
||||
id = calloc(1, NVME_IDENTIFY_DATA_SIZE);
|
||||
check(id, "memory allocation failed");
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_ns_descs(TEST_FD, TEST_NSID, id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(id, expected_id, sizeof(expected_id), "incorrect identify data");
|
||||
free(id);
|
||||
}
|
||||
|
||||
static void test_nvmset_list(void)
|
||||
{
|
||||
struct nvme_id_nvmset_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_NVMSET_LIST,
|
||||
.cdw11 = TEST_NVMSETID,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_nvmset_list(TEST_FD, TEST_NVMSETID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_ns_csi(void)
|
||||
{
|
||||
uint8_t expected_id[NVME_IDENTIFY_DATA_SIZE];
|
||||
uint8_t id[NVME_IDENTIFY_DATA_SIZE] = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_CSI_NS,
|
||||
.cdw11 = TEST_CSI << 24,
|
||||
.cdw14 = TEST_UUID,
|
||||
.out_data = expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_ns_csi(TEST_FD, TEST_NSID, TEST_UUID, TEST_CSI, id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(id, expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_zns_identify_ns(void)
|
||||
{
|
||||
struct nvme_zns_id_ns expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_CSI_NS,
|
||||
.cdw11 = NVME_CSI_ZNS << 24,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_zns_identify_ns(TEST_FD, TEST_NSID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_nvm_identify_ctrl(void)
|
||||
{
|
||||
struct nvme_id_ctrl_nvm expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_CSI_CTRL,
|
||||
.cdw11 = NVME_CSI_NVM << 24,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_nvm_identify_ctrl(TEST_FD, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_zns_identify_ctrl(void)
|
||||
{
|
||||
struct nvme_zns_id_ctrl expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_CSI_CTRL,
|
||||
.cdw11 = NVME_CSI_ZNS << 24,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_zns_identify_ctrl(TEST_FD, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_active_ns_list_csi(void)
|
||||
{
|
||||
struct nvme_ns_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST,
|
||||
.cdw11 = TEST_CSI << 24,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_active_ns_list_csi(
|
||||
TEST_FD, TEST_NSID, TEST_CSI, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_independent_identify_ns(void)
|
||||
{
|
||||
struct nvme_id_independent_id_ns expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
/* That's a mouthful! */
|
||||
err = nvme_identify_independent_identify_ns(TEST_FD, TEST_NSID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_allocated_ns_list(void)
|
||||
{
|
||||
struct nvme_ns_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_allocated_ns_list(TEST_FD, TEST_NSID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_allocated_ns(void)
|
||||
{
|
||||
struct nvme_id_ns expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_ALLOCATED_NS,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_allocated_ns(TEST_FD, TEST_NSID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_nsid_ctrl_list(void)
|
||||
{
|
||||
struct nvme_ctrl_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = TEST_CNTID << 16
|
||||
| NVME_IDENTIFY_CNS_NS_CTRL_LIST,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_nsid_ctrl_list(TEST_FD, TEST_NSID, TEST_CNTID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_ctrl_list(void)
|
||||
{
|
||||
struct nvme_ctrl_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = TEST_CNTID << 16
|
||||
| NVME_IDENTIFY_CNS_CTRL_LIST,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_ctrl_list(TEST_FD, TEST_CNTID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_primary_ctrl(void)
|
||||
{
|
||||
struct nvme_primary_ctrl_cap expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = TEST_CNTID << 16
|
||||
| NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_primary_ctrl(TEST_FD, TEST_CNTID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_secondary_ctrl_list(void)
|
||||
{
|
||||
struct nvme_secondary_ctrl_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = TEST_CNTID << 16
|
||||
| NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_secondary_ctrl_list(TEST_FD, TEST_CNTID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_ns_granularity(void)
|
||||
{
|
||||
struct nvme_id_ns_granularity_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_NS_GRANULARITY,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_ns_granularity(TEST_FD, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_uuid(void)
|
||||
{
|
||||
struct nvme_id_uuid_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_UUID_LIST,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_uuid(TEST_FD, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_domain_list(void)
|
||||
{
|
||||
struct nvme_id_domain_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_DOMAIN_LIST,
|
||||
.cdw11 = TEST_DOMID,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_domain_list(TEST_FD, TEST_DOMID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_endurance_group_list(void)
|
||||
{
|
||||
struct nvme_id_endurance_group_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID,
|
||||
.cdw11 = TEST_ENDGID,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_endurance_group_list(TEST_FD, TEST_ENDGID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_allocated_ns_list_csi(void)
|
||||
{
|
||||
struct nvme_ns_list expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST,
|
||||
.cdw11 = TEST_CSI << 24,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_allocated_ns_list_csi(
|
||||
TEST_FD, TEST_NSID, TEST_CSI, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
static void test_iocs(void)
|
||||
{
|
||||
struct nvme_id_iocs expected_id, id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(expected_id),
|
||||
.cdw10 = TEST_CNTID << 16
|
||||
| NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE,
|
||||
.out_data = &expected_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
arbitrary(&expected_id, sizeof(expected_id));
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_iocs(TEST_FD, TEST_CNTID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == 0, "identify returned error %d, errno %m", err);
|
||||
cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
|
||||
}
|
||||
|
||||
/*
|
||||
* All identify functions tail-call nvme_identify(),
|
||||
* so testing errors in any of them will do
|
||||
*/
|
||||
|
||||
static void test_status_code_error(void)
|
||||
{
|
||||
struct nvme_id_nvmset_list id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.data_len = sizeof(id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_NVMSET_LIST,
|
||||
.cdw11 = TEST_NVMSETID,
|
||||
.err = TEST_SC,
|
||||
};
|
||||
int err;
|
||||
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_nvmset_list(TEST_FD, TEST_NVMSETID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == TEST_SC, "got error %d, expected %d", err, TEST_SC);
|
||||
}
|
||||
|
||||
static void test_kernel_error(void)
|
||||
{
|
||||
struct nvme_id_ns id = {};
|
||||
struct mock_cmd mock_admin_cmd = {
|
||||
.opcode = nvme_admin_identify,
|
||||
.nsid = TEST_NSID,
|
||||
.data_len = sizeof(id),
|
||||
.cdw10 = NVME_IDENTIFY_CNS_NS,
|
||||
.err = -EIO,
|
||||
};
|
||||
int err;
|
||||
|
||||
set_mock_admin_cmds(&mock_admin_cmd, 1);
|
||||
err = nvme_identify_ns(TEST_FD, TEST_NSID, &id);
|
||||
end_mock_cmds();
|
||||
check(err == -1, "got error %d, expected -1", err);
|
||||
check(errno == EIO, "unexpected error %m");
|
||||
}
|
||||
|
||||
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(ns);
|
||||
RUN_TEST(ctrl);
|
||||
RUN_TEST(active_ns_list);
|
||||
RUN_TEST(ns_descs);
|
||||
RUN_TEST(nvmset_list);
|
||||
RUN_TEST(ns_csi);
|
||||
RUN_TEST(zns_identify_ns);
|
||||
RUN_TEST(nvm_identify_ctrl);
|
||||
RUN_TEST(zns_identify_ctrl);
|
||||
RUN_TEST(active_ns_list_csi);
|
||||
RUN_TEST(independent_identify_ns);
|
||||
RUN_TEST(allocated_ns_list);
|
||||
RUN_TEST(allocated_ns);
|
||||
RUN_TEST(nsid_ctrl_list);
|
||||
RUN_TEST(ctrl_list);
|
||||
RUN_TEST(primary_ctrl);
|
||||
RUN_TEST(secondary_ctrl_list);
|
||||
RUN_TEST(ns_granularity);
|
||||
RUN_TEST(uuid);
|
||||
RUN_TEST(domain_list);
|
||||
RUN_TEST(endurance_group_list);
|
||||
RUN_TEST(allocated_ns_list_csi);
|
||||
RUN_TEST(iocs);
|
||||
RUN_TEST(status_code_error);
|
||||
RUN_TEST(kernel_error);
|
||||
}
|
32
test/ioctl/meson.build
Normal file
32
test/ioctl/meson.build
Normal file
|
@ -0,0 +1,32 @@
|
|||
mock_ioctl = library(
|
||||
'mock-ioctl',
|
||||
['mock.c', 'util.c'],
|
||||
)
|
||||
|
||||
discovery = executable(
|
||||
'test-discovery',
|
||||
'discovery.c',
|
||||
dependencies: libnvme_dep,
|
||||
include_directories: [incdir, internal_incdir],
|
||||
link_with: mock_ioctl,
|
||||
)
|
||||
|
||||
test('discovery', discovery, env: ['LD_PRELOAD=' + mock_ioctl.full_path()])
|
||||
|
||||
features = executable(
|
||||
'test-features',
|
||||
'features.c',
|
||||
dependencies: libnvme_dep,
|
||||
link_with: mock_ioctl,
|
||||
)
|
||||
|
||||
test('features', features, env: ['LD_PRELOAD=' + mock_ioctl.full_path()])
|
||||
|
||||
identify = executable(
|
||||
'test-identify',
|
||||
'identify.c',
|
||||
dependencies: libnvme_dep,
|
||||
link_with: mock_ioctl,
|
||||
)
|
||||
|
||||
test('identify', identify, env: ['LD_PRELOAD=' + mock_ioctl.full_path()])
|
168
test/ioctl/mock.c
Normal file
168
test/ioctl/mock.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "mock.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "../../src/nvme/ioctl.h"
|
||||
#include "util.h"
|
||||
|
||||
struct mock_cmds {
|
||||
const char *name;
|
||||
const struct mock_cmd *cmds;
|
||||
size_t remaining_cmds;
|
||||
};
|
||||
|
||||
static int mock_fd = -1;
|
||||
static struct mock_cmds mock_admin_cmds = {.name = "admin"};
|
||||
static struct mock_cmds mock_io_cmds = {.name = "IO"};
|
||||
|
||||
static void set_mock_cmds(
|
||||
struct mock_cmds *mock_cmds, const struct mock_cmd *cmds, size_t len)
|
||||
{
|
||||
mock_cmds->cmds = cmds;
|
||||
mock_cmds->remaining_cmds = len;
|
||||
}
|
||||
|
||||
static void mock_cmds_done(const struct mock_cmds *mock_cmds)
|
||||
{
|
||||
check(!mock_cmds->remaining_cmds,
|
||||
"%zu %s commands not executed",
|
||||
mock_cmds->remaining_cmds, mock_cmds->name);
|
||||
}
|
||||
|
||||
void set_mock_fd(int fd)
|
||||
{
|
||||
mock_fd = fd;
|
||||
}
|
||||
|
||||
void set_mock_admin_cmds(const struct mock_cmd *cmds, size_t len)
|
||||
{
|
||||
set_mock_cmds(&mock_admin_cmds, cmds, len);
|
||||
}
|
||||
|
||||
void set_mock_io_cmds(const struct mock_cmd *cmds, size_t len)
|
||||
{
|
||||
set_mock_cmds(&mock_io_cmds, cmds, len);
|
||||
}
|
||||
|
||||
void end_mock_cmds(void)
|
||||
{
|
||||
mock_cmds_done(&mock_admin_cmds);
|
||||
mock_cmds_done(&mock_io_cmds);
|
||||
}
|
||||
|
||||
#define execute_ioctl(cmd, mock_cmd) ({ \
|
||||
check((cmd)->opcode == (mock_cmd)->opcode, \
|
||||
"got opcode %" PRIu8 ", expected %" PRIu8, \
|
||||
(cmd)->opcode, (mock_cmd)->opcode); \
|
||||
check((cmd)->flags == (mock_cmd)->flags, \
|
||||
"got flags %" PRIu8 ", expected %" PRIu8, \
|
||||
(cmd)->flags, (mock_cmd)->flags); \
|
||||
check((cmd)->nsid == (mock_cmd)->nsid, \
|
||||
"got nsid %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->nsid, (mock_cmd)->nsid); \
|
||||
check((cmd)->cdw2 == (mock_cmd)->cdw2, \
|
||||
"got cdw2 %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->cdw2, (mock_cmd)->cdw2); \
|
||||
check((cmd)->cdw3 == (mock_cmd)->cdw3, \
|
||||
"got cdw3 %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->cdw3, (mock_cmd)->cdw3); \
|
||||
check((cmd)->metadata_len == (mock_cmd)->metadata_len, \
|
||||
"got metadata_len %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->metadata_len, (mock_cmd)->metadata_len); \
|
||||
cmp((void const *)(uintptr_t)(cmd)->metadata, \
|
||||
(mock_cmd)->metadata, \
|
||||
(cmd)->metadata_len, \
|
||||
"incorrect metadata"); \
|
||||
__u32 data_len = (cmd)->data_len; \
|
||||
check(data_len == (mock_cmd)->data_len, \
|
||||
"got data_len %" PRIu32 ", expected %" PRIu32, \
|
||||
data_len, (mock_cmd)->data_len); \
|
||||
void *data = (void *)(uintptr_t)(cmd)->addr; \
|
||||
if ((mock_cmd)->in_data) { \
|
||||
cmp(data, (mock_cmd)->in_data, data_len, "incorrect data"); \
|
||||
} \
|
||||
check((cmd)->cdw10 == (mock_cmd)->cdw10, \
|
||||
"got cdw10 %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->cdw10, (mock_cmd)->cdw10); \
|
||||
check((cmd)->cdw11 == (mock_cmd)->cdw11, \
|
||||
"got cdw11 %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->cdw11, (mock_cmd)->cdw11); \
|
||||
check((cmd)->cdw12 == (mock_cmd)->cdw12, \
|
||||
"got cdw12 %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->cdw12, (mock_cmd)->cdw12); \
|
||||
check((cmd)->cdw13 == (mock_cmd)->cdw13, \
|
||||
"got cdw13 %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->cdw13, (mock_cmd)->cdw13); \
|
||||
check((cmd)->cdw14 == (mock_cmd)->cdw14, \
|
||||
"got cdw14 %" PRIu32 ", expected %" PRIu32, \
|
||||
(cmd)->cdw14, (mock_cmd)->cdw14); \
|
||||
check((cmd)->cdw15 == (mock_cmd)->cdw15, \
|
||||
"got cdw15 %" PRIu32 ", expected %" PRIu32, \
|
||||
(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); \
|
||||
} \
|
||||
})
|
||||
|
||||
int ioctl(int fd, unsigned long request, ...)
|
||||
{
|
||||
struct mock_cmds *mock_cmds;
|
||||
bool result64;
|
||||
const struct mock_cmd *mock_cmd;
|
||||
va_list args;
|
||||
void *cmd;
|
||||
|
||||
check(fd == mock_fd, "got fd %d, expected %d", fd, mock_fd);
|
||||
switch (request) {
|
||||
case NVME_IOCTL_ADMIN_CMD:
|
||||
mock_cmds = &mock_admin_cmds;
|
||||
result64 = false;
|
||||
break;
|
||||
case NVME_IOCTL_ADMIN64_CMD:
|
||||
mock_cmds = &mock_admin_cmds;
|
||||
result64 = true;
|
||||
break;
|
||||
case NVME_IOCTL_IO_CMD:
|
||||
mock_cmds = &mock_io_cmds;
|
||||
result64 = false;
|
||||
break;
|
||||
case NVME_IOCTL_IO64_CMD:
|
||||
mock_cmds = &mock_io_cmds;
|
||||
result64 = true;
|
||||
break;
|
||||
default:
|
||||
fail("unexpected %s %lu", __func__, request);
|
||||
}
|
||||
check(mock_cmds->remaining_cmds,
|
||||
"unexpected %s command", mock_cmds->name);
|
||||
mock_cmd = mock_cmds->cmds++;
|
||||
mock_cmds->remaining_cmds--;
|
||||
|
||||
va_start(args, request);
|
||||
cmd = va_arg(args, void *);
|
||||
va_end(args);
|
||||
if (result64) {
|
||||
execute_ioctl((struct nvme_passthru_cmd64 *)cmd, mock_cmd);
|
||||
} else {
|
||||
check((uint32_t)mock_cmd->result == mock_cmd->result,
|
||||
"expected 64-bit %s for result %" PRIu64,
|
||||
__func__, mock_cmd->result);
|
||||
execute_ioctl((struct nvme_passthru_cmd *)cmd, mock_cmd);
|
||||
}
|
||||
if (mock_cmd->err < 0) {
|
||||
errno = -mock_cmd->err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return mock_cmd->err;
|
||||
}
|
104
test/ioctl/mock.h
Normal file
104
test/ioctl/mock.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#ifndef _LIBNVME_TEST_IOCTL_MOCK_H
|
||||
#define _LIBNVME_TEST_IOCTL_MOCK_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* struct mock_cmd - a mock NVMe passthru ioctl() invocation
|
||||
* @opcode: the expected `opcode` passed to ioctl()
|
||||
* @flags: the expected `flags` passed to ioctl()
|
||||
* @nsid: the expected `nsid` passed to ioctl()
|
||||
* @cdw2: the expected `cdw2` passed to ioctl()
|
||||
* @cdw3: the expected `cdw3` passed to ioctl()
|
||||
* @metadata: the expected `metadata` of length `metadata_len` passed to ioctl()
|
||||
* @in_data: the expected `addr` of length `data_len` passed to ioctl().
|
||||
* Set this to NULL to skip checking the data,
|
||||
* for example if the command is in the read direction.
|
||||
* @metadata_len: the expected `metadata_len` passed to ioctl()
|
||||
* @data_len: the expected `data_len` passed to ioctl()
|
||||
* @cdw10: the expected `cdw10` passed to ioctl()
|
||||
* @cdw11: the expected `cdw11` passed to ioctl()
|
||||
* @cdw12: the expected `cdw12` passed to ioctl()
|
||||
* @cdw13: the expected `cdw13` passed to ioctl()
|
||||
* @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`
|
||||
* @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`.
|
||||
* Otherwise, ioctl() returns `err`, representing a NVMe status code.
|
||||
*/
|
||||
struct mock_cmd {
|
||||
uint8_t opcode;
|
||||
uint8_t flags;
|
||||
uint32_t nsid;
|
||||
uint32_t cdw2;
|
||||
uint32_t cdw3;
|
||||
const void *metadata;
|
||||
const void *in_data;
|
||||
uint32_t metadata_len;
|
||||
uint32_t data_len;
|
||||
uint32_t cdw10;
|
||||
uint32_t cdw11;
|
||||
uint32_t cdw12;
|
||||
uint32_t cdw13;
|
||||
uint32_t cdw14;
|
||||
uint32_t cdw15;
|
||||
uint32_t timeout_ms;
|
||||
const void *out_data;
|
||||
uint64_t result;
|
||||
int err;
|
||||
};
|
||||
|
||||
/**
|
||||
* set_mock_fd() - sets the expected file descriptor for NVMe passthru ioctls()
|
||||
* @fd: file descriptor expected to be passed to ioctl()
|
||||
*/
|
||||
void set_mock_fd(int fd);
|
||||
|
||||
/**
|
||||
* set_mock_admin_cmds() - mocks NVMe admin passthru ioctl() invocations
|
||||
* @cmds: pointer to start of the mock_cmd slice
|
||||
* @len: length of the mock_cmd slice (number of ioctl() invocations)
|
||||
*
|
||||
* Provides a sequence of mocks for NVMe admin passthru ioctl() invocations.
|
||||
* Each ioctl() consumes the next mock from the sequence.
|
||||
* Its arguments are checked against the mock's expected arguments,
|
||||
* aborting the process if unexpected arguments are passed.
|
||||
* The mock results (return value, NVMe result and data)
|
||||
* are returned from the ioctl().
|
||||
*
|
||||
* Analogous to set_mock_io_cmds(), but for admin commands.
|
||||
* Both admin and IO mocks can be active at the same time.
|
||||
*/
|
||||
void set_mock_admin_cmds(const struct mock_cmd *cmds, size_t len);
|
||||
|
||||
/**
|
||||
* set_mock_io_cmds() - mocks NVMe IO passthru ioctl() invocations
|
||||
* @cmds: pointer to start of the mock_cmd slice
|
||||
* @len: length of the mock_cmd slice (number of ioctl() invocations)
|
||||
*
|
||||
* Provides a sequence of mocks for NVMe IO passthru ioctl() invocations.
|
||||
* Each ioctl() consumes the next mock from the sequence.
|
||||
* Its arguments are checked against the mock's expected arguments,
|
||||
* aborting the process if unexpected arguments are passed.
|
||||
* The mock results (return value, NVMe result and data)
|
||||
* are returned from the ioctl().
|
||||
*
|
||||
* Analogous to set_mock_admin_cmds(), but for IO commands.
|
||||
* Both admin and IO mocks can be active at the same time.
|
||||
*/
|
||||
void set_mock_io_cmds(const struct mock_cmd *cmds, size_t len);
|
||||
|
||||
/**
|
||||
* end_mock_cmds() - finishes mocking NVMe passthru ioctl() invocations
|
||||
*
|
||||
* Checks that all mock ioctl() invocations were performed.
|
||||
*/
|
||||
void end_mock_cmds(void);
|
||||
|
||||
#endif /* #ifndef _LIBNVME_TEST_IOCTL_MOCK_H */
|
65
test/ioctl/util.c
Normal file
65
test/ioctl/util.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void hexdump(const uint8_t *buf, size_t len)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
fprintf(stderr, "%02X", buf[i++]);
|
||||
if (i >= len)
|
||||
break;
|
||||
|
||||
fputc(i % 16 > 0 ? ' ' : '\n', stderr);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
void fail(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
fputc('\n', stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
void cmp(const void *actual, const void *expected, size_t len, const char *msg)
|
||||
{
|
||||
if (memcmp(actual, expected, len) == 0)
|
||||
return;
|
||||
|
||||
fputs(msg, stderr);
|
||||
fputs("\nactual:\n", stderr);
|
||||
hexdump(actual, len);
|
||||
fputs("expected:\n", stderr);
|
||||
hexdump(expected, len);
|
||||
abort();
|
||||
}
|
||||
|
||||
void arbitrary(void *buf_, size_t len)
|
||||
{
|
||||
uint8_t *buf = buf_;
|
||||
|
||||
while (len--)
|
||||
*(buf++) = rand();
|
||||
}
|
||||
|
||||
size_t arbitrary_range(size_t max)
|
||||
{
|
||||
size_t value;
|
||||
arbitrary(&value, sizeof(value));
|
||||
return value % max;
|
||||
}
|
19
test/ioctl/util.h
Normal file
19
test/ioctl/util.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#ifndef _LIBNVME_TEST_IOCTL_UTIL_H
|
||||
#define _LIBNVME_TEST_IOCTL_UTIL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdnoreturn.h>
|
||||
|
||||
noreturn void fail(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
#define check(condition, fmt...) ((condition) || (fail(fmt), 0))
|
||||
|
||||
void cmp(const void *actual, const void *expected, size_t len, const char *msg);
|
||||
|
||||
void arbitrary(void *buf, size_t len);
|
||||
|
||||
size_t arbitrary_range(size_t max);
|
||||
|
||||
#endif /* #ifndef _LIBNVME_TEST_IOCTL_UTIL_H */
|
Loading…
Add table
Add a link
Reference in a new issue