Linux-NVME Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] nvme: Add support for FW activation without reset
       [not found] <CGME20170505114235epcas1p37200fbb3f181c1c840ab3c46b95691f1@epcas1p3.samsung.com>
@ 2017-05-05 11:41 ` Arnav dawn
  2017-05-05 15:27   ` Keith Busch
  0 siblings, 1 reply; 2+ messages in thread
From: Arnav dawn @ 2017-05-05 11:41 UTC (permalink / raw)


This patch adds support for AER handling to activate firmware
without controller reset.

Signed-off-by: Arnav Kumar Dawn <a.dawn at samsung.com>
---
 drivers/nvme/host/core.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++-
 drivers/nvme/host/nvme.h |  3 +++
 include/linux/nvme.h     |  3 +++
 3 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index d5e0906..db7969d 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1590,7 +1590,7 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
 	nvme_set_queue_limits(ctrl, ctrl->admin_q);
 	ctrl->sgls = le32_to_cpu(id->sgls);
 	ctrl->kas = le16_to_cpu(id->kas);
-
+	ctrl->mtfa = le16_to_cpu(id->mtfa);
 	ctrl->npss = id->npss;
 	prev_apsta = ctrl->apsta;
 	if (ctrl->quirks & NVME_QUIRK_NO_APST) {
@@ -2260,6 +2260,36 @@ static void nvme_async_event_work(struct work_struct *work)
 	spin_unlock_irq(&ctrl->lock);
 }
 
+static void nvme_fw_act_work(struct work_struct *work)
+{
+	struct nvme_ctrl *ctrl =
+		container_of(work, struct nvme_ctrl, fw_act_work);
+	u32 csts, ret;
+
+	while (1) {
+		ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &csts);
+		if (ret)
+			return;
+
+		ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CC,
+					&ctrl->ctrl_config);
+		if (ret)
+			return;
+
+		if ((ctrl->ctrl_config & NVME_CC_ENABLE)
+				&& !(csts & NVME_CSTS_PP)) {
+			ctrl->lfu_timeout = 0;
+			return;
+		}
+		if (time_after(jiffies, ctrl->lfu_timeout)) {
+			dev_warn(ctrl->device,
+				"Fw activation timeout, reset controller\n");
+			return;
+		}
+		msleep(100);
+	}
+}
+
 void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status,
 		union nvme_result *res)
 {
@@ -2286,6 +2316,33 @@ 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:
+	{
+		u32 csts, ret;
+
+		ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &csts);
+		if (ret)
+			return;
+
+		ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CC,
+					&ctrl->ctrl_config);
+		if (ret)
+			return;
+
+		if ((ctrl->ctrl_config & NVME_CC_ENABLE)
+				&& (csts & NVME_CSTS_PP)) {
+			ctrl->lfu_timeout =
+				jiffies + msecs_to_jiffies(ctrl->mtfa * 100);
+			schedule_work(&ctrl->fw_act_work);
+
+		}
+		break;
+	}
+	case NVME_AER_ERR_FW_IMG_LOAD:
+		dev_warn(ctrl->device, "FW image load error\n");
+		cancel_work_sync(&ctrl->fw_act_work);
+		ctrl->lfu_timeout = 0;
+		break;
 	default:
 		dev_warn(ctrl->device, "async event result %08x\n", result);
 	}
