[PATCH 3/4] nvme-fabrics: add tp8010 support
Martin Belanger
nitram_67 at hotmail.com
Tue Jan 25 06:59:55 PST 2022
From: Martin Belanger <martin.belanger at dell.com>
TP8010 introduces Central Discovery Controllers (CDC) and the
Discovery Information Management (DIM) PDU, which is used to
perform explicit registration with Discovery Controllers.
This patch defines the DIM PDU and adds logic to perform explicit
registration with DCs when registration has been requested by the
user.
Signed-off-by: Martin Belanger <martin.belanger at dell.com>
---
drivers/nvme/host/fabrics.c | 164 +++++++++++++++++++++++
drivers/nvme/host/fabrics.h | 9 ++
drivers/nvme/host/tcp.c | 51 +++++++-
include/linux/nvme.h | 255 ++++++++++++++++++++++++++++++++++--
4 files changed, 467 insertions(+), 12 deletions(-)
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index 040a6cce6afa..e6fc2f85b670 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -10,6 +10,7 @@
#include <linux/mutex.h>
#include <linux/parser.h>
#include <linux/seq_file.h>
+#include <linux/utsname.h>
#include "nvme.h"
#include "fabrics.h"
@@ -526,6 +527,165 @@ static struct nvmf_transport_ops *nvmf_lookup_transport(
return NULL;
}
+/**
+ * nvmf_get_tel() - Calculate the amount of memory needed for a Discovery
+ * Information Entry. Each Entry must contain at a minimum an Extended
+ * Attribute for the HostID. The Entry may optionally contain an Extended
+ * Attribute for the Symbolic Name.
+ *
+ * @hostsymname - Symbolic name (may be NULL)
+ *
+ * Return Total Entry Length
+ */
+static u32 nvmf_get_tel(const char *hostsymname)
+{
+ u32 tel = SIZEOF_EXT_DIE_WO_EXAT;
+ u16 len;
+
+ /* Host ID is mandatory */
+ tel += exat_size(sizeof(uuid_t));
+
+ /* Symbolic name is optional */
+ len = hostsymname ? strlen(hostsymname) : 0;
+ if (len)
+ tel += exat_size(len);
+
+ return tel;
+}
+
+/**
+ * nvmf_fill_die() - Fill the Discovery Information Entry pointed to by
+ * @die.
+ *
+ * @die - Pointer to Discovery Information Entry to be filled
+ * @tel - Length of the DIE
+ * @trtype - Transport type
+ * @adrfam - Address family
+ * @reg_addr - Address to register. Setting this to an empty string tells
+ * the DC to infer address from the source address of the socket.
+ * @nqn - Host NQN
+ * @hostid - Host ID
+ * @hostsymname - Host symbolic name
+ * @tsas - Transport Specific Address Subtype for the address being
+ * registered.
+ */
+static void nvmf_fill_die(struct nvmf_ext_die *die,
+ u32 tel,
+ u8 trtype,
+ u8 adrfam,
+ const char *reg_addr,
+ struct nvmf_host *host,
+ union nvmf_tsas *tsas)
+{
+ u16 numexat = 0;
+ size_t symname_len;
+ struct nvmf_ext_attr *exat;
+
+ die->tel = cpu_to_le32(tel);
+ die->trtype = trtype;
+ die->adrfam = adrfam;
+
+ strncpy(die->nqn, host->nqn, sizeof(die->nqn));
+ strncpy(die->traddr, reg_addr, sizeof(die->traddr));
+ memcpy(&die->tsas, tsas, sizeof(die->tsas));
+
+ /* Extended Attribute for the HostID (mandatory) */
+ numexat++;
+ exat = &die->exat;
+ exat->type = cpu_to_le16(NVME_EXATTYPE_HOSTID);
+ exat->len = cpu_to_le16(exat_len(sizeof(host->id)));
+ uuid_copy((uuid_t *)exat->val, &host->id);
+
+ /* Extended Attribute for the Symbolic Name (optional) */
+ symname_len = strlen(host->symname);
+ if (symname_len) {
+ u16 exatlen = exat_len(symname_len);
+
+ numexat++;
+ exat = exat_ptr_next(exat);
+ exat->type = cpu_to_le16(NVME_EXATTYPE_SYMNAME);
+ exat->len = cpu_to_le16(exatlen);
+ memcpy(exat->val, host->symname, symname_len);
+ /* Per Base specs, ASCII strings must be padded with spaces */
+ memset(&exat->val[symname_len], ' ', exatlen - symname_len);
+ }
+
+ die->numexat = cpu_to_le16(numexat);
+}
+
+/**
+ * nvmf_disc_info_mgmt() - Perform explicit registration, deregistration,
+ * or registration-update (specified by @tas) by sending a Discovery
+ * Information Management (DIM) command to the Discovery Controller (DC).
+ *
+ * @ctrl: Host NVMe controller instance maintaining the admin queue used to
+ * submit the DIM command to the DC.
+ * @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to
+ * perform a Registration, Deregistration, or Registration-update.
+ * @trtype: Transport type (e.g. NVMF_TRTYPE_TCP)
+ * @adrfam: Address family (e.g. NVMF_ADDR_FAMILY_IP6)
+ * @reg_addr - Address to register. Setting this to an empty string tells
+ * the DC to infer address from the source address of the socket.
+ * @tsas - Transport Specific Address Subtype for the address being
+ * registered.
+ *
+ * Return: 0: success,
+ * > 0: NVMe error status code,
+ * < 0: Linux errno error code
+ */
+int nvmf_disc_info_mgmt(struct nvme_ctrl *ctrl,
+ enum nvmf_dim_tas tas,
+ u8 trtype,
+ u8 adrfam,
+ const char *reg_addr,
+ union nvmf_tsas *tsas)
+{
+ int ret;
+ u32 tdl; /* Total Data Length */
+ u32 tel; /* Total Entry Length */
+ struct nvmf_dim_data *dim; /* Discovery Information Management */
+ struct nvmf_ext_die *die; /* Discovery Information Entry */
+ struct nvme_command cmd = {};
+ union nvme_result result;
+
+ /* Register 1 Discovery Information Entry (DIE) of size TEL */
+ tel = nvmf_get_tel(ctrl->opts->host->symname);
+ tdl = SIZEOF_DIM_WO_DIE + tel;
+
+ dim = kzalloc(tdl, GFP_KERNEL);
+ if (!dim)
+ return -ENOMEM;
+
+ cmd.dim.opcode = nvme_admin_dim;
+ cmd.dim.cdw10 = cpu_to_le32(tas);
+
+ dim->tdl = cpu_to_le32(tdl);
+ dim->nument = cpu_to_le64(1); /* only 1 DIE to register */
+ dim->entfmt = cpu_to_le16(NVME_ENTFMT_EXTENDED);
+ dim->etype = cpu_to_le16(NVME_REGISTRATION_HOST);
+ dim->ektype = cpu_to_le16(0x5F); /* must be 0x5F per specs */
+
+ strncpy(dim->eid, ctrl->opts->host->nqn, sizeof(dim->eid));
+ strncpy(dim->ename, utsname()->nodename, sizeof(dim->ename));
+ strncpy(dim->ever, utsname()->release, sizeof(dim->ever));
+
+ die = &dim->die.extended;
+ nvmf_fill_die(die, tel, trtype, adrfam, reg_addr,
+ ctrl->opts->host, tsas);
+
+ ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &result,
+ dim, tdl, 0, NVME_QID_ANY, 1, 0);
+ if (unlikely(ret)) {
+ dev_err(ctrl->device, "DIM error Result:0x%04X Status:0x%04X\n",
+ le32_to_cpu(result.u32), ret);
+ }
+
+ kfree(dim);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nvmf_disc_info_mgmt);
+
static const match_table_t opt_tokens = {
{ NVMF_OPT_TRANSPORT, "transport=%s" },
{ NVMF_OPT_TRADDR, "traddr=%s" },
@@ -550,6 +710,7 @@ static const match_table_t opt_tokens = {
{ NVMF_OPT_TOS, "tos=%d" },
{ NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" },
{ NVMF_OPT_DISCOVERY, "discovery" },
+ { NVMF_OPT_REGISTER, "register" },
{ NVMF_OPT_ERR, NULL }
};
@@ -831,6 +992,9 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
case NVMF_OPT_DISCOVERY:
opts->discovery_nqn = true;
break;
+ case NVMF_OPT_REGISTER:
+ opts->explicit_registration = true;
+ break;
case NVMF_OPT_SYMNAME:
hostsymname = match_strdup(args);
if (!hostsymname) {
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index 494e6dbe233a..77f5768f8ff4 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -70,6 +70,7 @@ enum {
NVMF_OPT_HOST_IFACE = 1 << 21,
NVMF_OPT_DISCOVERY = 1 << 22,
NVMF_OPT_SYMNAME = 1 << 23,
+ NVMF_OPT_REGISTER = 1 << 24,
};
/**
@@ -106,6 +107,7 @@ enum {
* @nr_poll_queues: number of queues for polling I/O
* @tos: type of service
* @fast_io_fail_tmo: Fast I/O fail timeout in seconds
+ * @explicit_registration: Explicit Registration with DC using the DIM PDU
*/
struct nvmf_ctrl_options {
unsigned mask;
@@ -130,6 +132,7 @@ struct nvmf_ctrl_options {
unsigned int nr_poll_queues;
int tos;
int fast_io_fail_tmo;
+ bool explicit_registration;
};
/*
@@ -200,5 +203,11 @@ int nvmf_get_address(struct nvme_ctrl *ctrl, char *buf, int size);
bool nvmf_should_reconnect(struct nvme_ctrl *ctrl);
bool nvmf_ip_options_match(struct nvme_ctrl *ctrl,
struct nvmf_ctrl_options *opts);
+int nvmf_disc_info_mgmt(struct nvme_ctrl *ctrl,
+ enum nvmf_dim_tas tas,
+ u8 trtype,
+ u8 adrfam,
+ const char *reg_addr,
+ union nvmf_tsas *tsas);
#endif /* _NVME_FABRICS_H */
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 4ceb28675fdf..970b7df574a4 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -1993,6 +1993,40 @@ static void nvme_tcp_reconnect_or_remove(struct nvme_ctrl *ctrl)
}
}
+/**
+ * nvme_get_adrfam() - Get address family for the address we're registering
+ * with the DC. We retrieve this info from the socket itself. If we can't
+ * get the source address from the socket, then we'll infer the address
+ * family from the address of the DC since the DC address has the same
+ * address family.
+ *
+ * @ctrl: Host NVMe controller instance maintaining the admin queue used to
+ * submit the DIM command to the DC.
+ *
+ * Return: The address family of the source address associated with the
+ * socket connected to the DC.
+ */
+static u8 nvme_get_adrfam(struct nvme_ctrl *ctrl)
+{
+ u8 adrfam = NVMF_ADDR_FAMILY_IP4;
+ struct sockaddr_storage sas;
+ struct sockaddr *sa = (struct sockaddr *)&sas;
+ struct nvme_tcp_queue *queue = &to_tcp_ctrl(ctrl)->queues[0];
+
+ if (kernel_getsockname(queue->sock, sa) == 0) {
+ if (sa->sa_family == AF_INET6)
+ adrfam = NVMF_ADDR_FAMILY_IP6;
+ } else {
+ unsigned char buf[sizeof(struct in6_addr)];
+
+ if (in6_pton(ctrl->opts->traddr,
+ strlen(ctrl->opts->traddr), buf, -1, NULL) > 0)
+ adrfam = NVMF_ADDR_FAMILY_IP6;
+ }
+
+ return adrfam;
+}
+
static int nvme_tcp_setup_ctrl(struct nvme_ctrl *ctrl, bool new)
{
struct nvmf_ctrl_options *opts = ctrl->opts;
@@ -2045,6 +2079,20 @@ static int nvme_tcp_setup_ctrl(struct nvme_ctrl *ctrl, bool new)
goto destroy_io;
}
+ if (ctrl->opts->explicit_registration &&
++ ((ctrl->dctype == NVME_DCTYPE_DDC) ||
+ (ctrl->dctype == NVME_DCTYPE_CDC))) {
+ /* We're registering our source address with the DC. To do
+ * that, we can simply send an empty string. This tells the DC
+ * to retrieve the source address from the socket and use that
+ * as the registration address.
+ */
+ union nvmf_tsas tsas = {.tcp.sectype = NVMF_TCP_SECTYPE_NONE};
+
+ nvmf_disc_info_mgmt(ctrl, NVME_TAS_REGISTER, NVMF_TRTYPE_TCP,
+ nvme_get_adrfam(ctrl), "", &tsas);
+ }
+
nvme_start_ctrl(ctrl);
return 0;
@@ -2613,7 +2661,8 @@ static struct nvmf_transport_ops nvme_tcp_transport = {
NVMF_OPT_HOST_TRADDR | NVMF_OPT_CTRL_LOSS_TMO |
NVMF_OPT_HDR_DIGEST | NVMF_OPT_DATA_DIGEST |
NVMF_OPT_NR_WRITE_QUEUES | NVMF_OPT_NR_POLL_QUEUES |
- NVMF_OPT_TOS | NVMF_OPT_HOST_IFACE,
+ NVMF_OPT_TOS | NVMF_OPT_HOST_IFACE |
+ NVMF_OPT_REGISTER,
.create_ctrl = nvme_tcp_create_ctrl,
};
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 3b47951342f4..891615876cfa 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include <linux/uuid.h>
+#include <linux/kernel.h>
/* NQN names in commands fields specified one size */
#define NVMF_NQN_FIELD_LEN 256
@@ -102,6 +103,16 @@ enum {
NVMF_RDMA_CMS_RDMA_CM = 1, /* Sockets based endpoint addressing */
};
+/**
+ * enum -
+ * @NVMF_TCP_SECTYPE_NONE: No Security
+ * @NVMF_TCP_SECTYPE_TLS: Transport Layer Security
+ */
+enum {
+ NVMF_TCP_SECTYPE_NONE = 0,
+ NVMF_TCP_SECTYPE_TLS = 1,
+};
+
#define NVME_AQ_DEPTH 32
#define NVME_NR_AEN_COMMANDS 1
#define NVME_AQ_BLK_MQ_DEPTH (NVME_AQ_DEPTH - NVME_NR_AEN_COMMANDS)
@@ -1036,6 +1047,7 @@ enum nvme_admin_opcode {
nvme_admin_sanitize_nvm = 0x84,
nvme_admin_get_lba_status = 0x86,
nvme_admin_vendor_start = 0xC0,
+ nvme_admin_dim = 0x21,
};
#define nvme_admin_opcode_name(opcode) { opcode, #opcode }
@@ -1316,6 +1328,29 @@ struct nvmf_common_command {
__u8 ts[24];
};
+/**
+ * union nvmf_tsas - Transport Specific Address Subtype
+ * @qptype: Queue Pair Service Type (NVMF_RDMA_QPTYPE_CONNECTED, NVMF_RDMA_QPTYPE_DATAGRAM)
+ * @prtype: Provider Type (see enum nvme_rdma_prtype)
+ * @cma: Connection Management Service (NVMF_RDMA_CMS_RDMA_CM)
+ * @pkey: Partition Key
+ * @sectype: Security Type (NVMF_TCP_SECTYPE_NONE, NVMF_TCP_SECTYPE_TLS)
+ */
+union nvmf_tsas {
+ char common[NVMF_TSAS_SIZE];
+ struct rdma {
+ __u8 qptype;
+ __u8 prtype;
+ __u8 cms;
+ __u8 rsvd3[5];
+ __u16 pkey;
+ __u8 rsvd10[246];
+ } rdma;
+ struct tcp {
+ __u8 sectype;
+ } tcp;
+};
+
/*
* The legal cntlid range a NVMe Target will provide.
* Note that cntlid of value 0 is considered illegal in the fabrics world.
@@ -1349,17 +1384,7 @@ struct nvmf_disc_rsp_page_entry {
__u8 resv64[192];
char subnqn[NVMF_NQN_FIELD_LEN];
char traddr[NVMF_TRADDR_SIZE];
- union tsas {
- char common[NVMF_TSAS_SIZE];
- struct rdma {
- __u8 qptype;
- __u8 prtype;
- __u8 cms;
- __u8 resv3[5];
- __u16 pkey;
- __u8 resv10[246];
- } rdma;
- } tsas;
+ union nvmf_tsas tsas;
};
/* Discovery log page header */
@@ -1447,6 +1472,213 @@ struct streams_directive_params {
__u8 rsvd2[6];
};
+/**
+ * Discovery Information Management (DIM) command. This is sent by a
+ * host to the Central Discovery Controller (CDC) to perform
+ * explicit registration.
+ */
+enum nvmf_dim_tas {
+ NVME_TAS_REGISTER = 0x00,
+ NVME_TAS_DEREGISTER = 0x01,
+ NVME_TAS_UPDATE = 0x02,
+};
+
+struct nvmf_dim_command {
+ __u8 opcode; /* nvme_admin_dim */
+ __u8 flags;
+ __u16 command_id;
+ __le32 nsid;
+ __u64 rsvd2[2];
+ union nvme_data_ptr dptr;
+ __le32 cdw10; /* enum nvmf_dim_tas */
+ __le32 cdw11;
+ __le32 cdw12;
+ __le32 cdw13;
+ __le32 cdw14;
+ __le32 cdw15;
+};
+
+#define NVMF_ENAME_LEN 256
+#define NVMF_EVER_LEN 64
+
+enum nvmf_dim_entfmt {
+ NVME_ENTFMT_BASIC = 0x01,
+ NVME_ENTFMT_EXTENDED = 0x02,
+};
+
+enum nvmf_dim_etype {
+ NVME_REGISTRATION_HOST = 0x01,
+ NVME_REGISTRATION_DDC = 0x02,
+ NVME_REGISTRATION_CDC = 0x03,
+};
+
+enum nvmf_exattype {
+ NVME_EXATTYPE_HOSTID = 0x01, /* Host Identifier */
+ NVME_EXATTYPE_SYMNAME = 0x02, /* Symbolic Name */
+};
+
+/**
+ * struct nvmf_ext_attr - Extended Attribute (EXAT)
+ *
+ * Bytes Description
+ * @type 01:00 Extended Attribute Type (EXATTYPE) - enum nvmf_exattype
+ * @len 03:02 Extended Attribute Length (EXATLEN)
+ * @val (EXATLEN-1) Extended Attribute Value (EXATVAL) - size allocated
+ * +4:04 for array must be a multiple of 4 bytes
+ */
+struct nvmf_ext_attr {
+ __le16 type;
+ __le16 len;
+ __u8 val[];
+};
+#define SIZEOF_EXT_EXAT_WO_VAL offsetof(struct nvmf_ext_attr, val)
+
+/**
+ * struct nvmf_ext_die - Extended Discovery Information Entry (DIE)
+ *
+ * Bytes Description
+ * @trtype 00 Transport Type (enum nvme_trtype)
+ * @adrfam 01 Address Family (enum nvmf_addr_familiy)
+ * @subtype 02 Subsystem Type (enum nvme_subsys_type)
+ * @treq 03 Transport Requirements (enum nvmf_treq)
+ * @portid 05:04 Port ID
+ * @cntlid 07:06 Controller ID
+ * @asqsz 09:08 Admin Max SQ Size
+ * 31:10 Reserved
+ * @trsvcid 63:32 Transport Service Identifier
+ * 255:64 Reserved
+ * @nqn 511:256 NVM Qualified Name
+ * @traddr 767:512 Transport Address
+ * @tsas 1023:768 Transport Specific Address Subtype
+ * @tel 1027:1024 Total Entry Length
+ * @numexat 1029:1028 Number of Extended Attributes
+ * 1031:1030 Reserved
+ * @exat[0] ((EXATLEN-1)+ Extented Attributes 0 (see @numexat)
+ * 4)+1032:1032 Cannot access as an array because each EXAT
+ * entry has an undetermined size.
+ * @exat[N] TEL-1:TEL- Extented Attributes N (w/ N = NUMEXAT-1)
+ * (EXATLEN+4)
+ */
+struct nvmf_ext_die {
+ __u8 trtype;
+ __u8 adrfam;
+ __u8 subtype;
+ __u8 treq;
+ __le16 portid;
+ __le16 cntlid;
+ __le16 asqsz;
+ __u8 resv10[22];
+ char trsvcid[NVMF_TRSVCID_SIZE];
+ __u8 resv64[192];
+ char nqn[NVMF_NQN_FIELD_LEN];
+ char traddr[NVMF_TRADDR_SIZE];
+ union nvmf_tsas tsas;
+ __le32 tel;
+ __le16 numexat;
+ __u16 resv1030;
+ struct nvmf_ext_attr exat;
+};
+#define SIZEOF_EXT_DIE_WO_EXAT offsetof(struct nvmf_ext_die, exat)
+
+/**
+ * union nvmf_die - Discovery Information Entry (DIE)
+ *
+ * Depending on the ENTFMT specified in the DIM, DIEs can be entered with
+ * the Basic or Extended formats. For Basic format, each entry has a fixed
+ * length. Therefore, the "basic" field defined below can be accessed as a
+ * C array. For the Extended format, however, each entry is of variable
+ * length (TEL). Therefore, the "extended" field defined below cannot be
+ * accessed as a C array. Instead, the "extended" field is akin to a
+ * linked-list, where one can "walk" through the list. To move to the next
+ * entry, one simply adds the current entry's length (TEL) to the "walk"
+ * pointer. The number of entries in the list is specified by NUMENT.
+ * Although extended entries are of a variable lengths (TEL), TEL is
+ * always a multiple of 4 bytes.
+ */
+union nvmf_die {
+ struct nvmf_disc_rsp_page_entry basic[0];
+ struct nvmf_ext_die extended;
+};
+
+/**
+ * struct nvmf_dim_data - Discovery Information Management (DIM) - Data
+ *
+ * Bytes Description
+ * @tdl 03:00 Total Data Length
+ * 07:04 Reserved
+ * @nument 15:08 Number of entries
+ * @entfmt 17:16 Entry Format (enum nvmf_dim_entfmt)
+ * @etype 19:18 Entity Type (enum nvmf_dim_etype)
+ * @portlcL 20 Port Local
+ * 21 Reserved
+ * @ektype 23:22 Entry Key Type
+ * @eid 279:24 Entity Identifier (e.g. Host NQN)
+ * @ename 535:280 Entity Name (e.g. hostname)
+ * @ever 599:536 Entity Version (e.g. OS Name/Version)
+ * 1023:600 Reserved
+ * @die TDL-1:1024 Discovery Information Entry (see @nument above)
+ */
+struct nvmf_dim_data {
+ __le32 tdl;
+ __u32 rsvd4;
+ __le64 nument;
+ __le16 entfmt;
+ __le16 etype;
+ __u8 portlcl;
+ __u8 rsvd21;
+ __le16 ektype;
+ char eid[NVMF_NQN_FIELD_LEN];
+ char ename[NVMF_ENAME_LEN];
+ char ever[NVMF_EVER_LEN];
+ __u8 rsvd600[424];
+ union nvmf_die die;
+};
+#define SIZEOF_DIM_WO_DIE offsetof(struct nvmf_dim_data, die)
+
+/**
+ * exat_len() - Return the size in bytes, rounded to a multiple of 4 ((i.e.
+ * size of u32), of the buffer needed to hold the exat value of
+ * size @val_len.
+ */
+static inline u16 exat_len(size_t val_len)
+{
+ return (u16)round_up(val_len, sizeof(u32));
+}
+
+/**
+ * exat_size - return the size of the "struct nvmf_ext_attr"
+ * needed to hold a value of size @val_len.
+ *
+ * @val_len: This is the length of the data to be copied to the "val"
+ * field of a "struct nvmf_ext_attr".
+ *
+ * @return The size in bytes, rounded to a multiple of 4 (i.e. size of
+ * u32), of the "struct nvmf_ext_attr" required to hold a string of
+ * length @val_len.
+ */
+static inline u16 exat_size(size_t val_len)
+{
+ return (u16)(SIZEOF_EXT_EXAT_WO_VAL + exat_len(val_len));
+}
+
+/**
+ * exat_ptr_next - Increment @p to the next element in the array. Extended
+ * attributes are saved to an array of "struct nvmf_ext_attr" where each
+ * element of the array is of variable size. In order to move to the next
+ * element in the array one must increment the pointer to the current
+ * element (@p) by the size of the current element.
+ *
+ * @p: Pointer to an element of an array of "struct nvmf_ext_attr".
+ *
+ * @return Pointer to the next element in the array.
+ */
+static inline struct nvmf_ext_attr *exat_ptr_next(struct nvmf_ext_attr *p)
+{
+ return (struct nvmf_ext_attr *)
+ ((uintptr_t)p + (ptrdiff_t)exat_size(le16_to_cpu(p->len)));
+}
+
+
struct nvme_command {
union {
struct nvme_common_command common;
@@ -1470,6 +1702,7 @@ struct nvme_command {
struct nvmf_property_get_command prop_get;
struct nvme_dbbuf dbbuf;
struct nvme_directive_cmd directive;
+ struct nvmf_dim_command dim;
};
};
--
2.34.1
More information about the Linux-nvme
mailing list