Commit ea52ac1c authored by Israel Rukshin's avatar Israel Rukshin Committed by Christoph Hellwig
Browse files

nvmet: add metadata/T10-PI support



Expose the namespace metadata format when PI is enabled. The user needs
to enable the capability per subsystem and per port. The other metadata
properties are taken from the namespace/bdev.

Usage example:
echo 1 > /config/nvmet/subsystems/${NAME}/attr_pi_enable
echo 1 > /config/nvmet/ports/${PORT_NUM}/param_pi_enable

Signed-off-by: default avatarIsrael Rukshin <israelr@mellanox.com>
Signed-off-by: default avatarMax Gurtovoy <maxg@mellanox.com>
Reviewed-by: default avatarJames Smart <james.smart@broadcom.com>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
parent 39481fbd
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -341,6 +341,7 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
{
	struct nvmet_ctrl *ctrl = req->sq->ctrl;
	struct nvme_id_ctrl *id;
	u32 cmd_capsule_size;
	u16 status = 0;

	id = kzalloc(sizeof(*id), GFP_KERNEL);
@@ -433,9 +434,15 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)

	strlcpy(id->subnqn, ctrl->subsys->subsysnqn, sizeof(id->subnqn));

	/* Max command capsule size is sqe + single page of in-capsule data */
	id->ioccsz = cpu_to_le32((sizeof(struct nvme_command) +
				  req->port->inline_data_size) / 16);
	/*
	 * Max command capsule size is sqe + in-capsule data size.
	 * Disable in-capsule data for Metadata capable controllers.
	 */
	cmd_capsule_size = sizeof(struct nvme_command);
	if (!ctrl->pi_support)
		cmd_capsule_size += req->port->inline_data_size;
	id->ioccsz = cpu_to_le32(cmd_capsule_size / 16);

	/* Max response capsule size is cqe */
	id->iorcsz = cpu_to_le32(sizeof(struct nvme_completion) / 16);

@@ -465,6 +472,7 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)

static void nvmet_execute_identify_ns(struct nvmet_req *req)
{
	struct nvmet_ctrl *ctrl = req->sq->ctrl;
	struct nvmet_ns *ns;
	struct nvme_id_ns *id;
	u16 status = 0;
@@ -482,7 +490,7 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
	}

	/* return an all zeroed buffer if we can't find an active namespace */
	ns = nvmet_find_namespace(req->sq->ctrl, req->cmd->identify.nsid);
	ns = nvmet_find_namespace(ctrl, req->cmd->identify.nsid);
	if (!ns)
		goto done;

@@ -523,6 +531,16 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)

	id->lbaf[0].ds = ns->blksize_shift;

	if (ctrl->pi_support && nvmet_ns_has_pi(ns)) {
		id->dpc = NVME_NS_DPC_PI_FIRST | NVME_NS_DPC_PI_LAST |
			  NVME_NS_DPC_PI_TYPE1 | NVME_NS_DPC_PI_TYPE2 |
			  NVME_NS_DPC_PI_TYPE3;
		id->mc = NVME_MC_EXTENDED_LBA;
		id->dps = ns->pi_type;
		id->flbas = NVME_NS_FLBAS_META_EXT;
		id->lbaf[0].ms = cpu_to_le16(ns->metadata_size);
	}

	if (ns->readonly)
		id->nsattr |= (1 << 0);
	nvmet_put_namespace(ns);