@@ -2332,6 +2389,7 @@ void nvme_uninit_ctrl(struct nvme_ctrl *ctrl)
 {
 	flush_work(&ctrl->async_event_work);
 	flush_work(&ctrl->scan_work);
+	flush_work(&ctrl->fw_act_work);
 	nvme_remove_namespaces(ctrl);
 
 	device_destroy(nvme_class, MKDEV(nvme_char_major, ctrl->instance));
@@ -2379,6 +2437,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)
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 29c708c..abb7120 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -141,6 +141,8 @@ struct nvme_ctrl {
 	u16 cntlid;
 
 	u32 ctrl_config;
+	u16 mtfa;
+	unsigned long lfu_timeout;
 
 	u32 page_size;
 	u32 max_hw_sectors;
@@ -162,6 +164,7 @@ struct nvme_ctrl {
 	struct work_struct scan_work;
 	struct work_struct async_event_work;
 	struct delayed_work ka_work;
+	struct work_struct fw_act_work;
 
 	/* Power saving configuration */
 	u64 ps_max_latency_us;
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index b625bac..1066b64 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -144,6 +144,7 @@ enum {
 	NVME_CSTS_RDY		= 1 << 0,
 	NVME_CSTS_CFS		= 1 << 1,
 	NVME_CSTS_NSSRO		= 1 << 4,
+	NVME_CSTS_PP		= 1 << 5,
 	NVME_CSTS_SHST_NORMAL	= 0 << 2,
 	NVME_CSTS_SHST_OCCUR	= 1 << 2,
 	NVME_CSTS_SHST_CMPLT	= 2 << 2,
@@ -347,6 +348,8 @@ enum {
 
 enum {
 	NVME_AER_NOTICE_NS_CHANGED	= 0x0002,
+	NVME_AER_NOTICE_FW_ACT_STARTING = 0x0102,
+	NVME_AER_ERR_FW_IMG_LOAD	= 0x0500,
 };
 
 struct nvme_lba_range_type {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH] nvme: Add support for FW activation without reset
  2017-05-05 11:41 ` [PATCH] nvme: Add support for FW activation without reset Arnav dawn
@ 2017-05-05 15:27   ` Keith Busch
  0 siblings, 0 replies; 2+ messages in thread
From: Keith Busch @ 2017-05-05 15:27 UTC (permalink / raw)


On Fri, May 05, 2017@05:11:55PM +0530, Arnav dawn wrote:
> This patch adds support for AER handling to activate firmware
> without controller reset.

I agree with the feature in principle, but have some comments on the
implementation.

> +static void nvme_fw_act_work(struct work_struct *work)
> +{
> +	struct nvme_ctrl *ctrl =
> +		container_of(work, struct nvme_ctrl, fw_act_work);
> +	u32 csts, ret;

IO not being possible in this state, shouldn't we call nvme_stop_queues
and restart after CSTS.PP clears so we don't risk IO timeout while
activation is occuring?

> +	while (1) {
> +		ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &csts);
> +		if (ret)
> +			return;
> +
> +		ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CC,
> +					&ctrl->ctrl_config);
> +		if (ret)
> +			return;
> +
> +		if ((ctrl->ctrl_config & NVME_CC_ENABLE)
> +				&& !(csts & NVME_CSTS_PP)) {

In case the the drive is removed during the event, append to the 'if':

	|| (csts == ~0)


> +			ctrl->lfu_timeout = 0;
> +			return;
> +		}
> +		if (time_after(jiffies, ctrl->lfu_timeout)) {
> +			dev_warn(ctrl->device,
> +				"Fw activation timeout, reset controller\n");

The log warns that it will reset the controller but that's not
happening.

> +			return;
> +		}
> +		msleep(100);
> +	}
> +}

At the end of this function, we should get the firmware information log
page to re-arm the event. Or are you counting on user space to do that?

> +	case NVME_AER_ERR_FW_IMG_LOAD:
> +		dev_warn(ctrl->device, "FW image load error\n");
> +		cancel_work_sync(&ctrl->fw_act_work);

This is executing in irq context, and you can't sync with work in that
state.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2017-05-05 15:27 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <CGME20170505114235epcas1p37200fbb3f181c1c840ab3c46b95691f1@epcas1p3.samsung.com>
2017-05-05 11:41 ` [PATCH] nvme: Add support for FW activation without reset Arnav dawn
2017-05-05 15:27   ` Keith Busch

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox