nvmet: implement the changed namespaces log

Just keep a per-controller buffer of changed namespaces and copy it out
in the get log page implementation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Christoph Hellwig 2018-05-25 17:16:09 +02:00
parent 8ab0805f11
commit c16734ea98
3 changed files with 76 additions and 9 deletions

View File

@ -126,6 +126,29 @@ static void nvmet_execute_get_log_page_smart(struct nvmet_req *req)
nvmet_req_complete(req, status); nvmet_req_complete(req, status);
} }
static void nvmet_execute_get_log_changed_ns(struct nvmet_req *req)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
u16 status = NVME_SC_INTERNAL;
size_t len;
if (req->data_len != NVME_MAX_CHANGED_NAMESPACES * sizeof(__le32))
goto out;
mutex_lock(&ctrl->lock);
if (ctrl->nr_changed_ns == U32_MAX)
len = sizeof(__le32);
else
len = ctrl->nr_changed_ns * sizeof(__le32);
status = nvmet_copy_to_sgl(req, 0, ctrl->changed_ns_list, len);
if (!status)
status = nvmet_zero_sgl(req, len, req->data_len - len);
ctrl->nr_changed_ns = 0;
mutex_unlock(&ctrl->lock);
out:
nvmet_req_complete(req, status);
}
static void nvmet_execute_identify_ctrl(struct nvmet_req *req) static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
{ {
struct nvmet_ctrl *ctrl = req->sq->ctrl; struct nvmet_ctrl *ctrl = req->sq->ctrl;
@ -543,6 +566,9 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
*/ */
req->execute = nvmet_execute_get_log_page_noop; req->execute = nvmet_execute_get_log_page_noop;
return 0; return 0;
case NVME_LOG_CHANGED_NS:
req->execute = nvmet_execute_get_log_changed_ns;
return 0;
} }
break; break;
case nvme_admin_identify: case nvme_admin_identify:

View File

