All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Laight <david.laight.linux@gmail.com>
To: Peter Collingbourne <peter@pcc.me.uk>
Cc: "Mark Brown" <broonie@kernel.org>,
	"Christophe Kerello" <christophe.kerello@foss.st.com>,
	"Patrice Chotard" <patrice.chotard@foss.st.com>,
	"Boris Brezillon" <boris.brezillon@collabora.com>,
	linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Jani Nikula" <jani.nikula@intel.com>,
	"Ville Syrjälä" <ville.syrjala@linux.intel.com>,
	"Simona Vetter" <simona.vetter@ffwll.ch>,
	"Randy Dunlap" <rdunlap@infradead.org>
Subject: Re: [PATCH v2] iopoll: use udelay() for initial polling
Date: Tue, 19 May 2026 19:35:43 +0100	[thread overview]
Message-ID: <20260519193543.18f45af2@pumpkin> (raw)
In-Reply-To: <20260519102446.209723-1-peter@pcc.me.uk>

On Tue, 19 May 2026 03:24:46 -0700
Peter Collingbourne <peter@pcc.me.uk> wrote:

> A short polling delay, such as the delay of 5us
> (SPINAND_READ_POLL_DELAY_US) provided by the SPI NAND driver,
> can become a 1/HZ (order of ms) delay caused by the usleep_range()
> call in read_poll_timeout(), significantly reducing SPI NAND access
> performance. Fix it by adjusting the read_poll_timeout() macro to use
> udelay() to delay until 1/10 of a timer tick after it is called, and
> only then sleep.
> 
> Fixes: c955a0cc8a28 ("spi: spi-mem: add automatic poll status functions")
> Signed-off-by: Peter Collingbourne <peter@pcc.me.uk>
> ---
>  include/linux/iopoll.h | 30 ++++++++++++++++++++++--------
>  1 file changed, 22 insertions(+), 8 deletions(-)
> 
> v2:
> * Fix it in read_poll_timeout() instead
> 
> diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h
> index 53edd69acb9b..2ee89b76f072 100644
> --- a/include/linux/iopoll.h
> +++ b/include/linux/iopoll.h
> @@ -19,9 +19,11 @@
>   *
>   * @op: Operation
>   * @cond: Break condition
> - * @sleep_us: Maximum time to sleep between operations in us (0 tight-loops).
> - *            Please read usleep_range() function description for details and
> - *            limitations.
> + * @sleep_us: Maximum time to sleep or delay between operations in us
> + *            (0 tight-loops). Please read usleep_range() and udelay()
> + *            function descriptions for details and limitations.
> + *            This macro will delay until 1/10 of a timer tick after
> + *            it is called, and will then start sleeping.
>   * @timeout_us: Timeout in us, 0 means never timeout
>   * @sleep_before_op: if it is true, sleep @sleep_us before operation.
>   *
> @@ -35,11 +37,18 @@
>  ({ \
>  	u64 __timeout_us = (timeout_us); \
>  	unsigned long __sleep_us = (sleep_us); \
> -	ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
> +	ktime_t __start_time = ktime_get(); \
> +	u64 __delay_timeout_us = 100000/HZ; \
> +	ktime_t __delay_timeout = ktime_add_us(__start_time, __delay_timeout_us); \
> +	ktime_t __timeout = ktime_add_us(__start_time, __timeout_us); \
>  	int ___ret; \
>  	might_sleep_if((__sleep_us) != 0); \
> -	if ((sleep_before_op) && __sleep_us) \
> -		usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
> +	if ((sleep_before_op) && __sleep_us) { \
> +		if (__sleep_us <= __delay_timeout_us) \
> +			udelay(__sleep_us); \
> +		else \
> +			usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
> +	} \
>  	for (;;) { \
>  		bool __expired = __timeout_us && \
>  			ktime_compare(ktime_get(), __timeout) > 0; \
> @@ -54,8 +63,13 @@
>  			___ret = -ETIMEDOUT; \
>  			break; \
>  		} \
> -		if (__sleep_us) \
> -			usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
> +		if (__sleep_us) { \
> +			if (__sleep_us <= __delay_timeout_us && \
> +			    ktime_compare(ktime_get(), __delay_timeout) < 0) \
> +				udelay(__sleep_us); \
> +			else \
> +				usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
> +		} \
>  		cpu_relax(); \
>  	} \
>  	___ret; \

How about:
#define poll_timeout_us(op, cond, sleep_us, timeout_us, sleep_before_op) \
({ \
	u64 __timeout_us = (timeout_us); \
	unsigned long __sleep_us = (sleep_us); \
	ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
	u64 __delay_timeout_us = 100000/HZ; \
	int ___ret; \
	bool __expired; \
	might_sleep_if((__sleep_us) != 0); \
\
	for (;; sleep_before_op = false) { \
		if (!sleep_before_op) { \
			__expired = __timeout_us && \
				ktime_compare(ktime_get(), __timeout) > 0; \
			/* guarantee 'op' and 'cond' are evaluated after timeout expired */ \
			barrier(); \
			op; \
			if (cond) { \
				___ret = 0; \
				break; \
			} \
			if (__expired) { \
				___ret = -ETIMEDOUT; \
				break; \
			} \
		} \
		if (__sleep_us > __delay_timeout_us) { \
			usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
			continue;
		} \
		if (__sleep_us) { \
			__delay_timeout_us -= __sleep_us; \
			udelay(__sleep_us); \
		} \
		cpu_relax(); \
	} \
	___ret; \
})
Which I think is approximately equivalent.
But I'm not at all sure the usleep/udelay test it right.
100000/HZ is a strange number of usecs; for HZ=100 it is 1ms, but for HZ=1000 0.1ms.

Maybe it should be more like:
	u32 __delay_timeout_us = __sleep_us > 20 ? 0 : 100;
so that you delay for (approx) max 100us if the interval is less than 20us.

The is also a mismatch of long and u64.
I don't think anything (except the time_t) needs to be 64bit (esp. on 32bit).

I'm not sure about all architectures, but I'm pretty sure than on x86
usleep_range() is independent of HZ.

-- David


  reply	other threads:[~2026-05-19 18:35 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-19 10:24 [PATCH v2] iopoll: use udelay() for initial polling Peter Collingbourne
2026-05-19 18:35 ` David Laight [this message]
2026-05-20  7:38   ` Peter Collingbourne
2026-05-20 13:29 ` Ville Syrjälä
2026-05-21  5:59   ` Peter Collingbourne
2026-05-21  7:03     ` Jani Nikula
2026-05-22 14:50       ` Mark Brown
2026-05-29  5:59         ` Peter Collingbourne
2026-05-29 11:20           ` David Laight
2026-05-29 21:56             ` Mark Brown
2026-05-30 10:20               ` David Laight

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=20260519193543.18f45af2@pumpkin \
    --to=david.laight.linux@gmail.com \
    --cc=boris.brezillon@collabora.com \
    --cc=broonie@kernel.org \
    --cc=christophe.kerello@foss.st.com \
    --cc=jani.nikula@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    --cc=patrice.chotard@foss.st.com \
    --cc=peter@pcc.me.uk \
    --cc=rdunlap@infradead.org \
    --cc=simona.vetter@ffwll.ch \
    --cc=ville.syrjala@linux.intel.com \
    /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 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.