Commit 013b7ebe authored by Mark Ruijter's avatar Mark Ruijter Committed by Keith Busch
Browse files

nvmet: make ctrl model configurable



This patch adds a new target subsys attribute which allows user to
optionally specify model name which then used in the
nvmet_execute_identify_ctrl() to fill up the nvme_id_ctrl structure.

The default value for the model is set to "Linux" for backward
compatibility.

Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarSagi Grimberg <sagi@grimberg.me>
Signed-off-by: default avatarMark Ruijter <MRuijter@onestopsystems.com>
[chaitanya.kulkarni@wdc.com
 *Use macro for default model, coding style fixes.
 *Use RCU for accessing model in for configfs and in
  nvmet_execute_identify_ctrl().
]
Signed-off-by: default avatarChaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
Signed-off-by: default avatarKeith Busch <kbusch@kernel.org>
parent 94a39d61
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -322,12 +322,25 @@ static void nvmet_execute_get_log_page(struct nvmet_req *req)
	nvmet_req_complete(req, NVME_SC_INVALID_FIELD | NVME_SC_DNR);
}

static void nvmet_id_set_model_number(struct nvme_id_ctrl *id,
				      struct nvmet_subsys *subsys)
{
	const char *model = NVMET_DEFAULT_CTRL_MODEL;
	struct nvmet_subsys_model *subsys_model;

	rcu_read_lock();
	subsys_model = rcu_dereference(subsys->model);
	if (subsys_model)
		model = subsys_model->number;
	memcpy_and_pad(id->mn, sizeof(id->mn), model, strlen(model), ' ');
	rcu_read_unlock();
}

static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
{
	struct nvmet_ctrl *ctrl = req->sq->ctrl;
	struct nvme_id_ctrl *id;
	u16 status = 0;
	const char model[] = "Linux";

	id = kzalloc(sizeof(*id), GFP_KERNEL);
	if (!id) {
@@ -342,7 +355,7 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
	memset(id->sn, ' ', sizeof(id->sn));
	bin2hex(id->sn, &ctrl->subsys->serial,
		min(sizeof(ctrl->subsys->serial), sizeof(id->sn) / 2));
	memcpy_and_pad(id->mn, sizeof(id->mn), model, sizeof(model) - 1, ' ');
	nvmet_id_set_model_number(id, ctrl->subsys);
	memcpy_and_pad(id->fr, sizeof(id->fr),
		       UTS_RELEASE, strlen(UTS_RELEASE), ' ');

+66 −0
Original line number Diff line number Diff line
@@ -919,12 +919,78 @@ out_unlock:
}
CONFIGFS_ATTR(nvmet_subsys_, attr_cntlid_max);

static ssize_t nvmet_subsys_attr_model_show(struct config_item *item,
					    char *page)
{
	struct nvmet_subsys *subsys = to_subsys(item);
	struct nvmet_subsys_model *subsys_model;
	char *model = NVMET_DEFAULT_CTRL_MODEL;
	int ret;

	rcu_read_lock();
	subsys_model = rcu_dereference(subsys->model);
	if (subsys_model)
		model = subsys_model->number;
	ret = snprintf(page, PAGE_SIZE, "%s\n", model);
	rcu_read_unlock();

	return ret;
}

/* See Section 1.5 of NVMe 1.4 */
static bool nvmet_is_ascii(const char c)
{
	return c >= 0x20 && c <= 0x7e;
}

static ssize_t nvmet_subsys_attr_model_store(struct config_item *item,
					     const char *page, size_t count)
{
	struct nvmet_subsys *subsys = to_subsys(item);
	struct nvmet_subsys_model *new_model;
	char *new_model_number;
	int pos = 0, len;

	len = strcspn(page, "\n");
	if (!len)
		return -EINVAL;

	for (pos = 0; pos < len; pos++) {
		if (!nvmet_is_ascii(page[pos]))
			return -EINVAL;
	}

	new_model_number = kstrndup(page, len, GFP_KERNEL);
	if (!new_model_number)
		return -ENOMEM;

	new_model = kzalloc(sizeof(*new_model) + len + 1, GFP_KERNEL);
	if (!new_model) {
		kfree(new_model_number);
		return -ENOMEM;
	}
	memcpy(new_model->number, new_model_number, len);

	down_write(&nvmet_config_sem);
	mutex_lock(&subsys->lock);
	new_model = rcu_replace_pointer(subsys->model, new_model,
					mutex_is_locked(&subsys->lock));
	mutex_unlock(&subsys->lock);
	up_write(&nvmet_config_sem);

	kfree_rcu(new_model, rcuhead);

	return count;
}
CONFIGFS_ATTR(nvmet_subsys_, attr_model);

static struct configfs_attribute *nvmet_subsys_attrs[] = {
	&nvmet_subsys_attr_attr_allow_any_host,
	&nvmet_subsys_attr_attr_version,
	&nvmet_subsys_attr_attr_serial,
	&nvmet_subsys_attr_attr_cntlid_min,
	&nvmet_subsys_attr_attr_cntlid_max,
	&nvmet_subsys_attr_attr_model,
	NULL,
};

+1 −0
Original line number Diff line number Diff line
@@ -1461,6 +1461,7 @@ static void nvmet_subsys_free(struct kref *ref)
	WARN_ON_ONCE(!list_empty(&subsys->namespaces));

	kfree(subsys->subsysnqn);
	kfree_rcu(subsys->model, rcuhead);
	kfree(subsys);
}

+8 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#define NVMET_ASYNC_EVENTS		4
#define NVMET_ERROR_LOG_SLOTS		128
#define NVMET_NO_ERROR_LOC		((u16)-1)
#define NVMET_DEFAULT_CTRL_MODEL	"Linux"

/*
 * Supported optional AENs:
@@ -202,6 +203,11 @@ struct nvmet_ctrl {
	struct nvme_error_slot	slots[NVMET_ERROR_LOG_SLOTS];
};

struct nvmet_subsys_model {
	struct rcu_head		rcuhead;
	char			number[];
};

struct nvmet_subsys {
	enum nvme_subsys_type	type;

@@ -229,6 +235,8 @@ struct nvmet_subsys {

	struct config_group	namespaces_group;
	struct config_group	allowed_hosts_group;

	struct nvmet_subsys_model	__rcu *model;
};

static inline struct nvmet_subsys *to_subsys(struct config_item *item)