@ -144,6 +144,42 @@ static void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,
schedule_work(&ctrl->async_event_work); schedule_work(&ctrl->async_event_work);
} }
static void nvmet_add_to_changed_ns_log(struct nvmet_ctrl *ctrl, __le32 nsid)
{
u32 i;
mutex_lock(&ctrl->lock);
if (ctrl->nr_changed_ns > NVME_MAX_CHANGED_NAMESPACES)
goto out_unlock;
for (i = 0; i < ctrl->nr_changed_ns; i++) {
if (ctrl->changed_ns_list[i] == nsid)
goto out_unlock;
}
if (ctrl->nr_changed_ns == NVME_MAX_CHANGED_NAMESPACES) {
ctrl->changed_ns_list[0] = cpu_to_le32(0xffffffff);
ctrl->nr_changed_ns = U32_MAX;
goto out_unlock;
}
ctrl->changed_ns_list[ctrl->nr_changed_ns++] = nsid;
out_unlock:
mutex_unlock(&ctrl->lock);
}
static void nvmet_ns_changed(struct nvmet_subsys *subsys, u32 nsid)
{
struct nvmet_ctrl *ctrl;
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) {
nvmet_add_to_changed_ns_log(ctrl, cpu_to_le32(nsid));
nvmet_add_async_event(ctrl, NVME_AER_TYPE_NOTICE,
NVME_AER_NOTICE_NS_CHANGED,
NVME_LOG_CHANGED_NS);
}
}
int nvmet_register_transport(const struct nvmet_fabrics_ops *ops) int nvmet_register_transport(const struct nvmet_fabrics_ops *ops)
{ {
int ret = 0; int ret = 0;
@ -287,7 +323,6 @@ static void nvmet_ns_dev_disable(struct nvmet_ns *ns)
int nvmet_ns_enable(struct nvmet_ns *ns) int nvmet_ns_enable(struct nvmet_ns *ns)
{ {
struct nvmet_subsys *subsys = ns->subsys; struct nvmet_subsys *subsys = ns->subsys;
struct nvmet_ctrl *ctrl;
int ret = 0; int ret = 0;
mutex_lock(&subsys->lock); mutex_lock(&subsys->lock);
@ -326,9 +361,7 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
list_add_tail_rcu(&ns->dev_link, &old->dev_link); list_add_tail_rcu(&ns->dev_link, &old->dev_link);
} }
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) nvmet_ns_changed(subsys, ns->nsid);
nvmet_add_async_event(ctrl, NVME_AER_TYPE_NOTICE, 0, 0);
ns->enabled = true; ns->enabled = true;
ret = 0; ret = 0;
out_unlock: out_unlock:
@ -342,7 +375,6 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
void nvmet_ns_disable(struct nvmet_ns *ns) void nvmet_ns_disable(struct nvmet_ns *ns)
{ {
struct nvmet_subsys *subsys = ns->subsys; struct nvmet_subsys *subsys = ns->subsys;
struct nvmet_ctrl *ctrl;
mutex_lock(&subsys->lock); mutex_lock(&subsys->lock);
if (!ns->enabled) if (!ns->enabled)
@ -368,9 +400,7 @@ void nvmet_ns_disable(struct nvmet_ns *ns)
percpu_ref_exit(&ns->ref); percpu_ref_exit(&ns->ref);
mutex_lock(&subsys->lock); mutex_lock(&subsys->lock);
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) nvmet_ns_changed(subsys, ns->nsid);
nvmet_add_async_event(ctrl, NVME_AER_TYPE_NOTICE, 0, 0);
nvmet_ns_dev_disable(ns); nvmet_ns_dev_disable(ns);
out_unlock: out_unlock:
mutex_unlock(&subsys->lock); mutex_unlock(&subsys->lock);
@ -832,11 +862,16 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
kref_init(&ctrl->ref); kref_init(&ctrl->ref);
ctrl->subsys = subsys; ctrl->subsys = subsys;
ctrl->changed_ns_list = kmalloc_array(NVME_MAX_CHANGED_NAMESPACES,
sizeof(__le32), GFP_KERNEL);
if (!ctrl->changed_ns_list)
goto out_free_ctrl;
ctrl->cqs = kcalloc(subsys->max_qid + 1, ctrl->cqs = kcalloc(subsys->max_qid + 1,
sizeof(struct nvmet_cq *), sizeof(struct nvmet_cq *),
GFP_KERNEL); GFP_KERNEL);
if (!ctrl->cqs) if (!ctrl->cqs)
goto out_free_ctrl; goto out_free_changed_ns_list;
ctrl->sqs = kcalloc(subsys->max_qid + 1, ctrl->sqs = kcalloc(subsys->max_qid + 1,
sizeof(struct nvmet_sq *), sizeof(struct nvmet_sq *),
@ -894,6 +929,8 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
kfree(ctrl->sqs); kfree(ctrl->sqs);
out_free_cqs: out_free_cqs:
kfree(ctrl->cqs); kfree(ctrl->cqs);
out_free_changed_ns_list:
kfree(ctrl->changed_ns_list);
out_free_ctrl: out_free_ctrl:
kfree(ctrl); kfree(ctrl);
out_put_subsystem: out_put_subsystem:
@ -920,6 +957,7 @@ static void nvmet_ctrl_free(struct kref *ref)
kfree(ctrl->sqs); kfree(ctrl->sqs);
kfree(ctrl->cqs); kfree(ctrl->cqs);
kfree(ctrl->changed_ns_list);
kfree(ctrl); kfree(ctrl);
nvmet_subsys_put(subsys); nvmet_subsys_put(subsys);

View File

@ -135,6 +135,9 @@ struct nvmet_ctrl {
const struct nvmet_fabrics_ops *ops; const struct nvmet_fabrics_ops *ops;
__le32 *changed_ns_list;
u32 nr_changed_ns;
char subsysnqn[NVMF_NQN_FIELD_LEN]; char subsysnqn[NVMF_NQN_FIELD_LEN];
char hostnqn[NVMF_NQN_FIELD_LEN]; char hostnqn[NVMF_NQN_FIELD_LEN];
}; };