[PATCH] nvmet: Add support for reservations.
Omri Mann
omri at excelero.com
Mon Sep 4 05:59:47 PDT 2017
Signed-off-by: Omri Mann <omri at excelero.com>
---
drivers/nvme/target/admin-cmd.c | 48 +++++
drivers/nvme/target/core.c | 24 +++
drivers/nvme/target/fabrics-cmd.c | 2 +-
drivers/nvme/target/io-cmd.c | 414 ++++++++++++++++++++++++++++++++++++++
drivers/nvme/target/nvmet.h | 29 +++
include/linux/nvme.h | 30 ++-
6 files changed, 545 insertions(+), 2 deletions(-)
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 3e1a255..fb88dd5 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -107,6 +107,35 @@ static u16 nvmet_get_smart_log(struct nvmet_req *req,
return status;
}
+static u16 nvmet_get_rsrv_log(struct nvmet_req *req,
+ struct nvme_reservation_log *rlog)
+{
+ u16 status = NVME_SC_SUCCESS;
+ int remaining;
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct nvmet_rsrv_log *p;
+
+ mutex_lock(&ctrl->lock);
+ ctrl->rsrv_log_async_sent = false;
+ if (!list_empty(&ctrl->rsrv_log)) {
+ remaining = 0;
+ list_for_each_entry(p, &ctrl->rsrv_log, link) {
+ if (++remaining == 256)
+ break;
+ }
+ rlog->num_avail = remaining - 1;
+ p = list_first_entry(&ctrl->rsrv_log,
+ struct nvmet_rsrv_log, link);
+ ++ctrl->rsrv_log_counter;
+ rlog->count = cpu_to_le64(ctrl->rsrv_log_counter);
+ rlog->type = p->log_type;
+ rlog->nsid = cpu_to_le32(p->nsid);
+ }
+ mutex_unlock(&ctrl->lock);
+
+ return status;
+}
+
static void nvmet_execute_get_log_page(struct nvmet_req *req)
{
struct nvme_smart_log *smart_log;
@@ -156,6 +185,13 @@ static void nvmet_execute_get_log_page(struct nvmet_req *req)
* still claim to fully implement this mandatory log page.
*/
break;
+ case NVME_LOG_RESERVATION:
+ if (data_len != sizeof(struct nvme_reservation_log)) {
+ status = NVME_SC_INTERNAL;
+ goto err;
+ }
+ status = nvmet_get_rsrv_log(req, buf);
+ break;
default:
BUG();
}
@@ -456,6 +492,12 @@ static void nvmet_execute_set_features(struct nvmet_req *req)
case NVME_FEAT_HOST_ID:
status = NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;
break;
+ case NVME_FEAT_RESV_MASK:
+ req->sq->ctrl->rsrv_mask =
+ le32_to_cpu(req->cmd->common.cdw10[1]);
+ break;
+ case NVME_FEAT_RESV_PERSIST:
+ break;
default:
status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
break;
@@ -511,6 +553,11 @@ static void nvmet_execute_get_features(struct nvmet_req *req)
status = nvmet_copy_to_sgl(req, 0,
req->sq->ctrl->hostid, sizeof(req->sq->ctrl->hostid));
break;
+ case NVME_FEAT_RESV_MASK:
+ nvmet_set_result(req, req->sq->ctrl->rsrv_mask);
+ break;
+ case NVME_FEAT_RESV_PERSIST:
+ break;
default:
status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
break;
@@ -565,6 +612,7 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
case NVME_LOG_ERROR:
case NVME_LOG_SMART:
case NVME_LOG_FW_SLOT:
+ case NVME_LOG_RESERVATION:
req->execute = nvmet_execute_get_log_page;
return 0;
}
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 7c23eaf..3904d7e 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -126,6 +126,27 @@ static void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,
schedule_work(&ctrl->async_event_work);
}
+void add_reservation_log_page(struct nvmet_ctrl *ctrl,
+ struct nvmet_ns *ns, int type)
+{
+ struct nvmet_rsrv_log *p;
+
+ if (ctrl->rsrv_mask & (1 << type))
+ return;
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL)
+ return;
+ p->nsid = ns->nsid;
+ p->log_type = type;
+ mutex_lock(&ctrl->lock);
+ list_add_tail(&p->link, &ctrl->rsrv_log);
+ mutex_unlock(&ctrl->lock);
+ if (!ctrl->rsrv_log_async_sent) {
+ ctrl->rsrv_log_async_sent = true;
+ nvmet_add_async_event(ctrl, 6, 0, NVME_LOG_RESERVATION);
+ }
+}
+
int nvmet_register_transport(struct nvmet_fabrics_ops *ops)
{
int ret = 0;
@@ -377,6 +398,8 @@ struct nvmet_ns *nvmet_ns_alloc(struct nvmet_subsys *subsys, u32 nsid)
INIT_LIST_HEAD(&ns->dev_link);
init_completion(&ns->disable_done);
+ INIT_LIST_HEAD(&ns->rsrv_list);
+ mutex_init(&ns->rsrv_lock);
ns->nsid = nsid;
ns->subsys = subsys;
@@ -847,6 +870,7 @@ static void nvmet_ctrl_free(struct kref *ref)
nvmet_stop_keep_alive_timer(ctrl);
mutex_lock(&subsys->lock);
+ nvmet_rsrv_remove_ctrl(ctrl);
list_del(&ctrl->subsys_entry);
mutex_unlock(&subsys->lock);
diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c
index 40c8682..a13624f 100644
--- a/drivers/nvme/target/fabrics-cmd.c
+++ b/drivers/nvme/target/fabrics-cmd.c
@@ -154,7 +154,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
le32_to_cpu(c->kato), &ctrl);
if (status)
goto out;
- memcpy(ctrl->hostid, d->hostid, sizeof(ctrl->hostid));
+ memcpy(ctrl->hostid, &d->hostid, sizeof(ctrl->hostid));
status = nvmet_install_queue(ctrl, req);
if (status) {
diff --git a/drivers/nvme/target/io-cmd.c b/drivers/nvme/target/io-cmd.c
index 0d4c23d..376cd8c 100644
--- a/drivers/nvme/target/io-cmd.c
+++ b/drivers/nvme/target/io-cmd.c
@@ -40,6 +40,60 @@ static void nvmet_inline_bio_init(struct nvmet_req *req)
bio_init(bio, req->inline_bvec, NVMET_MAX_INLINE_BIOVEC);
}
+static inline bool nvmet_ctrl_same_host(struct nvmet_ctrl *ctrl1,
+ struct nvmet_ctrl *ctrl2)
+{
+ static const u8 zero[sizeof(ctrl1->hostid)] = {0};
+
+ if (ctrl1 == NULL || ctrl2 == NULL)
+ return false;
+ if (ctrl1 == ctrl2)
+ return true;
+ if (memcmp(ctrl1->hostid, ctrl2->hostid, sizeof(ctrl1->hostid)) != 0)
+ return false;
+ if (memcmp(ctrl1->hostid, zero, sizeof(ctrl1->hostid)) == 0)
+ return false;
+ return true;
+}
+
+static struct nvmet_rsrv *nvmet_find_reservation(struct nvmet_req *req)
+{
+ struct nvmet_rsrv *p;
+
+ list_for_each_entry_rcu(p, &req->ns->rsrv_list, link) {
+ if (nvmet_ctrl_same_host(p->ctrl, req->sq->ctrl))
+ return p;
+ }
+ return NULL;
+}
+
+static bool nvmet_check_reservation(struct nvmet_req *req)
+{
+ bool success = true;
+
+ rcu_read_lock();
+ switch (req->ns->rsrv_type) {
+ case PR_WRITE_EXCLUSIVE:
+ if (req->cmd->rw.opcode == nvme_cmd_read)
+ goto out;
+ case PR_EXCLUSIVE_ACCESS:
+ success = nvmet_ctrl_same_host(req->ns->rsrv_ctrl,
+ req->sq->ctrl);
+ goto out;
+ case PR_WRITE_EXCLUSIVE_REG_ONLY:
+ case PR_WRITE_EXCLUSIVE_ALL_REGS:
+ if (req->cmd->rw.opcode == nvme_cmd_read)
+ goto out;
+ case PR_EXCLUSIVE_ACCESS_REG_ONLY:
+ case PR_EXCLUSIVE_ACCESS_ALL_REGS:
+ success = (nvmet_find_reservation(req) != NULL);
+ default:
+ goto out;
+ }
+out:
+ rcu_read_unlock();
+ return success;
+}
static void nvmet_execute_rw(struct nvmet_req *req)
{
int sg_cnt = req->sg_cnt;
@@ -196,6 +250,342 @@ static void nvmet_execute_write_zeroes(struct nvmet_req *req)
}
}
+
+static void nvmet_execute_resv_register(struct nvmet_req *req)
+{
+ struct nvmet_rsrv *p;
+ u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10[0]);
+ u16 status = NVME_SC_SUCCESS;
+ struct { u64 crkey, nrkey; } keys;
+
+ mutex_lock(&req->ns->rsrv_lock);
+ status = nvmet_copy_from_sgl(req, 0, &keys, sizeof(keys));
+ if (status)
+ goto out;
+ p = nvmet_find_reservation(req);
+ switch (cdw10 & 0x7) {
+ case 0: /* Register */
+ if (p == NULL) {
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p->ctrl = req->sq->ctrl;
+ p->rkey = keys.nrkey;
+ list_add_tail_rcu(&p->link, &req->ns->rsrv_list);
+ } else if (p->rkey != keys.nrkey) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ }
+ break;
+ case 1: /* Unregister */
+ if (p == NULL ||
+ (!(cdw10 & NVME_IEKEY) && p->rkey != keys.crkey)) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ goto out;
+ }
+ list_del_rcu(&p->link);
+ if (list_empty(&req->ns->rsrv_list) ||
+ nvmet_ctrl_same_host(req->ns->rsrv_ctrl, req->sq->ctrl)) {
+ req->ns->rsrv_type = 0;
+ req->ns->rsrv_ctrl = NULL;
+ }
+ kfree_rcu(p, rcu_head);
+ break;
+ case 2: /* Replace */
+ if (!(cdw10 & NVME_IEKEY) &&
+ (p == NULL || p->rkey != keys.crkey)) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ goto out;
+ }
+ if (p == NULL) {
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p->ctrl = req->sq->ctrl;
+ list_add_tail_rcu(&p->link, &req->ns->rsrv_list);
+ }
+ p->rkey = keys.nrkey;
+ break;
+ default:
+ status = NVME_SC_BAD_ATTRIBUTES | NVME_SC_DNR;
+ }
+ if (status == NVME_SC_SUCCESS)
+ ++req->ns->rsrv_gen;
+out:
+ mutex_unlock(&req->ns->rsrv_lock);
+ nvmet_req_complete(req, status);
+}
+
+static u16 nvmet_execute_resv_report_short(struct nvmet_req *req)
+{
+ struct nvme_reservation_status stat;
+ typeof(stat.regctl_ds[0]) regctl;
+ struct nvmet_rsrv *p;
+ u16 count = 0;
+ int index;
+ size_t buflen = 4*(le32_to_cpu(req->cmd->common.cdw10[0]) + 1);
+
+ if (!req->ns)
+ return NVME_SC_INVALID_NS | NVME_SC_DNR;
+ memset(&stat, 0, sizeof(stat));
+ mutex_lock(&req->ns->rsrv_lock);
+ list_for_each_entry(p, &req->ns->rsrv_list, link)
+ ++count;
+ stat.gen = cpu_to_le32(req->ns->rsrv_gen);
+ stat.rtype = req->ns->rsrv_type;
+ stat.regctl[0] = count & 0xff;
+ stat.regctl[1] = count >> 8;
+ stat.ptpls = 0;
+ nvmet_copy_to_sgl(req, 0, &stat, min(sizeof(stat), buflen));
+
+ memset(®ctl, 0, sizeof(regctl));
+ index = 1;
+ list_for_each_entry(p, &req->ns->rsrv_list, link) {
+ if (sizeof(regctl)*(index+1) > buflen)
+ break;
+ regctl.cntlid = p->ctrl->cntlid;
+ if (req->ns->rsrv_type != 0 && (req->ns->rsrv_ctrl == NULL ||
+ nvmet_ctrl_same_host(p->ctrl, req->ns->rsrv_ctrl)))
+ regctl.rcsts = 1;
+ else
+ regctl.rcsts = 0;
+ memcpy(®ctl.hostid, p->ctrl->hostid, sizeof(regctl.hostid));
+ regctl.rkey = p->rkey;
+ nvmet_copy_to_sgl(req, sizeof(regctl)*index, ®ctl,
+ sizeof(regctl));
+ ++index;
+ }
+ mutex_unlock(&req->ns->rsrv_lock);
+
+ return NVME_SC_SUCCESS;
+}
+
+static u16 nvmet_execute_resv_report_ext(struct nvmet_req *req)
+{
+ struct nvme_reservation_status_ext stat;
+ typeof(stat.regctl_eds[0]) regctl;
+ struct nvmet_rsrv *p;
+ u16 count = 0;
+ int index;
+ size_t buflen = 4*(le32_to_cpu(req->cmd->common.cdw10[0]) + 1);
+
+ if (!req->ns)
+ return NVME_SC_INVALID_NS | NVME_SC_DNR;
+ memset(&stat, 0, sizeof(stat));
+ mutex_lock(&req->ns->rsrv_lock);
+ list_for_each_entry(p, &req->ns->rsrv_list, link)
+ ++count;
+ stat.gen = cpu_to_le32(req->ns->rsrv_gen);
+ stat.rtype = req->ns->rsrv_type;
+ stat.regctl[0] = count & 0xff;
+ stat.regctl[1] = count >> 8;
+ stat.ptpls = 0;
+ nvmet_copy_to_sgl(req, 0, &stat, min(sizeof(stat), buflen));
+
+ memset(®ctl, 0, sizeof(regctl));
+ index = 1;
+ list_for_each_entry(p, &req->ns->rsrv_list, link) {
+ if (sizeof(regctl)*(index+1) > buflen)
+ break;
+ regctl.cntlid = p->ctrl->cntlid;
+ if (req->ns->rsrv_type != 0 && (req->ns->rsrv_ctrl == NULL ||
+ nvmet_ctrl_same_host(p->ctrl, req->ns->rsrv_ctrl)))
+ regctl.rcsts = 1;
+ else
+ regctl.rcsts = 0;
+ memcpy(®ctl.hostid, p->ctrl->hostid, sizeof(regctl.hostid));
+ regctl.rkey = p->rkey;
+ nvmet_copy_to_sgl(req, sizeof(regctl)*index, ®ctl,
+ sizeof(regctl));
+ ++index;
+ }
+ mutex_unlock(&req->ns->rsrv_lock);
+
+ return NVME_SC_SUCCESS;
+}
+
+static void nvmet_execute_resv_report(struct nvmet_req *req)
+{
+ u16 status;
+
+ if (le32_to_cpu(req->cmd->common.cdw10[1]) & 1)
+ status = nvmet_execute_resv_report_ext(req);
+ else
+ status = nvmet_execute_resv_report_short(req);
+ nvmet_req_complete(req, status);
+}
+
+static void nvmet_execute_resv_acquire(struct nvmet_req *req)
+{
+ struct nvmet_rsrv *p, *nextp;
+ u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10[0]);
+ u16 status = NVME_SC_SUCCESS;
+ u64 crkey;
+ u64 prkey;
+
+ mutex_lock(&req->ns->rsrv_lock);
+ p = nvmet_find_reservation(req);
+ if (p == NULL) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ goto out;
+ }
+ if (!(cdw10 & NVME_IEKEY)) {
+ status = nvmet_copy_from_sgl(req, 0, &crkey, sizeof(crkey));
+ if (status)
+ goto out;
+ if (p->rkey != crkey) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ goto out;
+ }
+ }
+ switch (cdw10 & 0x7) {
+ case 0: /* Aquire */
+ if (req->ns->rsrv_type != 0 &&
+ (req->ns->rsrv_type != ((cdw10 >> 8) & 0xff) ||
+ !nvmet_ctrl_same_host(req->ns->rsrv_ctrl, req->sq->ctrl))) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ goto out;
+ }
+ req->ns->rsrv_type = (cdw10 >> 8) & 0xff;
+ if (req->ns->rsrv_type != PR_WRITE_EXCLUSIVE_ALL_REGS &&
+ req->ns->rsrv_type != PR_EXCLUSIVE_ACCESS_ALL_REGS)
+ req->ns->rsrv_ctrl = req->sq->ctrl;
+ else
+ req->ns->rsrv_ctrl = NULL;
+ break;
+ case 2: /* Preempt and Abort */
+ /* We cannot abort, so we will sync the device to make sure
+ * all pending I/Os are done before we return
+ */
+ case 1: /* Preempt */
+ status = nvmet_copy_from_sgl(req, 8, &prkey, sizeof(prkey));
+ if (status)
+ goto out;
+ list_for_each_entry_safe(p, nextp, &req->ns->rsrv_list, link) {
+ if (p->rkey == prkey) {
+ list_del_rcu(&p->link);
+ add_reservation_log_page(req->sq->ctrl, req->ns,
+ 1);
+ kfree_rcu(p, rcu_head);
+ }
+ }
+ ++req->ns->rsrv_gen;
+ break;
+ default:
+ status = NVME_SC_BAD_ATTRIBUTES | NVME_SC_DNR;
+ goto out;
+ }
+out:
+ mutex_unlock(&req->ns->rsrv_lock);
+ if ((cdw10 & 0x7) == 2 && status == NVME_SC_SUCCESS)
+ sync_blockdev(req->ns->bdev); /* Preempt and abort */
+ nvmet_req_complete(req, status);
+}
+
+static void nvmet_execute_resv_release(struct nvmet_req *req)
+{
+ struct nvmet_rsrv *p;
+ u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10[0]);
+ u16 status;
+ u64 crkey;
+
+ mutex_lock(&req->ns->rsrv_lock);
+ p = nvmet_find_reservation(req);
+ if (p == NULL) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ goto out;
+ }
+ if (!(cdw10 & NVME_IEKEY)) {
+ status = nvmet_copy_from_sgl(req, 0, &crkey, sizeof(crkey));
+ if (status)
+ goto out;
+ if (p->rkey != crkey) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ goto out;
+ }
+ }
+ if ((cdw10 & 0x7) == 0) { /* Relase */
+ if (req->ns->rsrv_type == 0) {
+ status = NVME_SC_SUCCESS;
+ goto out;
+ }
+ if (((cdw10 >> 8) & 0xff) != req->ns->rsrv_type) {
+ status = NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
+ goto out;
+ }
+ if (req->ns->rsrv_type != PR_WRITE_EXCLUSIVE_ALL_REGS &&
+ req->ns->rsrv_type != PR_EXCLUSIVE_ACCESS_ALL_REGS &&
+ !nvmet_ctrl_same_host(req->ns->rsrv_ctrl, req->sq->ctrl)) {
+ /* We are not the reservation holders,
+ * silently ignore relase request.
+ */
+ status = NVME_SC_SUCCESS;
+ goto out;
+ }
+ } else if ((cdw10 & 0x7) == 1) { /* Clear */
+ while (!list_empty(&req->ns->rsrv_list)) {
+ p = list_first_entry(&req->ns->rsrv_list,
+ struct nvmet_rsrv, link);
+ if (!nvmet_ctrl_same_host(p->ctrl, req->sq->ctrl)) {
+ /* Registration Preempted notification */
+ add_reservation_log_page(p->ctrl, req->ns, 1);
+ }
+ list_del_rcu(&p->link);
+ kfree_rcu(p, rcu_head);
+ }
+ ++req->ns->rsrv_gen;
+ } else {
+ status = NVME_SC_BAD_ATTRIBUTES | NVME_SC_DNR;
+ goto out;
+ }
+ if (req->ns->rsrv_type != PR_WRITE_EXCLUSIVE &&
+ req->ns->rsrv_type != PR_EXCLUSIVE_ACCESS) {
+ list_for_each_entry(p, &req->ns->rsrv_list, link) {
+ if (!nvmet_ctrl_same_host(p->ctrl, req->sq->ctrl)) {
+ /* Reservation Released notification */
+ add_reservation_log_page(p->ctrl, req->ns, 2);
+ }
+ }
+ }
+ req->ns->rsrv_type = 0;
+ req->ns->rsrv_ctrl = NULL;
+out:
+ mutex_unlock(&req->ns->rsrv_lock);
+ nvmet_req_complete(req, status);
+}
+
+void nvmet_rsrv_remove_ctrl(struct nvmet_ctrl *ctrl)
+{
+ struct nvmet_subsys *subsys = ctrl->subsys;
+ struct nvmet_ctrl *c, *alt_ctrl = NULL;
+ struct nvmet_ns *ns;
+ struct nvmet_rsrv *r, *nr;
+
+ list_for_each_entry(c, &subsys->ctrls, subsys_entry) {
+ if (c != ctrl && nvmet_ctrl_same_host(c, ctrl)) {
+ alt_ctrl = c;
+ break;
+ }
+ }
+ list_for_each_entry(ns, &subsys->namespaces, dev_link) {
+ mutex_lock(&ns->rsrv_lock);
+ list_for_each_entry_safe(r, nr, &ns->rsrv_list, link) {
+ if (r->ctrl == ctrl) {
+ if (alt_ctrl != NULL)
+ r->ctrl = alt_ctrl;
+ else {
+ list_del_rcu(&r->link);
+ kfree_rcu(r, rcu_head);
+ }
+ }
+ }
+ if (list_empty(&ns->rsrv_list)) {
+ ns->rsrv_ctrl = NULL;
+ ns->rsrv_type = 0;
+ } else if (ns->rsrv_ctrl == ctrl) {
+ ns->rsrv_ctrl = alt_ctrl;
+ if (alt_ctrl == NULL)
+ ns->rsrv_type = 0;
+ }
+ mutex_unlock(&ns->rsrv_lock);
+ }
+}
+
u16 nvmet_parse_io_cmd(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;
@@ -214,21 +604,45 @@ u16 nvmet_parse_io_cmd(struct nvmet_req *req)
switch (cmd->common.opcode) {
case nvme_cmd_read:
case nvme_cmd_write:
+ if (!nvmet_check_reservation(req))
+ return NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
req->execute = nvmet_execute_rw;
req->data_len = nvmet_rw_len(req);
return 0;
case nvme_cmd_flush:
+ if (!nvmet_check_reservation(req))
+ return NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
req->execute = nvmet_execute_flush;
req->data_len = 0;
return 0;
case nvme_cmd_dsm:
+ if (!nvmet_check_reservation(req))
+ return NVME_SC_RESERVATION_CONFLICT | 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 (!nvmet_check_reservation(req))
+ return NVME_SC_RESERVATION_CONFLICT | NVME_SC_DNR;
req->execute = nvmet_execute_write_zeroes;
return 0;
+ case nvme_cmd_resv_register:
+ req->execute = nvmet_execute_resv_register;
+ req->data_len = 16;
+ return 0;
+ case nvme_cmd_resv_report:
+ req->execute = nvmet_execute_resv_report;
+ req->data_len = 4*(le32_to_cpu(cmd->common.cdw10[0])+1);
+ return 0;
+ case nvme_cmd_resv_acquire:
+ req->execute = nvmet_execute_resv_acquire;
+ req->data_len = 16;
+ return 0;
+ case nvme_cmd_resv_release:
+ req->execute = nvmet_execute_resv_release;
+ req->data_len = 8;
+ return 0;
default:
pr_err("unhandled cmd %d on qid %d\n", cmd->common.opcode,
req->sq->qid);
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 918c557..bcf3eec 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -26,6 +26,7 @@
#include <linux/configfs.h>
#include <linux/rcupdate.h>
#include <linux/blkdev.h>
+#include <linux/pr.h>
#define NVMET_ASYNC_EVENTS 4
#define NVMET_ERROR_LOG_SLOTS 128
@@ -57,6 +58,12 @@ struct nvmet_ns {
struct config_group group;
struct completion disable_done;
+
+ struct mutex rsrv_lock;
+ struct list_head rsrv_list;
+ struct nvmet_ctrl *rsrv_ctrl;
+ enum pr_type rsrv_type;
+ u32 rsrv_gen;
};
static inline struct nvmet_ns *to_nvmet_ns(struct config_item *item)
@@ -134,6 +141,11 @@ struct nvmet_ctrl {
char subsysnqn[NVMF_NQN_FIELD_LEN];
char hostnqn[NVMF_NQN_FIELD_LEN];
+
+ struct list_head rsrv_log;
+ u64 rsrv_log_counter;
+ u32 rsrv_mask;
+ bool rsrv_log_async_sent;
};
struct nvmet_subsys {
@@ -231,6 +243,19 @@ struct nvmet_req {
struct nvmet_fabrics_ops *ops;
};
+struct nvmet_rsrv {
+ struct list_head link;
+ struct nvmet_ctrl *ctrl;
+ u64 rkey;
+ struct rcu_head rcu_head;
+};
+
+struct nvmet_rsrv_log {
+ struct list_head link;
+ u32 nsid;
+ u8 log_type;
+};
+
static inline void nvmet_set_status(struct nvmet_req *req, u16 status)
{
req->rsp->status = cpu_to_le16(status << 1);
@@ -332,4 +357,8 @@ u16 nvmet_copy_from_sgl(struct nvmet_req *req, off_t off, void *buf,
bool nvmet_host_allowed(struct nvmet_req *req, struct nvmet_subsys *subsys,
const char *hostnqn);
+void add_reservation_log_page(struct nvmet_ctrl *ctrl, struct nvmet_ns *ns,
+ int type);
+void nvmet_rsrv_remove_ctrl(struct nvmet_ctrl *ctrl);
+
#endif /* _NVMET_H */
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 05560c2..bcf9184 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -393,6 +393,15 @@ struct nvme_fw_slot_info_log {
__u8 rsvd64[448];
};
+struct nvme_reservation_log {
+ __le64 count;
+ __u8 type;
+ __u8 num_avail;
+ __u16 rsvd2;
+ __le32 nsid;
+ __u8 rsvd48[48];
+};
+
enum {
NVME_SMART_CRIT_SPARE = 1 << 0,
NVME_SMART_CRIT_TEMPERATURE = 1 << 1,
@@ -432,7 +441,7 @@ struct nvme_reservation_status {
__u8 regctl[2];
__u8 resv5[2];
__u8 ptpls;
- __u8 resv10[13];
+ __u8 resv10[14];
struct {
__le16 cntlid;
__u8 rcsts;
@@ -442,6 +451,24 @@ struct nvme_reservation_status {
} regctl_ds[];
};
+struct nvme_reservation_status_ext {
+ __le32 gen;
+ __u8 rtype;
+ __u8 regctl[2];
+ __u8 resv5[2];
+ __u8 ptpls;
+ __u8 resv10[14];
+ __u8 resv24[40];
+ struct {
+ __le16 cntlid;
+ __u8 rcsts;
+ __u8 resv3[5];
+ __le64 rkey;
+ __u8 hostid[16];
+ __u8 resv32[32];
+ } regctl_eds[];
+};
+
enum nvme_async_event_type {
NVME_AER_TYPE_ERROR = 0,
NVME_AER_TYPE_SMART = 1,
@@ -709,6 +736,7 @@ enum {
NVME_FWACT_REPL = (0 << 3),
NVME_FWACT_REPL_ACTV = (1 << 3),
NVME_FWACT_ACTV = (2 << 3),
+ NVME_IEKEY = (1 << 3),
};
struct nvme_identify {
--
1.8.3.1
More information about the Linux-nvme
mailing list