[PATCH 2/2] nvmet: implement persistent read-only namespace feature
Hannes Reinecke
hare at kernel.org
Thu Apr 3 07:47:47 PDT 2025
Per default we try to open the 'device_path' read-write, which might
fail if the device is read-only. So retry with opening read-only, and
implement a new 'persistent_ro' flag if we did so.
And map that flag onto the 'Permanently Write Protect' setting in the
write protection feature.
Signed-off-by: Hannes Reinecke <hare at kernel.org>
---
drivers/nvme/target/admin-cmd.c | 15 +++++++++++----
drivers/nvme/target/configfs.c | 5 +++++
drivers/nvme/target/core.c | 2 ++
drivers/nvme/target/io-cmd-bdev.c | 11 +++++++++++
drivers/nvme/target/io-cmd-file.c | 21 +++++++++++++++++----
drivers/nvme/target/nvmet.h | 1 +
6 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 43f819b0f89b..2fea7ab05408 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -1098,7 +1098,7 @@ static void nvmet_execute_id_cs_indep(struct nvmet_req *req)
id->nstat = NVME_NSTAT_NRDY;
id->anagrpid = cpu_to_le32(req->ns->anagrpid);
id->nmic = NVME_NS_NMIC_SHARED;
- if (req->ns->readonly)
+ if (req->ns->readonly || req->ns->persistent_ro)
id->nsattr |= NVME_NS_ATTR_RO;
if (req->ns->bdev && !bdev_nonrot(req->ns->bdev))
id->nsfeat |= NVME_NS_ROTATIONAL;
@@ -1225,8 +1225,13 @@ static u16 nvmet_set_feat_write_protect(struct nvmet_req *req)
req->ns->readonly = false;
break;
case NVME_NS_NO_WRITE_PROTECT:
- req->ns->readonly = false;
- status = 0;
+ if (req->ns->persistent_ro)
+ status = NVME_SC_FEATURE_NOT_CHANGEABLE | \
+ NVME_STATUS_DNR;
+ else {
+ req->ns->readonly = false;
+ status = 0;
+ }
break;
default:
break;
@@ -1418,7 +1423,9 @@ static u16 nvmet_get_feat_write_protect(struct nvmet_req *req)
return result;
mutex_lock(&subsys->lock);
- if (req->ns->readonly == true)
+ if (req->ns->persistent_ro == true)
+ result = NVME_NS_WRITE_PROTECT_PERMANENT;
+ else if (req->ns->readonly == true)
result = NVME_NS_WRITE_PROTECT;
else
result = NVME_NS_NO_WRITE_PROTECT;
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index cb03b448ae6d..1efe1ed2ae50 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -840,6 +840,11 @@ static ssize_t nvmet_ns_readonly_store(struct config_item *item,
mutex_unlock(&ns->subsys->lock);
return -EINVAL;
}
+ if (ns->persistent_ro && val == false) {
+ pr_err("the ns:%d is permanently read-only\n", ns->nsid);
+ mutex_unlock(&ns->subsys->lock);
+ return -EACCES;
+ }
ns->readonly = val;
mutex_unlock(&ns->subsys->lock);
return count;
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 2e741696f371..42605c67cbeb 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -725,6 +725,8 @@ struct nvmet_ns *nvmet_ns_alloc(struct nvmet_subsys *subsys, u32 nsid)
uuid_gen(&ns->uuid);
ns->buffered_io = false;
+ ns->readonly = false;
+ ns->persistent_ro = false;
ns->csi = NVME_CSI_NVM;
return ns;
diff --git a/drivers/nvme/target/io-cmd-bdev.c b/drivers/nvme/target/io-cmd-bdev.c
index 83be0657e6df..f28dfac2b9fe 100644
--- a/drivers/nvme/target/io-cmd-bdev.c
+++ b/drivers/nvme/target/io-cmd-bdev.c
@@ -91,6 +91,17 @@ int nvmet_bdev_ns_enable(struct nvmet_ns *ns)
BLK_OPEN_READ | BLK_OPEN_WRITE, NULL, NULL);
if (IS_ERR(ns->bdev_file)) {
ret = PTR_ERR(ns->bdev_file);
+ if (ret == -EACCES) {
+ ns->bdev_file = bdev_file_open_by_path(ns->device_path,
+ BLK_OPEN_READ, NULL, NULL);
+ if (IS_ERR(ns->bdev_file)) {
+ ret = PTR_ERR(ns->bdev_file);
+ } else {
+ ns->readonly = true;
+ ns->persistent_ro = true;
+ ret = 0;
+ }
+ }
if (ret != -ENOTBLK) {
pr_err("failed to open block device %s: (%d)\n",
ns->device_path, ret);
diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 2d068439b129..770dec653bab 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -41,10 +41,23 @@ int nvmet_file_ns_enable(struct nvmet_ns *ns)
ns->file = filp_open(ns->device_path, flags, 0);
if (IS_ERR(ns->file)) {
ret = PTR_ERR(ns->file);
- pr_err("failed to open file %s: (%d)\n",
- ns->device_path, ret);
- ns->file = NULL;
- return ret;
+ if (ret == -EACCES) {
+ flags = O_RDONLY | O_LARGEFILE;
+ ns->file = filp_open(ns->device_path, flags, 0);
+ if (IS_ERR(ns->file)) {
+ ret = PTR_ERR(ns->file);
+ } else {
+ ns->readonly = true;
+ ns->persistent_ro = true;
+ ret = 0;
+ }
+ }
+ if (ret) {
+ pr_err("failed to open file %s: (%d)\n",
+ ns->device_path, ret);
+ ns->file = NULL;
+ return ret;
+ }
}
nvmet_file_ns_revalidate(ns);
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 29ae0657ba8f..166add3c119f 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -103,6 +103,7 @@ struct nvmet_ns {
struct block_device *bdev;
struct file *file;
bool readonly;
+ bool persistent_ro;
u32 nsid;
u32 blksize_shift;
loff_t size;
--
2.35.3
More information about the Linux-nvme
mailing list