+58 −0
Original line number Diff line number Diff line
@@ -248,6 +248,36 @@ static ssize_t nvmet_param_inline_data_size_store(struct config_item *item,

CONFIGFS_ATTR(nvmet_, param_inline_data_size);

#ifdef CONFIG_BLK_DEV_INTEGRITY
static ssize_t nvmet_param_pi_enable_show(struct config_item *item,
		char *page)
{
	struct nvmet_port *port = to_nvmet_port(item);

	return snprintf(page, PAGE_SIZE, "%d\n", port->pi_enable);
}

static ssize_t nvmet_param_pi_enable_store(struct config_item *item,
		const char *page, size_t count)
{
	struct nvmet_port *port = to_nvmet_port(item);
	bool val;

	if (strtobool(page, &val))
		return -EINVAL;

	if (port->enabled) {
		pr_err("Disable port before setting pi_enable value.\n");
		return -EACCES;
	}

	port->pi_enable = val;
	return count;
}

CONFIGFS_ATTR(nvmet_, param_pi_enable);
#endif

static ssize_t nvmet_addr_trtype_show(struct config_item *item,
		char *page)
{
@@ -1010,6 +1040,28 @@ static ssize_t nvmet_subsys_attr_model_store(struct config_item *item,
}
CONFIGFS_ATTR(nvmet_subsys_, attr_model);

#ifdef CONFIG_BLK_DEV_INTEGRITY
static ssize_t nvmet_subsys_attr_pi_enable_show(struct config_item *item,
						char *page)
{
	return snprintf(page, PAGE_SIZE, "%d\n", to_subsys(item)->pi_support);
}

static ssize_t nvmet_subsys_attr_pi_enable_store(struct config_item *item,
						 const char *page, size_t count)
{
	struct nvmet_subsys *subsys = to_subsys(item);
	bool pi_enable;

	if (strtobool(page, &pi_enable))
		return -EINVAL;

	subsys->pi_support = pi_enable;
	return count;
}
CONFIGFS_ATTR(nvmet_subsys_, attr_pi_enable);
#endif

static struct configfs_attribute *nvmet_subsys_attrs[] = {
	&nvmet_subsys_attr_attr_allow_any_host,
	&nvmet_subsys_attr_attr_version,
@@ -1017,6 +1069,9 @@ static struct configfs_attribute *nvmet_subsys_attrs[] = {
	&nvmet_subsys_attr_attr_cntlid_min,
	&nvmet_subsys_attr_attr_cntlid_max,
	&nvmet_subsys_attr_attr_model,
#ifdef CONFIG_BLK_DEV_INTEGRITY
	&nvmet_subsys_attr_attr_pi_enable,
#endif
	NULL,
};

@@ -1316,6 +1371,9 @@ static struct configfs_attribute *nvmet_port_attrs[] = {
	&nvmet_attr_addr_trsvcid,
	&nvmet_attr_addr_trtype,
	&nvmet_attr_param_inline_data_size,
#ifdef CONFIG_BLK_DEV_INTEGRITY
	&nvmet_attr_param_pi_enable,
#endif
	NULL,
};

+17 −4
Original line number Diff line number Diff line
@@ -323,12 +323,21 @@ int nvmet_enable_port(struct nvmet_port *port)
	if (!try_module_get(ops->owner))
		return -EINVAL;

	ret = ops->add_port(port);
	if (ret) {
		module_put(ops->owner);
		return ret;
	/*
	 * If the user requested PI support and the transport isn't pi capable,
	 * don't enable the port.
	 */
	if (port->pi_enable && !ops->metadata_support) {
		pr_err("T10-PI is not supported by transport type %d\n",
		       port->disc_addr.trtype);
		ret = -EINVAL;
		goto out_put;
	}

	ret = ops->add_port(port);
	if (ret)
		goto out_put;

	/* If the transport didn't set inline_data_size, then disable it. */
	if (port->inline_data_size < 0)
		port->inline_data_size = 0;
@@ -336,6 +345,10 @@ int nvmet_enable_port(struct nvmet_port *port)
	port->enabled = true;
	port->tr_ops = ops;
	return 0;

out_put:
	module_put(ops->owner);
	return ret;
}

void nvmet_disable_port(struct nvmet_port *port)
+5 −2
Original line number Diff line number Diff line
@@ -197,6 +197,8 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
		goto out;
	}

	ctrl->pi_support = ctrl->port->pi_enable && ctrl->subsys->pi_support;

	uuid_copy(&ctrl->hostid, &d->hostid);

	status = nvmet_install_queue(ctrl, req);
@@ -205,8 +207,9 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
		goto out;
	}

	pr_info("creating controller %d for subsystem %s for NQN %s.\n",
		ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn);
	pr_info("creating controller %d for subsystem %s for NQN %s%s.\n",
		ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
		ctrl->pi_support ? " T10-PI is enabled" : "");
	req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);

out:
+19 −0
Original line number Diff line number Diff line
@@ -145,6 +145,7 @@ struct nvmet_port {
	bool				enabled;
	int				inline_data_size;
	const struct nvmet_fabrics_ops	*tr_ops;
	bool				pi_enable;
};

static inline struct nvmet_port *to_nvmet_port(struct config_item *item)
@@ -204,6 +205,7 @@ struct nvmet_ctrl {
	spinlock_t		error_lock;
	u64			err_counter;
	struct nvme_error_slot	slots[NVMET_ERROR_LOG_SLOTS];
	bool			pi_support;
};

struct nvmet_subsys_model {
@@ -233,6 +235,7 @@ struct nvmet_subsys {
	u64			ver;
	u64			serial;
	char			*subsysnqn;
	bool			pi_support;

	struct config_group	group;

@@ -284,6 +287,7 @@ struct nvmet_fabrics_ops {
	unsigned int type;
	unsigned int msdbd;
	bool has_keyed_sgls : 1;
	bool metadata_support : 1;
	void (*queue_response)(struct nvmet_req *req);
	int (*add_port)(struct nvmet_port *port);
	void (*remove_port)(struct nvmet_port *port);
@@ -511,6 +515,14 @@ static inline u32 nvmet_rw_data_len(struct nvmet_req *req)
			req->ns->blksize_shift;
}

static inline u32 nvmet_rw_metadata_len(struct nvmet_req *req)
{
	if (!IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY))
		return 0;
	return ((u32)le16_to_cpu(req->cmd->rw.length) + 1) *
			req->ns->metadata_size;
}

static inline u32 nvmet_dsm_len(struct nvmet_req *req)
{
	return (le32_to_cpu(req->cmd->dsm.nr) + 1) *
@@ -525,4 +537,11 @@ static inline __le16 to0based(u32 a)
	return cpu_to_le16(max(1U, min(1U << 16, a)) - 1);
}

static inline bool nvmet_ns_has_pi(struct nvmet_ns *ns)
{
	if (!IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY))
		return false;
	return ns->pi_type && ns->metadata_size == sizeof(struct t10_pi_tuple);
}

#endif /* _NVMET_H */