// SPDX-License-Identifier: GPL-2.0
/*
* NVMe over Fabrics Persist Reservation.
* Copyright (c) 2024 Guixin Liu, Alibaba Group.
* All rights reserved.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/unaligned.h>
#include "nvmet.h"
#define NVMET_PR_NOTIFI_MASK_ALL \
(1 << NVME_PR_NOTIFY_BIT_REG_PREEMPTED | \
1 << NVME_PR_NOTIFY_BIT_RESV_RELEASED | \
1 << NVME_PR_NOTIFY_BIT_RESV_PREEMPTED)
static inline bool nvmet_pr_parse_ignore_key(u32 cdw10)
{
/* Ignore existing key, bit 03. */
return (cdw10 >> 3) & 1;
}
static inline struct nvmet_ns *nvmet_pr_to_ns(struct nvmet_pr *pr)
{
return container_of(pr, struct nvmet_ns, pr);
}
static struct nvmet_pr_registrant *
nvmet_pr_find_registrant(struct nvmet_pr *pr, uuid_t *hostid)
{
struct nvmet_pr_registrant *reg;
list_for_each_entry_rcu(reg, &pr->registrant_list, entry) {
if (uuid_equal(®->hostid, hostid))
return reg;
}
return NULL;
}
u16 nvmet_set_feat_resv_notif_mask(struct nvmet_req *req, u32 mask)
{
u32 nsid = le32_to_cpu(req->cmd->common.nsid);
struct nvmet_ctrl *ctrl = req->sq->ctrl;
struct nvmet_ns *ns;
unsigned long idx;
u16 status;
if (mask & ~(NVMET_PR_NOTIFI_MASK_ALL)) {
req->error_loc = offsetof(struct nvme_common_command, cdw11);
return NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
}
if (nsid != U32_MAX) {
status = nvmet_req_find_ns(req);
if (status)
return status;
if (!req->ns->pr.enable)
return NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
WRITE_ONCE(req->ns->pr.notify_mask, mask);
goto success;
}
nvmet_for_each_enabled_ns(&ctrl->subsys->namespaces, idx, ns) {
if (ns->pr.enable)
WRITE_ONCE(ns->pr.notify_mask, mask);
}
success:
nvmet_set_result(req, mask);
return NVME_SC_SUCCESS;
}
u16 nvmet_get_feat_resv_notif_mask(struct nvmet_req *req)
{
u16 status;
status = nvmet_req_find_ns(req);
if (status)
return status;
if (!req->ns->pr.enable)
return NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
nvmet_set_result(req, READ_ONCE(req->ns->pr.notify_mask));
return status;
}
void nvmet_execute_get_log_page_resv(struct nvmet_req *req)
{
struct nvmet_pr_log_mgr *log_mgr = &req->sq->ctrl->pr_log_mgr;
struct nvme_pr_log next_log = {0};
struct nvme_pr_log log = {0};
u16 status = NVME_SC_SUCCESS;
u64 lost_count;
u64 cur_count;
u64 next_count;
mutex_lock(&log_mgr->lock);
if (!kfifo_get(&log_mgr->log_queue, &log))
goto out;
/*
* We can't get the last in kfifo.
* Utilize the current count and the count from the next log to
* calculate the number of lost logs, while also addressing cases
* of overflow. If there is no subsequent log, the number of lost
* logs is equal to the lost_count within the nvmet_pr_log_mgr.
*/
cur_count =