|
@@ -76,6 +76,11 @@ static DEFINE_SPINLOCK(dev_list_lock);
|
|
|
|
|
|
static struct class *nvme_class;
|
|
|
|
|
|
+static __le32 nvme_get_log_dw10(u8 lid, size_t size)
|
|
|
+{
|
|
|
+ return cpu_to_le32((((size / 4) - 1) << 16) | lid);
|
|
|
+}
|
|
|
+
|
|
|
int nvme_reset_ctrl(struct nvme_ctrl *ctrl)
|
|
|
{
|
|
|
if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
|
|
@@ -2534,6 +2539,71 @@ static void nvme_async_event_work(struct work_struct *work)
|
|
|
spin_unlock_irq(&ctrl->lock);
|
|
|
}
|
|
|
|
|
|
+static bool nvme_ctrl_pp_status(struct nvme_ctrl *ctrl)
|
|
|
+{
|
|
|
+
|
|
|
+ u32 csts;
|
|
|
+
|
|
|
+ if (ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &csts))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (csts == ~0)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return ((ctrl->ctrl_config & NVME_CC_ENABLE) && (csts & NVME_CSTS_PP));
|
|
|
+}
|
|
|
+
|
|
|
+static void nvme_get_fw_slot_info(struct nvme_ctrl *ctrl)
|
|
|
+{
|
|
|
+ struct nvme_command c = { };
|
|
|
+ struct nvme_fw_slot_info_log *log;
|
|
|
+
|
|
|
+ log = kmalloc(sizeof(*log), GFP_KERNEL);
|
|
|
+ if (!log)
|
|
|
+ return;
|
|
|
+
|
|
|
+ c.common.opcode = nvme_admin_get_log_page;
|
|
|
+ c.common.nsid = cpu_to_le32(0xffffffff);
|
|
|
+ c.common.cdw10[0] = nvme_get_log_dw10(NVME_LOG_FW_SLOT, sizeof(*log));
|
|
|
+
|
|
|
+ if (!nvme_submit_sync_cmd(ctrl->admin_q, &c, log, sizeof(*log)))
|
|
|
+ dev_warn(ctrl->device,
|
|
|
+ "Get FW SLOT INFO log error\n");
|
|
|
+ kfree(log);
|
|
|
+}
|
|
|
+
|
|
|
+static void nvme_fw_act_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct nvme_ctrl *ctrl = container_of(work,
|
|
|
+ struct nvme_ctrl, fw_act_work);
|
|
|
+ unsigned long fw_act_timeout;
|
|
|
+
|
|
|
+ if (ctrl->mtfa)
|
|
|
+ fw_act_timeout = jiffies +
|
|
|
+ msecs_to_jiffies(ctrl->mtfa * 100);
|
|
|
+ else
|
|
|
+ fw_act_timeout = jiffies +
|
|
|
+ msecs_to_jiffies(admin_timeout * 1000);
|
|
|
+
|
|
|
+ nvme_stop_queues(ctrl);
|
|
|
+ while (nvme_ctrl_pp_status(ctrl)) {
|
|
|
+ if (time_after(jiffies, fw_act_timeout)) {
|
|
|
+ dev_warn(ctrl->device,
|
|
|
+ "Fw activation timeout, reset controller\n");
|
|
|
+ nvme_reset_ctrl(ctrl);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ msleep(100);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ctrl->state != NVME_CTRL_LIVE)
|
|
|
+ return;
|
|
|
+
|
|
|
+ nvme_start_queues(ctrl);
|
|
|
+ /* read FW slot informationi to clear the AER*/
|
|
|
+ nvme_get_fw_slot_info(ctrl);
|
|
|
+}
|
|
|
+
|
|
|
void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status,
|
|
|
union nvme_result *res)
|
|
|
{
|
|
@@ -2560,6 +2630,9 @@ void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status,
|
|
|
dev_info(ctrl->device, "rescanning\n");
|
|
|
nvme_queue_scan(ctrl);
|
|
|
break;
|
|
|
+ case NVME_AER_NOTICE_FW_ACT_STARTING:
|
|
|
+ schedule_work(&ctrl->fw_act_work);
|
|
|
+ break;
|
|
|
default:
|
|
|
dev_warn(ctrl->device, "async event result %08x\n", result);
|
|
|
}
|
|
@@ -2607,6 +2680,7 @@ void nvme_stop_ctrl(struct nvme_ctrl *ctrl)
|
|
|
nvme_stop_keep_alive(ctrl);
|
|
|
flush_work(&ctrl->async_event_work);
|
|
|
flush_work(&ctrl->scan_work);
|
|
|
+ cancel_work_sync(&ctrl->fw_act_work);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(nvme_stop_ctrl);
|
|
|
|
|
@@ -2670,6 +2744,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
|
|
|
ctrl->quirks = quirks;
|
|
|
INIT_WORK(&ctrl->scan_work, nvme_scan_work);
|
|
|
INIT_WORK(&ctrl->async_event_work, nvme_async_event_work);
|
|
|
+ INIT_WORK(&ctrl->fw_act_work, nvme_fw_act_work);
|
|
|
|
|
|
ret = nvme_set_instance(ctrl);
|
|
|
if (ret)
|