[PATCH rfc 2/2] nvmet: expose option to emulate a nvm subsystem in read-only mode

Sagi Grimberg sagi at grimberg.me
Mon Dec 4 01:24:35 PST 2017


Useful for exercising the host. Also, if a host connects to
a subsystem which is read-only to begin with, queue an AER
to let it know about it.

In read-only state, we fail writes/dsms/write-zeros.

Signed-off-by: Sagi Grimberg <sagi at grimberg.me>
---
 drivers/nvme/target/admin-cmd.c   |  3 +++
 drivers/nvme/target/configfs.c    | 34 ++++++++++++++++++++++++++++++++++
 drivers/nvme/target/core.c        |  2 +-
 drivers/nvme/target/fabrics-cmd.c |  3 +++
 drivers/nvme/target/io-cmd.c      |  8 +++++++-
 drivers/nvme/target/nvmet.h       |  3 +++
 6 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 6b921c253618..aab646f44a66 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -69,6 +69,9 @@ static u16 nvmet_get_smart_log_all(struct nvmet_req *req,
 
 	ctrl = req->sq->ctrl;
 
+	if(req->sq->ctrl->subsys->read_only)
+		slog->critical_warning |= NVME_SMART_CRIT_MEDIA;
+
 	rcu_read_lock();
 	list_for_each_entry_rcu(ns, &ctrl->subsys->namespaces, dev_link) {
 		host_reads += part_stat_read(ns->bdev->bd_part, ios[READ]);
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 8f378030c8e4..c966228439f6 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -711,6 +711,39 @@ static ssize_t nvmet_subsys_attr_allow_any_host_store(struct config_item *item,
 
 CONFIGFS_ATTR(nvmet_subsys_, attr_allow_any_host);
 
+static ssize_t nvmet_subsys_attr_ro_show(struct config_item *item,
+		char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%d\n",
+		to_subsys(item)->read_only);
+}
+
+static ssize_t nvmet_subsys_attr_ro_store(struct config_item *item,
+		const char *page, size_t count)
+{
+	struct nvmet_subsys *subsys = to_subsys(item);
+	bool subsys_ro = subsys->read_only;
+	bool ro;
+
+	if (strtobool(page, &ro))
+		return -EINVAL;
+
+	subsys->read_only = ro;
+	if ((!subsys_ro && ro) || (subsys_ro && !ro)) {
+		struct nvmet_ctrl *ctrl;
+
+		/* read only state change, send a SMART AER */
+		mutex_lock(&subsys->lock);
+		list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
+			nvmet_add_async_event(ctrl, NVME_AER_SMART, 0, 0);
+		mutex_unlock(&subsys->lock);
+	}
+
+	return count;
+}
+
+CONFIGFS_ATTR(nvmet_subsys_, attr_ro);
+
 static ssize_t nvmet_subsys_attr_version_show(struct config_item *item,
 					      char *page)
 {
@@ -770,6 +803,7 @@ CONFIGFS_ATTR(nvmet_subsys_, attr_serial);
 
 static struct configfs_attribute *nvmet_subsys_attrs[] = {
 	&nvmet_subsys_attr_attr_allow_any_host,
+	&nvmet_subsys_attr_attr_ro,
 	&nvmet_subsys_attr_attr_version,
 	&nvmet_subsys_attr_attr_serial,
 	NULL,
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 841a37dfb987..5b4d30db9776 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -117,7 +117,7 @@ static void nvmet_async_event_work(struct work_struct *work)
 	}
 }
 
-static void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,
+void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,
 		u8 event_info, u8 log_page)
 {
 	struct nvmet_async_event *aen;
diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c
index 62315cc4b5b0..fc9ef44d2b49 100644
--- a/drivers/nvme/target/fabrics-cmd.c
+++ b/drivers/nvme/target/fabrics-cmd.c
@@ -177,6 +177,9 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
 		goto out;
 	}
 
+	if(req->sq->ctrl->subsys->read_only)
+		nvmet_add_async_event(req->sq->ctrl, NVME_AER_SMART, 0, 0);
+
 	pr_info("creating controller %d for subsystem %s for NQN %s.\n",
 		ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn);
 	req->rsp->result.u16 = cpu_to_le16(ctrl->cntlid);
diff --git a/drivers/nvme/target/io-cmd.c b/drivers/nvme/target/io-cmd.c
index 0a4372a016f2..6f121d2f2ea3 100644
--- a/drivers/nvme/target/io-cmd.c
+++ b/drivers/nvme/target/io-cmd.c
@@ -202,8 +202,10 @@ u16 nvmet_parse_io_cmd(struct nvmet_req *req)
 		return NVME_SC_INVALID_NS | NVME_SC_DNR;
 
 	switch (cmd->common.opcode) {
-	case nvme_cmd_read:
 	case nvme_cmd_write:
+		if(unlikely(req->sq->ctrl->subsys->read_only))
+			return NVME_SC_READ_ONLY | NVME_SC_DNR;
+	case nvme_cmd_read:
 		req->execute = nvmet_execute_rw;
 		req->data_len = nvmet_rw_len(req);
 		return 0;
@@ -212,11 +214,15 @@ u16 nvmet_parse_io_cmd(struct nvmet_req *req)
 		req->data_len = 0;
 		return 0;
 	case nvme_cmd_dsm:
+		if(unlikely(req->sq->ctrl->subsys->read_only))
+			return NVME_SC_READ_ONLY | NVME_SC_DNR;
 		req->execute = nvmet_execute_dsm;
 		req->data_len = (le32_to_cpu(cmd->dsm.nr) + 1) *
 			sizeof(struct nvme_dsm_range);
 		return 0;
 	case nvme_cmd_write_zeroes:
+		if(unlikely(req->sq->ctrl->subsys->read_only))
+			return NVME_SC_READ_ONLY | NVME_SC_DNR;
 		req->execute = nvmet_execute_write_zeroes;
 		return 0;
 	default:
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index d69d948c8f36..a9fb763020b6 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -144,6 +144,7 @@ struct nvmet_ctrl {
 
 struct nvmet_subsys {
 	enum nvme_subsys_type	type;
+	bool			read_only;
 
 	struct mutex		lock;
 	struct kref		ref;
@@ -287,6 +288,8 @@ void nvmet_sq_destroy(struct nvmet_sq *sq);
 int nvmet_sq_init(struct nvmet_sq *sq);
 
 void nvmet_ctrl_fatal_error(struct nvmet_ctrl *ctrl);
+void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,
+		u8 event_info, u8 log_page);
 
 void nvmet_update_cc(struct nvmet_ctrl *ctrl, u32 new);
 u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
-- 
2.14.1




More information about the Linux-nvme mailing list