|
@@ -72,6 +72,9 @@ static DEFINE_IDA(nvme_instance_ida);
|
|
|
static dev_t nvme_chr_devt;
|
|
|
static struct class *nvme_class;
|
|
|
|
|
|
+static void nvme_ns_remove(struct nvme_ns *ns);
|
|
|
+static int nvme_revalidate_disk(struct gendisk *disk);
|
|
|
+
|
|
|
static __le32 nvme_get_log_dw10(u8 lid, size_t size)
|
|
|
{
|
|
|
return cpu_to_le32((((size / 4) - 1) << 16) | lid);
|
|
@@ -992,12 +995,87 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
|
|
|
metadata, meta_len, io.slba, NULL, 0);
|
|
|
}
|
|
|
|
|
|
+static u32 nvme_known_admin_effects(u8 opcode)
|
|
|
+{
|
|
|
+ switch (opcode) {
|
|
|
+ case nvme_admin_format_nvm:
|
|
|
+ return NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC |
|
|
|
+ NVME_CMD_EFFECTS_CSE_MASK;
|
|
|
+ case nvme_admin_sanitize_nvm:
|
|
|
+ return NVME_CMD_EFFECTS_CSE_MASK;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
|
|
+ u8 opcode)
|
|
|
+{
|
|
|
+ u32 effects = 0;
|
|
|
+
|
|
|
+ if (ns) {
|
|
|
+ if (ctrl->effects)
|
|
|
+ effects = le32_to_cpu(ctrl->effects->iocs[opcode]);
|
|
|
+ if (effects & ~NVME_CMD_EFFECTS_CSUPP)
|
|
|
+ dev_warn(ctrl->device,
|
|
|
+ "IO command:%02x has unhandled effects:%08x\n",
|
|
|
+ opcode, effects);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ctrl->effects)
|
|
|
+ effects = le32_to_cpu(ctrl->effects->iocs[opcode]);
|
|
|
+ else
|
|
|
+ effects = nvme_known_admin_effects(opcode);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For simplicity, IO to all namespaces is quiesced even if the command
|
|
|
+ * effects say only one namespace is affected.
|
|
|
+ */
|
|
|
+ if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK)) {
|
|
|
+ nvme_start_freeze(ctrl);
|
|
|
+ nvme_wait_freeze(ctrl);
|
|
|
+ }
|
|
|
+ return effects;
|
|
|
+}
|
|
|
+
|
|
|
+static void nvme_update_formats(struct nvme_ctrl *ctrl)
|
|
|
+{
|
|
|
+ struct nvme_ns *ns;
|
|
|
+
|
|
|
+ mutex_lock(&ctrl->namespaces_mutex);
|
|
|
+ list_for_each_entry(ns, &ctrl->namespaces, list) {
|
|
|
+ if (ns->disk && nvme_revalidate_disk(ns->disk))
|
|
|
+ nvme_ns_remove(ns);
|
|
|
+ }
|
|
|
+ mutex_unlock(&ctrl->namespaces_mutex);
|
|
|
+}
|
|
|
+
|
|
|
+static void nvme_passthru_end(struct nvme_ctrl *ctrl, u32 effects)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * Revalidate LBA changes prior to unfreezing. This is necessary to
|
|
|
+ * prevent memory corruption if a logical block size was changed by
|
|
|
+ * this command.
|
|
|
+ */
|
|
|
+ if (effects & NVME_CMD_EFFECTS_LBCC)
|
|
|
+ nvme_update_formats(ctrl);
|
|
|
+ if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK))
|
|
|
+ nvme_unfreeze(ctrl);
|
|
|
+ if (effects & NVME_CMD_EFFECTS_CCC)
|
|
|
+ nvme_init_identify(ctrl);
|
|
|
+ if (effects & (NVME_CMD_EFFECTS_NIC | NVME_CMD_EFFECTS_NCC))
|
|
|
+ nvme_queue_scan(ctrl);
|
|
|
+}
|
|
|
+
|
|
|
static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
|
|
struct nvme_passthru_cmd __user *ucmd)
|
|
|
{
|
|
|
struct nvme_passthru_cmd cmd;
|
|
|
struct nvme_command c;
|
|
|
unsigned timeout = 0;
|
|
|
+ u32 effects;
|
|
|
int status;
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
@@ -1023,10 +1101,13 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
|
|
if (cmd.timeout_ms)
|
|
|
timeout = msecs_to_jiffies(cmd.timeout_ms);
|
|
|
|
|
|
+ effects = nvme_passthru_start(ctrl, ns, cmd.opcode);
|
|
|
status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c,
|
|
|
(void __user *)(uintptr_t)cmd.addr, cmd.data_len,
|
|
|
(void __user *)(uintptr_t)cmd.metadata, cmd.metadata,
|
|
|
0, &cmd.result, timeout);
|
|
|
+ nvme_passthru_end(ctrl, effects);
|
|
|
+
|
|
|
if (status >= 0) {
|
|
|
if (put_user(cmd.result, &ucmd->result))
|
|
|
return -EFAULT;
|
|
@@ -1759,6 +1840,25 @@ static int nvme_get_log(struct nvme_ctrl *ctrl, u8 log_page, void *log,
|
|
|
return nvme_submit_sync_cmd(ctrl->admin_q, &c, log, size);
|
|
|
}
|
|
|
|
|
|
+static int nvme_get_effects_log(struct nvme_ctrl *ctrl)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!ctrl->effects)
|
|
|
+ ctrl->effects = kzalloc(sizeof(*ctrl->effects), GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!ctrl->effects)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ret = nvme_get_log(ctrl, NVME_LOG_CMD_EFFECTS, ctrl->effects,
|
|
|
+ sizeof(*ctrl->effects));
|
|
|
+ if (ret) {
|
|
|
+ kfree(ctrl->effects);
|
|
|
+ ctrl->effects = NULL;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Initialize the cached copies of the Identify data and various controller
|
|
|
* register in our nvme_ctrl structure. This should be called as soon as
|
|
@@ -1794,6 +1894,12 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
|
+ if (id->lpa & NVME_CTRL_LPA_CMD_EFFECTS_LOG) {
|
|
|
+ ret = nvme_get_effects_log(ctrl);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
nvme_init_subnqn(ctrl, id);
|
|
|
|
|
|
if (!ctrl->identified) {
|
|
@@ -2713,6 +2819,7 @@ static void nvme_free_ctrl(struct device *dev)
|
|
|
|
|
|
ida_simple_remove(&nvme_instance_ida, ctrl->instance);
|
|
|
ida_destroy(&ctrl->ns_ida);
|
|
|
+ kfree(ctrl->effects);
|
|
|
|
|
|
ctrl->ops->free_ctrl(ctrl);
|
|
|
}
|