From: "William A. Kennington III" <william@wkennington.com>
To: Tudor Ambarus <tudor.ambarus@linaro.org>,
Pratyush Yadav <pratyush@kernel.org>,
Michael Walle <mwalle@kernel.org>
Cc: linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org,
"William A. Kennington III" <william@wkennington.com>
Subject: [PATCH RFC] mtd: spi-nor: Sleep between ready checks
Date: Thu, 22 May 2025 17:14:11 -0700 [thread overview]
Message-ID: <20250523001412.878560-1-william@wkennington.com> (raw)
We have SPI NOR devices that respond very slowly (200ms+) to some
commands. This causes our CPU to stall while repeatedly polling the NOR
with no cooldown in between attempts. In order to reduce overhead, this
patch introduces an exponential backoff in SPI readiness polling, that
self tunes the polling interval to match the speed of most transactions.
Signed-off-by: William A. Kennington III <william@wkennington.com>
---
drivers/mtd/spi-nor/core.c | 56 +++++++++++++++++++++++++++----------
include/linux/mtd/spi-nor.h | 2 ++
2 files changed, 44 insertions(+), 14 deletions(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index ac4b960101cc..75d3a4a1c1e3 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -714,27 +714,47 @@ static int spi_nor_ready(struct spi_nor *nor)
static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,
unsigned long timeout_jiffies)
{
- unsigned long deadline;
- int timeout = 0, ret;
-
- deadline = jiffies + timeout_jiffies;
-
- while (!timeout) {
- if (time_after_eq(jiffies, deadline))
- timeout = 1;
+ unsigned long deadline = jiffies + timeout_jiffies;
+ unsigned long sleep = nor->ready_sleep;
+ int ret, sleeps = 0;
+ while (true) {
ret = spi_nor_ready(nor);
if (ret < 0)
return ret;
- if (ret)
+ if (ret) {
+ /*
+ * We want to decrease the polling interval in cases
+ * where multiple requests finish in a single iteration
+ * or less. We don't want to do it for single outliers,
+ * but we give more weight to short transactions.
+ */
+ if (sleeps < 2)
+ nor->ready_sleep_down += 2;
+ else if (nor->ready_sleep_down > 0)
+ nor->ready_sleep_down--;
+
+ if (nor->ready_sleep_down >= 5) {
+ nor->ready_sleep >>= 1;
+ nor->ready_sleep_down = 0;
+ }
return 0;
+ }
+ if (time_after_eq(jiffies, deadline)) {
+ dev_dbg(nor->dev, "flash operation timed out\n");
+ return -ETIMEDOUT;
+ }
- cond_resched();
- }
-
- dev_dbg(nor->dev, "flash operation timed out\n");
+ fsleep(sleep);
+ sleeps++;
- return -ETIMEDOUT;
+ /*
+ * Exponentially backoff the sleep, but hard limit at
+ * 1ms to avoid responsiveness issues.
+ */
+ if (sleep < 1000)
+ sleep += (sleep >> 1) + 1;
+ }
}
/**
@@ -3449,6 +3469,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
nor->info = info;
+ /*
+ * Pick an initial sleep value that will get downtuned.
+ * We want this value to be higher than needed for most flashes
+ * as it will clamp down to the fastest transactions.
+ */
+ nor->ready_sleep = 127;
+ nor->ready_sleep_down = 0;
+
mutex_init(&nor->lock);
/* Init flash parameters based on flash_info struct and SFDP */
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index cdcfe0fd2e7d..27bb4243db64 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -406,6 +406,8 @@ struct spi_nor {
enum spi_nor_protocol reg_proto;
bool sst_write_second;
u32 flags;
+ unsigned long ready_sleep;
+ unsigned int ready_sleep_down;
enum spi_nor_cmd_ext cmd_ext_type;
struct sfdp *sfdp;
struct dentry *debugfs_root;
--
2.49.0.1151.ga128411c76-goog
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
next reply other threads:[~2025-05-23 0:24 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-23 0:14 William A. Kennington III [this message]
2025-05-27 16:48 ` [PATCH RFC] mtd: spi-nor: Sleep between ready checks Pratyush Yadav
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250523001412.878560-1-william@wkennington.com \
--to=william@wkennington.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=mwalle@kernel.org \
--cc=pratyush@kernel.org \
--cc=tudor.ambarus@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox