Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177570599
D61557.1794767938.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
8 KB
Referenced Files
None
Subscribers
None
D61557.1794767938.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D61557: nvmft: Add an upper bound on the maximum transfer size (MDTS)
Attached
Detach File
Event Timeline