Page MenuHomeFreeBSD

D61557.1794767938.diff
No OneTemporary

Authored By
Unknown
Size
8 KB
Referenced Files
None
Subscribers
None

D61557.1794767938.diff

diff --git a/sys/dev/nvmf/controller/ctl_frontend_nvmf.c b/sys/dev/nvmf/controller/ctl_frontend_nvmf.c
--- a/sys/dev/nvmf/controller/ctl_frontend_nvmf.c
+++ b/sys/dev/nvmf/controller/ctl_frontend_nvmf.c
@@ -728,6 +728,7 @@
* ioccsz: Maximum command capsule size
* iorcsz: Maximum response capsule size
* nn: Number of namespaces
+ * mdts: Maximum data transfer size (in bytes)
*/
static void
nvmft_port_create(struct ctl_req *req)
@@ -736,7 +737,10 @@
struct ctl_port *port;
const char *serial, *subnqn;
char serial_buf[NVME_SERIAL_NUMBER_LENGTH];
- u_long enable_timeout, hostid, ioccsz, iorcsz, max_io_qsize, nn, portid;
+ uint64_t cap;
+ u_long enable_timeout, hostid, ioccsz, iorcsz, max_io_qsize;
+ u_long max_data_xfer_size, nn, portid;
+ u_int mps, max_mdts;
int error;
/* Required parameters. */
@@ -781,6 +785,25 @@
return;
}
+ /*
+ * Compute the maximum MDTS value assuming the expanded value
+ * has to fit in a size_t.
+ */
+ cap = _nvmf_controller_cap(max_io_qsize, enable_timeout / 500);
+ mps = NVMEV(NVME_CAP_HI_REG_MPSMIN, cap >> 32);
+ max_mdts = ilog2(SIZE_MAX) - (mps + NVME_MPS_SHIFT);
+
+ if (!dnvlist_get_strnum(req->args_nvl, "mdts", NVMF_MDTS,
+ &max_data_xfer_size) || !powerof2(max_data_xfer_size) ||
+ (max_data_xfer_size != 0 &&
+ max_data_xfer_size < (1ul << (mps + NVME_MPS_SHIFT))) ||
+ max_data_xfer_size > (1ul << max_mdts)) {
+ req->status = CTL_LUN_ERROR;
+ snprintf(req->error_str, sizeof(req->error_str),
+ "Invalid maximum data transfer size");
+ return;
+ }
+
if (!dnvlist_get_strnum(req->args_nvl, "ioccsz", NVMF_IOCCSZ,
&ioccsz) || ioccsz < sizeof(struct nvme_command) ||
(ioccsz % 16) != 0) {
@@ -829,7 +852,8 @@
refcount_init(&np->refs, 1);
np->portid = portid;
np->max_io_qsize = max_io_qsize;
- np->cap = _nvmf_controller_cap(max_io_qsize, enable_timeout / 500);
+ np->max_data_xfer_size = max_data_xfer_size;
+ np->cap = cap;
mtx_init(&np->lock, "nvmft port", NULL, MTX_DEF);
np->ids = new_unrhdr(0, MIN(CTL_MAX_INIT_PER_PORT - 1,
NVMF_CNTLID_STATIC_MAX), UNR_NO_MTX);
@@ -845,6 +869,10 @@
NVMEF(NVME_CTRLR_DATA_ONCS_DSM, 1) |
NVMEF(NVME_CTRLR_DATA_ONCS_COMPARE, 1));
np->cdata.fuses = NVMEF(NVME_CTRLR_DATA_FUSES_CNW, 1);
+ if (max_data_xfer_size != 0) {
+ np->cdata.mdts = ilog2(max_data_xfer_size) -
+ (mps + NVME_MPS_SHIFT);
+ }
np->fp.afi = NVMEF(NVME_FIRMWARE_PAGE_AFI_SLOT, 1);
memcpy(np->fp.revision[0], np->cdata.fr, sizeof(np->cdata.fr));
diff --git a/sys/dev/nvmf/controller/nvmft_controller.c b/sys/dev/nvmf/controller/nvmft_controller.c
--- a/sys/dev/nvmf/controller/nvmft_controller.c
+++ b/sys/dev/nvmf/controller/nvmft_controller.c
@@ -631,7 +631,13 @@
goto done;
}
- len = (numd + 1) * 4;
+ if (ctrlr->np->max_data_xfer_size != 0 &&
+ numd > ctrlr->np->max_data_xfer_size / 4 - 1) {
+ status = NVME_SC_INVALID_FIELD;
+ goto done;
+ }
+
+ len = ((size_t)numd + 1) * 4;
switch (lid) {
case NVME_LOG_ERROR:
@@ -1128,6 +1134,20 @@
atomic_store_int(&ctrlr->ka_active_traffic, 1);
+ switch (cmd->opc) {
+ case NVME_OPC_WRITE:
+ case NVME_OPC_READ:
+ case NVME_OPC_COMPARE:
+ case NVME_OPC_DATASET_MANAGEMENT:
+ if (ctrlr->np->max_data_xfer_size != 0 &&
+ nvmf_capsule_data_len(nc) > ctrlr->np->max_data_xfer_size) {
+ nvmft_send_generic_error(qp, nc, NVME_SC_INVALID_FIELD);
+ nvmf_free_capsule(nc);
+ return;
+ }
+ break;
+ }
+
switch (cmd->opc) {
case NVME_OPC_FLUSH:
if (cmd->nsid == htole32(0xffffffff)) {
diff --git a/sys/dev/nvmf/controller/nvmft_var.h b/sys/dev/nvmf/controller/nvmft_var.h
--- a/sys/dev/nvmf/controller/nvmft_var.h
+++ b/sys/dev/nvmf/controller/nvmft_var.h
@@ -32,6 +32,7 @@
struct nvme_controller_data cdata;
struct nvme_firmware_page fp;
uint64_t cap;
+ size_t max_data_xfer_size;
uint32_t max_io_qsize;
uint16_t portid;
bool online;
diff --git a/sys/dev/nvmf/nvmf.h b/sys/dev/nvmf/nvmf.h
--- a/sys/dev/nvmf/nvmf.h
+++ b/sys/dev/nvmf/nvmf.h
@@ -25,6 +25,7 @@
#define NVMF_IORCSZ (sizeof(struct nvme_completion))
#define NVMF_NN (1024)
+#define NVMF_MDTS (4 * 1024 * 1024) /* In bytes */
/*
* Default settings for Fabrics hosts. These match values used by
diff --git a/tests/sys/cam/ctl/nvmft/nvmft.c b/tests/sys/cam/ctl/nvmft/nvmft.c
--- a/tests/sys/cam/ctl/nvmft/nvmft.c
+++ b/tests/sys/cam/ctl/nvmft/nvmft.c
@@ -985,6 +985,61 @@
nvmf_free_qpair(admin);
}
+static size_t
+fetch_mdts(struct nvmf_qpair *qp)
+{
+ struct nvme_controller_data cdata;
+ uint64_t cap;
+ u_int mpsmin;
+ int error;
+
+ error = nvmf_host_identify_controller(qp, &cdata);
+ if (error != 0)
+ atf_tc_fail("Failed to fetch controller data: %s",
+ strerror(error));
+
+ if (cdata.mdts == 0)
+ return (0);
+
+ error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap);
+ if (error != 0)
+ atf_tc_fail("Failed to fetch CAP: %s", strerror(error));
+
+ mpsmin = NVMEV(NVME_CAP_HI_REG_MPSMIN, cap >> 32);
+ return ((size_t)1 << (cdata.mdts + mpsmin + NVME_MPS_SHIFT));
+}
+
+ATF_TC(fetch_hip_longer_than_mdts);
+ATF_TC_HEAD(fetch_hip_longer_than_mdts, tc)
+{
+ require_nvmft(tc);
+}
+ATF_TC_BODY(fetch_hip_longer_than_mdts, tc)
+{
+ struct nvmf_qpair *admin;
+ char *buf;
+ size_t mdts, len;
+ uint16_t status;
+
+ admin = connect_admin_only(tc);
+
+ mdts = fetch_mdts(admin);
+ if (mdts == 0)
+ atf_tc_skip("Controller does not set a limit on transfers");
+
+ len = mdts + 4;
+ buf = malloc(len);
+
+ status = fetch_log_page(admin, NVME_LOG_HEALTH_INFORMATION, 0,
+ mdts / 4, buf, len);
+ ATF_REQUIRE(NVME_STATUS_GET_SCT(status) == NVME_SCT_GENERIC);
+ ATF_REQUIRE(NVME_STATUS_GET_SC(status) == NVME_SC_INVALID_FIELD);
+
+ free(buf);
+ shutdown_controller(admin);
+ nvmf_free_qpair(admin);
+}
+
static void
identify_bad_size(const atf_tc_t *tc, size_t len)
{
@@ -1404,6 +1459,89 @@
nvmf_free_qpair(admin);
}
+ATF_TC(read_longer_than_mdts);
+ATF_TC_HEAD(read_longer_than_mdts, tc)
+{
+ require_nvmft(tc);
+}
+ATF_TC_BODY(read_longer_than_mdts, tc)
+{
+ const struct nvme_completion *cpl;
+ struct nvmf_qpair_params qparams;
+ struct nvmf_association *na;
+ struct nvmf_qpair *admin, *io;
+ struct nvmf_capsule *cc, *rc;
+ char hostnqn[NVMF_NQN_MAX_LEN];
+ uint8_t hostid[16];
+ struct addrinfo *ai, *list;
+ char *buf;
+ size_t len, mdts, nlb;
+ u_int block_size, nsid;
+ int error;
+ uint16_t status;
+
+ init_hostid(hostid, hostnqn);
+
+ na = create_association(tc);
+
+ memset(&qparams, 0, sizeof(qparams));
+ qparams.admin = true;
+ qparams.tcp.fd = open_socket(tc, &ai, &list);
+
+ admin = connect_admin_queue(tc, na, &qparams, hostid, hostnqn, 0);
+ if (admin == NULL)
+ atf_tc_fail("Failed to create admin queue: %s",
+ nvmf_association_error(na));
+
+ require_nvm(admin);
+
+ memset(&qparams, 0, sizeof(qparams));
+ qparams.admin = false;
+ qparams.tcp.fd = open_socket_ai(ai);
+ freeaddrinfo(list);
+
+ io = connect_io_queue(tc, admin, na, &qparams, hostid, hostnqn);
+ nvmf_free_association(na);
+
+ nsid = validate_namespace(tc, admin, &block_size);
+
+ mdts = fetch_mdts(admin);
+ if (mdts == 0)
+ atf_tc_skip("Controller does not set a limit on transfers");
+
+ if (mdts < block_size)
+ atf_tc_skip("Controller MDTS less than namespace block size");
+
+ nlb = mdts / block_size;
+ if (nlb > UINT32_MAX)
+ atf_tc_skip("Cannot construct transfer larger than MDTS");
+
+ len = (nlb + 1) * block_size;
+ buf = malloc(len);
+ cc = create_read_command(io, nsid, 0, nlb + 1, buf, len);
+
+ error = nvmf_host_transmit_command(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to transmit READ capsule: %s",
+ strerror(errno));
+
+ error = nvmf_host_wait_for_response(cc, &rc);
+ nvmf_free_capsule(cc);
+ if (error != 0)
+ atf_tc_fail("Failed to receive READ response: %s",
+ strerror(error));
+
+ cpl = nvmf_capsule_cqe(rc);
+ status = le16toh(cpl->status);
+ nvmf_free_capsule(rc);
+ ATF_REQUIRE(NVME_STATUS_GET_SCT(status) == NVME_SCT_GENERIC);
+ ATF_REQUIRE(NVME_STATUS_GET_SC(status) == NVME_SC_INVALID_FIELD);
+
+ shutdown_controller(admin);
+ nvmf_free_qpair(io);
+ nvmf_free_qpair(admin);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, invalid_qid);
@@ -1427,11 +1565,13 @@
ATF_TP_ADD_TC(tp, fetch_hip_offset_3);
ATF_TP_ADD_TC(tp, fetch_hip_offset_beyond_end);
ATF_TP_ADD_TC(tp, fetch_reserved_log_page);
+ ATF_TP_ADD_TC(tp, fetch_hip_longer_than_mdts);
ATF_TP_ADD_TC(tp, identify_short);
ATF_TP_ADD_TC(tp, identify_long);
ATF_TP_ADD_TC(tp, read_block);
ATF_TP_ADD_TC(tp, compare_block);
ATF_TP_ADD_TC(tp, compare_mismatch);
+ ATF_TP_ADD_TC(tp, read_longer_than_mdts);
return (atf_no_error());
}
diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5
--- a/usr.sbin/ctld/ctl.conf.5
+++ b/usr.sbin/ctld/ctl.conf.5
@@ -359,6 +359,9 @@
The maximum number of entries a remote host can request for an admin queue pair.
.It max_io_qsize Ta 65536 Ta any Ta
The maximum number of entries a remote host can request for an I/O queue pair.
+.It mdts Ta 4MiB Ta any Ta
+The maximum data transfer size for any command (admin or I/O).
+Must be a power of two that is at least 4KiB.
.El
.It Ic tag Ar value
Unique 16-bit port ID for this

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 15, 6:38 PM (23 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41746892
Default Alt Text
D61557.1794767938.diff (8 KB)

Event Timeline