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);
                        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))
        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;
        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
                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) {
 
        ida_simple_remove(&nvme_instance_ida, ctrl->instance);
        ida_destroy(&ctrl->ns_ida);
+       kfree(ctrl->effects);
 
        ctrl->ops->free_ctrl(ctrl);
 }
 
        NVME_CTRL_OACS_SEC_SUPP                 = 1 << 0,
        NVME_CTRL_OACS_DIRECTIVES               = 1 << 5,
        NVME_CTRL_OACS_DBBUF_SUPP               = 1 << 8,
+       NVME_CTRL_LPA_CMD_EFFECTS_LOG           = 1 << 1,
 };
 
 struct nvme_lbaf {
        __u8                    rsvd64[448];
 };
 
+enum {
+       NVME_CMD_EFFECTS_CSUPP          = 1 << 0,
+       NVME_CMD_EFFECTS_LBCC           = 1 << 1,
+       NVME_CMD_EFFECTS_NCC            = 1 << 2,
+       NVME_CMD_EFFECTS_NIC            = 1 << 3,
+       NVME_CMD_EFFECTS_CCC            = 1 << 4,
+       NVME_CMD_EFFECTS_CSE_MASK       = 3 << 16,
+};
+
+struct nvme_effects_log {
+       __le32 acs[256];
+       __le32 iocs[256];
+       __u8   resv[2048];
+};
+
 enum {
        NVME_SMART_CRIT_SPARE           = 1 << 0,
        NVME_SMART_CRIT_TEMPERATURE     = 1 << 1,
        nvme_admin_format_nvm           = 0x80,
        nvme_admin_security_send        = 0x81,
        nvme_admin_security_recv        = 0x82,
+       nvme_admin_sanitize_nvm         = 0x84,
 };
 
 enum {
        NVME_LOG_ERROR          = 0x01,
        NVME_LOG_SMART          = 0x02,
        NVME_LOG_FW_SLOT        = 0x03,
+       NVME_LOG_CMD_EFFECTS    = 0x05,
        NVME_LOG_DISC           = 0x70,
        NVME_LOG_RESERVATION    = 0x80,
        NVME_FWACT_REPL         = (0 << 3),