All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] nvme: APST support
@ 2017-01-19 19:55 Andy Lutomirski
  2017-01-19 19:55 ` [PATCH v2 1/2] nvme: Add a quirk mechanism that uses identify_ctrl Andy Lutomirski
  2017-01-19 19:55 ` [PATCH v2 2/2] nvme: Enable autonomous power state transitions Andy Lutomirski
  0 siblings, 2 replies; 8+ messages in thread
From: Andy Lutomirski @ 2017-01-19 19:55 UTC (permalink / raw)


As far as I can tell, APST works fine on every NVMe device I'm aware
of with the single exception of a particular Samsung device.  This
series enables APST by default but quirks it off on the offending
Samsung device.  Some Samsung engineers are taking a look, and,
depending on what they find, we may be able to change the quirk to
work around the bug rather than disabling APST outright.

I think it would be nice to queue this up and give it a soak in
linux-next.

Changes from v1:

 - Fix a totally wrong comment in the quirk code (me)
 - Add a comment about not redetecting quirks after reset (Keith)
 - Rearrange the series to avoid bisection problems (Jens)

Once fully applied, v1 and v2 only differ in their comments.

Changes from before:

 - Rebased to linux-block/for-next.
 - I added a quirk for the known-bad Samsung device.
 - It's fully integrated with dev_pm_qos.
 - I now program APST after all the queues are set up, which seems safer.
   (This didn't fix the Samsung problem, though.)

Andy Lutomirski (2):
  nvme: Add a quirk mechanism that uses identify_ctrl
  nvme: Enable autonomous power state transitions

 drivers/nvme/host/core.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/nvme/host/nvme.h |  13 +++
 drivers/nvme/host/pci.c  |   2 +
 drivers/nvme/host/rdma.c |   2 +
 include/linux/nvme.h     |   6 ++
 5 files changed, 237 insertions(+)

-- 
2.9.3

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

* [PATCH v2 1/2] nvme: Add a quirk mechanism that uses identify_ctrl
  2017-01-19 19:55 [PATCH v2 0/2] nvme: APST support Andy Lutomirski
@ 2017-01-19 19:55 ` Andy Lutomirski
  2017-01-20 10:24   ` Christoph Hellwig
  2017-01-19 19:55 ` [PATCH v2 2/2] nvme: Enable autonomous power state transitions Andy Lutomirski
  1 sibling, 1 reply; 8+ messages in thread
From: Andy Lutomirski @ 2017-01-19 19:55 UTC (permalink / raw)


Currently, all NVMe quirks are based on PCI IDs.  Add a mechanism to
define quirks based on identify_ctrl's vendor id, model number,
and/or firmware revision.

Signed-off-by: Andy Lutomirski <luto at kernel.org>
---
 drivers/nvme/host/core.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/nvme/host/nvme.h |  1 +
 2 files changed, 65 insertions(+)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 8a3c3e32a704..d393d64955cb 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1196,6 +1196,50 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl,
 	blk_queue_write_cache(q, vwc, vwc);
 }
 
+struct nvme_core_quirk_entry {
+	/*
+	 * NVMe model and firmware strings are padded with spaces.  For
+	 * simplicity, strings in the quirk table are padded with NULLs
+	 * instead.
+	 */
+	u16 vid;
+	const char *mn;
+	const char *fr;
+	unsigned long quirks;
+};
+
+static const struct nvme_core_quirk_entry core_quirks[] = {
+};
+
+/* match is null-terminated but idstr is space-padded. */
+static bool string_matches(const char *idstr, const char *match, size_t len)
+{
+	size_t matchlen;
+
+	if (!match)
+		return true;
+
+	matchlen = strlen(match);
+	WARN_ON_ONCE(matchlen > len);
+
+	if (memcmp(idstr, match, matchlen))
+		return false;
+
+	for (; matchlen < len; matchlen++)
+		if (idstr[matchlen] != ' ')
+			return false;
+
+	return true;
+}
+
+static bool quirk_matches(const struct nvme_id_ctrl *id,
+			  const struct nvme_core_quirk_entry *q)
+{
+	return q->vid == le16_to_cpu(id->vid) &&
+		string_matches(id->mn, q->mn, sizeof(id->mn)) &&
+		string_matches(id->fr, q->fr, sizeof(id->fr));
+}
+
 /*
  * Initialize the cached copies of the Identify data and various controller
  * register in our nvme_ctrl structure.  This should be called as soon as
@@ -1230,6 +1274,24 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
 		return -EIO;
 	}
 
+	if (!ctrl->identified) {
+		/*
+		 * Check for quirks.  Quirk can depend on firmware version,
+		 * so, in principle, the set of quirks present can change
+		 * across a reset.  As a possible future enhancement, we
+		 * could re-scan for quirks every time we reinitialize
+		 * the device, but we'd have to make sure that the driver
+		 * behaves intelligently if the quirks change.
+		 */
+
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(core_quirks); i++) {
+			if (quirk_matches(id, &core_quirks[i]))
+				ctrl->quirks |= core_quirks[i].quirks;
+		}
+	}
+
 	ctrl->vid = le16_to_cpu(id->vid);
 	ctrl->oncs = le16_to_cpup(&id->oncs);
 	atomic_set(&ctrl->abort_limit, id->acl + 1);
@@ -1272,6 +1334,8 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
 	}
 
 	kfree(id);
+
+	ctrl->identified = true;
 	return ret;
 }
 EXPORT_SYMBOL_GPL(nvme_init_identify);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index aead6d08ed2c..9dccf7127cce 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -111,6 +111,7 @@ enum nvme_ctrl_state {
 
 struct nvme_ctrl {
 	enum nvme_ctrl_state state;
+	bool identified;
 	spinlock_t lock;
 	const struct nvme_ctrl_ops *ops;
 	struct request_queue *admin_q;
-- 
2.9.3

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

* [PATCH v2 2/2] nvme: Enable autonomous power state transitions
  2017-01-19 19:55 [PATCH v2 0/2] nvme: APST support Andy Lutomirski
  2017-01-19 19:55 ` [PATCH v2 1/2] nvme: Add a quirk mechanism that uses identify_ctrl Andy Lutomirski
@ 2017-01-19 19:55 ` Andy Lutomirski
  2017-01-19 20:15   ` Keith Busch
  2017-01-20 10:30   ` Christoph Hellwig
  1 sibling, 2 replies; 8+ messages in thread
From: Andy Lutomirski @ 2017-01-19 19:55 UTC (permalink / raw)


NVMe devices can advertise multiple power states.  These states can
be either "operational" (the device is fully functional but possibly
slow) or "non-operational" (the device is asleep until woken up).
Some devices can automatically enter a non-operational state when
idle for a specified amount of time and then automatically wake back
up when needed.

The hardware configuration is a table.  For each state, an entry in
the table indicates the next deeper non-operational state, if any,
to autonomously transition to and the idle time required before
transitioning.

This patch teaches the driver to program APST so that each successive
non-operational state will be entered after an idle time equal to 100%
of the total latency (entry plus exit) associated with that state.
The maximum acceptable latency is controlled using dev_pm_qos
(e.g. power/pm_qos_latency_tolerance_us in sysfs); non-operational
states with total latency greater than this value will not be used.
As a special case, setting the latency tolerance to 0 will disable
APST entirely.  On hardware without APST support, the sysfs file will
not be exposed.

The latency tolerance for newly-probed devices is set by the module
parameter nvme_core.default_ps_max_latency_us.

In theory, the device can expose "default" APST table, but this
doesn't seem to function correctly on my device (Samsung 950), nor
does it seem particularly useful.  There is also an optional
mechanism by which a configuration can be "saved" so it will be
automatically loaded on reset.  This can be configured from
userspace, but it doesn't seem useful to support in the driver.

On my laptop, enabling APST seems to save nearly 1W.

The hardware tables can be decoded in userspace with nvme-cli.
'nvme id-ctrl /dev/nvmeN' will show the power state table and
'nvme get-feature -f 0x0c -H /dev/nvme0' will show the current APST
configuration.

This feature is quirked off on a known-buggy Samsung device.

Signed-off-by: Andy Lutomirski <luto at kernel.org>
---
 drivers/nvme/host/core.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/nvme/host/nvme.h |  12 ++++
 drivers/nvme/host/pci.c  |   2 +
 drivers/nvme/host/rdma.c |   2 +
 include/linux/nvme.h     |   6 ++
 5 files changed, 172 insertions(+)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index d393d64955cb..b3f9f97dcafd 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -26,6 +26,7 @@
 #include <linux/ptrace.h>
 #include <linux/nvme_ioctl.h>
 #include <linux/t10-pi.h>
+#include <linux/pm_qos.h>
 #include <scsi/sg.h>
 #include <asm/unaligned.h>
 
@@ -56,6 +57,11 @@ EXPORT_SYMBOL_GPL(nvme_max_retries);
 static int nvme_char_major;
 module_param(nvme_char_major, int, 0);
 
+static unsigned long default_ps_max_latency_us = 25000;
+module_param(default_ps_max_latency_us, ulong, 0644);
+MODULE_PARM_DESC(default_ps_max_latency_us,
+		 "max power saving latency for new devices; use PM QOS to change per device");
+
 static LIST_HEAD(nvme_ctrl_list);
 static DEFINE_SPINLOCK(dev_list_lock);
 
@@ -1196,6 +1202,120 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl,
 	blk_queue_write_cache(q, vwc, vwc);
 }
 
+void nvme_configure_apst(struct nvme_ctrl *ctrl)
+{
+	/*
+	 * APST (Autonomous Power State Transition) lets us program a
+	 * table of power state transitions that the controller will
+	 * perform automatically.  We configure it with a simple
+	 * heuristic: we are willing to spend at most 2% of the time
+	 * transitioning between power states.  Therefore, when running
+	 * in any given state, we will enter the next lower-power
+	 * non-operational state after waiting 100 * (enlat + exlat)
+	 * microseconds, as long as that state's total latency is under
+	 * the requested maximum latency.
+	 *
+	 * We will not autonomously enter any non-operational state for
+	 * which the total latency exceeds ps_max_latency_us.  Users
+	 * can set ps_max_latency_us to zero to turn off APST.
+	 */
+
+	unsigned apste;
+	struct nvme_feat_auto_pst *table;
+	int ret;
+
+	/*
+	 * If APST isn't supported or if we haven't been initialized yet,
+	 * then don't do anything.
+	 */
+	if (!ctrl->apsta)
+		return;
+
+	if (ctrl->npss > 31) {
+		dev_warn(ctrl->device, "NPSS is invalid; not using APST\n");
+		return;
+	}
+
+	table = kzalloc(sizeof(*table), GFP_KERNEL);
+	if (!table)
+		return;
+
+	if (ctrl->ps_max_latency_us == 0) {
+		/* Turn off APST. */
+		apste = 0;
+	} else {
+		__le64 target = cpu_to_le64(0);
+		int state;
+
+		/*
+		 * Walk through all states from lowest- to highest-power.
+		 * According to the spec, lower-numbered states use more
+		 * power.  NPSS, despite the name, is the index of the
+		 * lowest-power state, not the number of states.
+		 */
+		for (state = (int)ctrl->npss; state >= 0; state--) {
+			u64 total_latency_us, transition_ms;
+
+			if (target)
+				table->entries[state] = target;
+
+			/*
+			 * Is this state a useful non-operational state for
+			 * higher-power states to autonomously transition to?
+			 */
+			if (!(ctrl->psd[state].flags &
+			      NVME_PS_FLAGS_NON_OP_STATE))
+				continue;
+
+			total_latency_us =
+				(u64)le32_to_cpu(ctrl->psd[state].entry_lat) +
+				+ le32_to_cpu(ctrl->psd[state].exit_lat);
+			if (total_latency_us > ctrl->ps_max_latency_us)
+				continue;
+
+			/*
+			 * This state is good.  Use it as the APST idle
+			 * target for higher power states.
+			 */
+			transition_ms = total_latency_us + 19;
+			do_div(transition_ms, 20);
+			if (transition_ms > (1 << 24) - 1)
+				transition_ms = (1 << 24) - 1;
+
+			target = cpu_to_le64((state << 3) |
+					     (transition_ms << 8));
+		}
+
+		apste = 1;
+	}
+
+	ret = nvme_set_features(ctrl, NVME_FEAT_AUTO_PST, apste,
+				table, sizeof(*table), NULL);
+	if (ret)
+		dev_err(ctrl->device, "failed to set APST feature (%d)\n", ret);
+
+	kfree(table);
+}
+EXPORT_SYMBOL_GPL(nvme_configure_apst);
+
+static void nvme_set_latency_tolerance(struct device *dev, s32 val)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+	u64 latency;
+
+	if (val == PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT ||
+	    val == PM_QOS_LATENCY_ANY)
+		latency = U64_MAX;
+	else
+		latency = val;
+
+	if (ctrl->ps_max_latency_us != val) {
+		ctrl->ps_max_latency_us = val;
+		nvme_configure_apst(ctrl);
+	}
+}
+
 struct nvme_core_quirk_entry {
 	/*
 	 * NVMe model and firmware strings are padded with spaces.  For
@@ -1209,6 +1329,16 @@ struct nvme_core_quirk_entry {
 };
 
 static const struct nvme_core_quirk_entry core_quirks[] = {
+	/*
+	 * Seen on a Samsung "SM951 NVMe SAMSUNG 256GB": using APST causes
+	 * the controller to go out to lunch.  It dies when the watchdog
+	 * timer reads CSTS and gets 0xffffffff.
+	 */
+	{
+		.vid = 0x144d,
+		.fr = "BXW75D0Q",
+		.quirks = NVME_QUIRK_NO_APST,
+	},
 };
 
 /* match is null-terminated but idstr is space-padded. */
@@ -1251,6 +1381,7 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
 	u64 cap;
 	int ret, page_shift;
 	u32 max_hw_sectors;
+	u8 prev_apsta;
 
 	ret = ctrl->ops->reg_read32(ctrl, NVME_REG_VS, &ctrl->vs);
 	if (ret) {
@@ -1311,6 +1442,11 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
 	ctrl->sgls = le32_to_cpu(id->sgls);
 	ctrl->kas = le16_to_cpu(id->kas);
 
+	ctrl->npss = id->npss;
+	prev_apsta = ctrl->apsta;
+	ctrl->apsta = (ctrl->quirks & NVME_QUIRK_NO_APST) ? 0 : id->apsta;
+	memcpy(ctrl->psd, id->psd, sizeof(ctrl->psd));
+
 	if (ctrl->ops->is_fabrics) {
 		ctrl->icdoff = le16_to_cpu(id->icdoff);
 		ctrl->ioccsz = le32_to_cpu(id->ioccsz);
@@ -1335,7 +1471,13 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
 
 	kfree(id);
 
+	if (ctrl->apsta && !prev_apsta)
+		dev_pm_qos_expose_latency_tolerance(ctrl->device);
+	else if (!ctrl->apsta && prev_apsta)
+		dev_pm_qos_hide_latency_tolerance(ctrl->device);
+
 	ctrl->identified = true;
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(nvme_init_identify);
@@ -2073,6 +2215,14 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
 	list_add_tail(&ctrl->node, &nvme_ctrl_list);
 	spin_unlock(&dev_list_lock);
 
+	/*
+	 * Initialize latency tolerance controls.  The sysfs files won't
+	 * be visible to userspace unless the device actually supports APST.
+	 */
+	ctrl->device->power.set_latency_tolerance = nvme_set_latency_tolerance;
+	dev_pm_qos_update_user_latency_tolerance(ctrl->device,
+		min(default_ps_max_latency_us, (unsigned long)S32_MAX));
+
 	return 0;
 out_release_instance:
 	nvme_release_instance(ctrl);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 9dccf7127cce..629129133be0 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -77,6 +77,11 @@ enum nvme_quirks {
 	 * readiness, which is done by reading the NVME_CSTS_RDY bit.
 	 */
 	NVME_QUIRK_DELAY_BEFORE_CHK_RDY		= (1 << 3),
+
+	/*
+	 * APST should not be used.
+	 */
+	NVME_QUIRK_NO_APST			= (1 << 4),
 };
 
 /*
@@ -144,13 +149,19 @@ struct nvme_ctrl {
 	u32 vs;
 	u32 sgls;
 	u16 kas;
+	u8 npss;
+	u8 apsta;
 	unsigned int kato;
 	bool subsystem;
 	unsigned long quirks;
+	struct nvme_id_power_state psd[32];
 	struct work_struct scan_work;
 	struct work_struct async_event_work;
 	struct delayed_work ka_work;
 
+	/* Power saving configuration */
+	u64 ps_max_latency_us;
+
 	/* Fabrics only */
 	u16 sqsize;
 	u32 ioccsz;
@@ -264,6 +275,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
 void nvme_uninit_ctrl(struct nvme_ctrl *ctrl);
 void nvme_put_ctrl(struct nvme_ctrl *ctrl);
 int nvme_init_identify(struct nvme_ctrl *ctrl);
+void nvme_configure_apst(struct nvme_ctrl *ctrl);
 
 void nvme_queue_scan(struct nvme_ctrl *ctrl);
 void nvme_remove_namespaces(struct nvme_ctrl *ctrl);
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 7103bce4ba4f..1c3e170da6de 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -1791,6 +1791,8 @@ static void nvme_reset_work(struct work_struct *work)
 	if (result)
 		goto out;
 
+	nvme_configure_apst(&dev->ctrl);
+
 	/*
 	 * A controller that can not execute IO typically requires user
 	 * intervention to correct. For such degraded controllers, the driver
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 557f29b1f1bb..a64b5db96fd8 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -1629,6 +1629,8 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl)
 
 	nvme_start_keep_alive(&ctrl->ctrl);
 
+	nvme_configure_apst(&ctrl->ctrl);
+
 	return 0;
 
 out_cleanup_queue:
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 3d1c6f1b15c9..f929e8cf0a16 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -576,6 +576,12 @@ struct nvme_write_zeroes_cmd {
 	__le16			appmask;
 };
 
+/* Features */
+
+struct nvme_feat_auto_pst {
+	__le64 entries[32];
+};
+
 /* Admin commands */
 
 enum nvme_admin_opcode {
-- 
2.9.3

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

* [PATCH v2 2/2] nvme: Enable autonomous power state transitions
  2017-01-19 19:55 ` [PATCH v2 2/2] nvme: Enable autonomous power state transitions Andy Lutomirski
@ 2017-01-19 20:15   ` Keith Busch
  2017-01-20  5:17     ` Andy Lutomirski
  2017-01-20 10:30   ` Christoph Hellwig
  1 sibling, 1 reply; 8+ messages in thread
From: Keith Busch @ 2017-01-19 20:15 UTC (permalink / raw)


On Thu, Jan 19, 2017@11:55:44AM -0800, Andy Lutomirski wrote:
> +static void nvme_set_latency_tolerance(struct device *dev, s32 val)
> +{
> +	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
> +
> +	u64 latency;
> +
> +	if (val == PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT ||
> +	    val == PM_QOS_LATENCY_ANY)
> +		latency = U64_MAX;
> +	else
> +		latency = val;
> +
> +	if (ctrl->ps_max_latency_us != val) {
> +		ctrl->ps_max_latency_us = val;

Did you mean to use 'latency' here instead of 'val'? Otherwise, I don't
see why the latency variable exists.

> +		nvme_configure_apst(ctrl);
> +	}
> +}

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

* [PATCH v2 2/2] nvme: Enable autonomous power state transitions
  2017-01-19 20:15   ` Keith Busch
@ 2017-01-20  5:17     ` Andy Lutomirski
  0 siblings, 0 replies; 8+ messages in thread
From: Andy Lutomirski @ 2017-01-20  5:17 UTC (permalink / raw)


On Thu, Jan 19, 2017@12:15 PM, Keith Busch <keith.busch@intel.com> wrote:
> On Thu, Jan 19, 2017@11:55:44AM -0800, Andy Lutomirski wrote:
>> +static void nvme_set_latency_tolerance(struct device *dev, s32 val)
>> +{
>> +     struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
>> +
>> +     u64 latency;
>> +
>> +     if (val == PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT ||
>> +         val == PM_QOS_LATENCY_ANY)
>> +             latency = U64_MAX;
>> +     else
>> +             latency = val;
>> +
>> +     if (ctrl->ps_max_latency_us != val) {
>> +             ctrl->ps_max_latency_us = val;
>
> Did you mean to use 'latency' here instead of 'val'? Otherwise, I don't
> see why the latency variable exists.

D'oh!  I'm a bit surprised the compiler didn't warn.

As a practical matter, this probably had no effect in my testing
because the two magic values would have ended up being rather large
s32 values, so the effect would be the same.  v3 coming, though.

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

* [PATCH v2 1/2] nvme: Add a quirk mechanism that uses identify_ctrl
  2017-01-19 19:55 ` [PATCH v2 1/2] nvme: Add a quirk mechanism that uses identify_ctrl Andy Lutomirski
@ 2017-01-20 10:24   ` Christoph Hellwig
  0 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2017-01-20 10:24 UTC (permalink / raw)


Looks fine,

Reviewed-by: Christoph Hellwig <hch at lst.de>

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

* [PATCH v2 2/2] nvme: Enable autonomous power state transitions
  2017-01-19 19:55 ` [PATCH v2 2/2] nvme: Enable autonomous power state transitions Andy Lutomirski
  2017-01-19 20:15   ` Keith Busch
@ 2017-01-20 10:30   ` Christoph Hellwig
  2017-01-20 18:07     ` Andy Lutomirski
  1 sibling, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2017-01-20 10:30 UTC (permalink / raw)



> +static void nvme_set_latency_tolerance(struct device *dev, s32 val)
> +{
> +	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
> +
> +	u64 latency;
> +
> +	if (val == PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT ||
> +	    val == PM_QOS_LATENCY_ANY)
> +		latency = U64_MAX;
> +	else
> +		latency = val;

In addition to the latency vs val mixup pointed out earlier -
can you use a switch statement here to make the code a little
more obvious?

> +	if (ctrl->ps_max_latency_us != val) {
> +		ctrl->ps_max_latency_us = val;
> +		nvme_configure_apst(ctrl);
> +	}
> +}
> +

>  	ctrl->identified = true;

The ->identified field seems to never be checked anywhere.


> diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
> index 7103bce4ba4f..1c3e170da6de 100644
> --- a/drivers/nvme/host/pci.c
> +++ b/drivers/nvme/host/pci.c
> @@ -1791,6 +1791,8 @@ static void nvme_reset_work(struct work_struct *work)
>  	if (result)
>  		goto out;
>  
> +	nvme_configure_apst(&dev->ctrl);
> +
>  	/*
>  	 * A controller that can not execute IO typically requires user
>  	 * intervention to correct. For such degraded controllers, the driver
> diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
> index 557f29b1f1bb..a64b5db96fd8 100644
> --- a/drivers/nvme/host/rdma.c
> +++ b/drivers/nvme/host/rdma.c
> @@ -1629,6 +1629,8 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl)
>  
>  	nvme_start_keep_alive(&ctrl->ctrl);
>  
> +	nvme_configure_apst(&ctrl->ctrl);
> +

Is there a specific reason for the exact placement of these calls here
and not at inside nvme_init_identify?  Having all the code called
from an existing core function would make things a lot easier in the
future.  And if that's not possible we'll probably need comments
on why it's placed like it is.

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

* [PATCH v2 2/2] nvme: Enable autonomous power state transitions
  2017-01-20 10:30   ` Christoph Hellwig
@ 2017-01-20 18:07     ` Andy Lutomirski
  0 siblings, 0 replies; 8+ messages in thread
From: Andy Lutomirski @ 2017-01-20 18:07 UTC (permalink / raw)


On Fri, Jan 20, 2017@2:30 AM, Christoph Hellwig <hch@lst.de> wrote:
>
>> +static void nvme_set_latency_tolerance(struct device *dev, s32 val)
>> +{
>> +     struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
>> +
>> +     u64 latency;
>> +
>> +     if (val == PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT ||
>> +         val == PM_QOS_LATENCY_ANY)
>> +             latency = U64_MAX;
>> +     else
>> +             latency = val;
>
> In addition to the latency vs val mixup pointed out earlier -
> can you use a switch statement here to make the code a little
> more obvious?

Sure.

>
>> +     if (ctrl->ps_max_latency_us != val) {
>> +             ctrl->ps_max_latency_us = val;
>> +             nvme_configure_apst(ctrl);
>> +     }
>> +}
>> +
>
>>       ctrl->identified = true;
>
> The ->identified field seems to never be checked anywhere.
>

See the previous patch:

+       if (!ctrl->identified) {


>
>> diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
>> index 7103bce4ba4f..1c3e170da6de 100644
>> --- a/drivers/nvme/host/pci.c
>> +++ b/drivers/nvme/host/pci.c
>> @@ -1791,6 +1791,8 @@ static void nvme_reset_work(struct work_struct *work)
>>       if (result)
>>               goto out;
>>
>> +     nvme_configure_apst(&dev->ctrl);
>> +
>>       /*
>>        * A controller that can not execute IO typically requires user
>>        * intervention to correct. For such degraded controllers, the driver
>> diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
>> index 557f29b1f1bb..a64b5db96fd8 100644
>> --- a/drivers/nvme/host/rdma.c
>> +++ b/drivers/nvme/host/rdma.c
>> @@ -1629,6 +1629,8 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl)
>>
>>       nvme_start_keep_alive(&ctrl->ctrl);
>>
>> +     nvme_configure_apst(&ctrl->ctrl);
>> +
>
> Is there a specific reason for the exact placement of these calls here
> and not at inside nvme_init_identify?  Having all the code called
> from an existing core function would make things a lot easier in the
> future.  And if that's not possible we'll probably need comments
> on why it's placed like it is.

In the original version I had this in nvme_init_identify().  I moved
it later just in case there was a buggy device that didn't like having
APST flipped on before the queue setup was done.  I could easily move
it back to nvme_init_identify() or add a new nvme_finish_setup() or
similar.  Any preference?

--Andy

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

end of thread, other threads:[~2017-01-20 18:07 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-19 19:55 [PATCH v2 0/2] nvme: APST support Andy Lutomirski
2017-01-19 19:55 ` [PATCH v2 1/2] nvme: Add a quirk mechanism that uses identify_ctrl Andy Lutomirski
2017-01-20 10:24   ` Christoph Hellwig
2017-01-19 19:55 ` [PATCH v2 2/2] nvme: Enable autonomous power state transitions Andy Lutomirski
2017-01-19 20:15   ` Keith Busch
2017-01-20  5:17     ` Andy Lutomirski
2017-01-20 10:30   ` Christoph Hellwig
2017-01-20 18:07     ` Andy Lutomirski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.