2023-01-13 17:44:18

by Serge Semin

[permalink] [raw]
Subject: [PATCH] spi: dw: Fix wrong FIFO level setting for long xfers

Due to using the u16 type in the min_t() macros the SPI transfer length
will be cast to word before participating in the conditional statement
implied by the macro. Thus if the transfer length is greater than 64KB the
Tx/Rx FIFO threshold level value will be determined by the leftover of the
truncated after the type-case length. In the worst case it will cause
having the "Tx FIFO Empty" or "Rx FIFO Full" interrupts triggered on each
word sent/received to/from the bus. In its turn it will cause the
dramatical performance drop.

The problem can be easily fixed by using the min() macros instead of
min_t() which doesn't imply any type casting thus preventing the possible
data loss.

Fixes: ea11370fffdf ("spi: dw: get TX level without an additional variable")
Reported-by: Sergey Nazarov <[email protected]>
Signed-off-by: Serge Semin <[email protected]>
---
drivers/spi/spi-dw-core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 99edddf9958b..b3d287f401cd 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -366,7 +366,7 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
* will be adjusted at the final stage of the IRQ-based SPI transfer
* execution so not to lose the leftover of the incoming data.
*/
- level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
+ level = min(dws->fifo_len / 2, dws->tx_len);
dw_writel(dws, DW_SPI_TXFTLR, level);
dw_writel(dws, DW_SPI_RXFTLR, level - 1);

--
2.39.0



2023-01-13 17:47:53

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH] spi: dw: Fix wrong FIFO level setting for long xfers

On Fri, Jan 13, 2023 at 6:57 PM Serge Semin
<[email protected]> wrote:
>
> Due to using the u16 type in the min_t() macros the SPI transfer length
> will be cast to word before participating in the conditional statement
> implied by the macro. Thus if the transfer length is greater than 64KB the
> Tx/Rx FIFO threshold level value will be determined by the leftover of the
> truncated after the type-case length. In the worst case it will cause
> having the "Tx FIFO Empty" or "Rx FIFO Full" interrupts triggered on each
> word sent/received to/from the bus. In its turn it will cause the
> dramatical performance drop.
>
> The problem can be easily fixed by using the min() macros instead of
> min_t() which doesn't imply any type casting thus preventing the possible
> data loss.

But this would be problematic if the types of the parameters are different.
Currently they are u32 vs. unsigned int. I would rather assume that
FIFO length is always less than or equal to 64K and just change the
type in min_t to follow what dws->tx_len is.

--
With Best Regards,
Andy Shevchenko

2023-01-13 18:45:13

by Serge Semin

[permalink] [raw]
Subject: Re: [PATCH] spi: dw: Fix wrong FIFO level setting for long xfers

On Fri, Jan 13, 2023 at 07:33:16PM +0200, Andy Shevchenko wrote:
> On Fri, Jan 13, 2023 at 6:57 PM Serge Semin
> <[email protected]> wrote:
> >
> > Due to using the u16 type in the min_t() macros the SPI transfer length
> > will be cast to word before participating in the conditional statement
> > implied by the macro. Thus if the transfer length is greater than 64KB the
> > Tx/Rx FIFO threshold level value will be determined by the leftover of the
> > truncated after the type-case length. In the worst case it will cause
> > having the "Tx FIFO Empty" or "Rx FIFO Full" interrupts triggered on each
> > word sent/received to/from the bus. In its turn it will cause the
> > dramatical performance drop.
> >
> > The problem can be easily fixed by using the min() macros instead of
> > min_t() which doesn't imply any type casting thus preventing the possible
> > data loss.
>

> But this would be problematic if the types of the parameters are different.
> Currently they are u32 vs. unsigned int.

Yes, it would but only in case if somebody changes their types. As you
said they are currently of u32 and unsigned int types which are the
same on all the currently supported platforms. So even if somebody
changes the type of any of them then the compiler will warn about it
anyway.

> I would rather assume that
> FIFO length is always less than or equal to 64K and just change the
> type in min_t to follow what dws->tx_len is.

There is no need in assuming in this case. FIFO depth doesn't exceed
256 xfer words by the DW SSI IP-core design (judging by the constraints
applied to the SSI_RX_FIFO_DEPTH and SSI_TX_FIFO_DEPTH synthesize
parameters). So the dws->fifo_len can be easily converted to u16 type.
The problem is in the tx_len field casting to u16. It's a rare case,
but the SPI xfers length can be greater than 64K. The
spi_transfer.len field is of the unsigned int type and the SPI-core
doesn't have any constraints to that (except the one defined by the
controller drivers).

So to make sure I correctly understand what you meant. Do you suggest
to do something like this (it was my first version of the fix):
- level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
+ level = min_t(u32, dws->fifo_len / 2, dws->tx_len);
or even like this
- level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
+ level = min_t(typeof(dws->tx_len), dws->fifo_len / 2, dws->tx_len);
?

Personally I would prefer either my solution with just min() macros
usage (which in case of the types change will give the compile-time
warning about the types mismatch) or using the min_t(u32, ...) version
(using typeof() seems overkill). I don't see much different (do you?).
Both versions have their pros and cons.

-Serge(y)

>
> --
> With Best Regards,
> Andy Shevchenko

2023-01-13 19:01:05

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH] spi: dw: Fix wrong FIFO level setting for long xfers

On Fri, Jan 13, 2023 at 09:18:54PM +0300, Serge Semin wrote:
> On Fri, Jan 13, 2023 at 07:33:16PM +0200, Andy Shevchenko wrote:
> > On Fri, Jan 13, 2023 at 6:57 PM Serge Semin
> > <[email protected]> wrote:
> > >
> > > Due to using the u16 type in the min_t() macros the SPI transfer length
> > > will be cast to word before participating in the conditional statement
> > > implied by the macro. Thus if the transfer length is greater than 64KB the
> > > Tx/Rx FIFO threshold level value will be determined by the leftover of the
> > > truncated after the type-case length. In the worst case it will cause
> > > having the "Tx FIFO Empty" or "Rx FIFO Full" interrupts triggered on each
> > > word sent/received to/from the bus. In its turn it will cause the
> > > dramatical performance drop.
> > >
> > > The problem can be easily fixed by using the min() macros instead of
> > > min_t() which doesn't imply any type casting thus preventing the possible
> > > data loss.
>
> > But this would be problematic if the types of the parameters are different.
> > Currently they are u32 vs. unsigned int.
>
> Yes, it would but only in case if somebody changes their types. As you
> said they are currently of u32 and unsigned int types which are the
> same on all the currently supported platforms. So even if somebody
> changes the type of any of them then the compiler will warn about it
> anyway.
>
> > I would rather assume that
> > FIFO length is always less than or equal to 64K and just change the
> > type in min_t to follow what dws->tx_len is.
>
> There is no need in assuming in this case. FIFO depth doesn't exceed
> 256 xfer words by the DW SSI IP-core design (judging by the constraints
> applied to the SSI_RX_FIFO_DEPTH and SSI_TX_FIFO_DEPTH synthesize
> parameters). So the dws->fifo_len can be easily converted to u16 type.
> The problem is in the tx_len field casting to u16. It's a rare case,
> but the SPI xfers length can be greater than 64K. The
> spi_transfer.len field is of the unsigned int type and the SPI-core
> doesn't have any constraints to that (except the one defined by the
> controller drivers).
>
> So to make sure I correctly understand what you meant. Do you suggest
> to do something like this (it was my first version of the fix):
> - level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
> + level = min_t(u32, dws->fifo_len / 2, dws->tx_len);
> or even like this
> - level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
> + level = min_t(typeof(dws->tx_len), dws->fifo_len / 2, dws->tx_len);
> ?

No, I suggest

level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);

So, we do not care about changing of the fifo_len type, and we won't issue
a compiler warning if it becomes, let's say, u8. While your solution will
still produce it.

> Personally I would prefer either my solution with just min() macros
> usage (which in case of the types change will give the compile-time
> warning about the types mismatch) or using the min_t(u32, ...) version
> (using typeof() seems overkill). I don't see much different (do you?).

Yes, hence personally I prefer my proposal.

> Both versions have their pros and cons.

Right.

--
With Best Regards,
Andy Shevchenko


2023-01-13 19:09:10

by Serge Semin

[permalink] [raw]
Subject: Re: [PATCH] spi: dw: Fix wrong FIFO level setting for long xfers

On Fri, Jan 13, 2023 at 08:30:57PM +0200, Andy Shevchenko wrote:
> On Fri, Jan 13, 2023 at 09:18:54PM +0300, Serge Semin wrote:
> > On Fri, Jan 13, 2023 at 07:33:16PM +0200, Andy Shevchenko wrote:
> > > On Fri, Jan 13, 2023 at 6:57 PM Serge Semin
> > > <[email protected]> wrote:
> > > >
> > > > Due to using the u16 type in the min_t() macros the SPI transfer length
> > > > will be cast to word before participating in the conditional statement
> > > > implied by the macro. Thus if the transfer length is greater than 64KB the
> > > > Tx/Rx FIFO threshold level value will be determined by the leftover of the
> > > > truncated after the type-case length. In the worst case it will cause
> > > > having the "Tx FIFO Empty" or "Rx FIFO Full" interrupts triggered on each
> > > > word sent/received to/from the bus. In its turn it will cause the
> > > > dramatical performance drop.
> > > >
> > > > The problem can be easily fixed by using the min() macros instead of
> > > > min_t() which doesn't imply any type casting thus preventing the possible
> > > > data loss.
> >
> > > But this would be problematic if the types of the parameters are different.
> > > Currently they are u32 vs. unsigned int.
> >
> > Yes, it would but only in case if somebody changes their types. As you
> > said they are currently of u32 and unsigned int types which are the
> > same on all the currently supported platforms. So even if somebody
> > changes the type of any of them then the compiler will warn about it
> > anyway.
> >
> > > I would rather assume that
> > > FIFO length is always less than or equal to 64K and just change the
> > > type in min_t to follow what dws->tx_len is.
> >
> > There is no need in assuming in this case. FIFO depth doesn't exceed
> > 256 xfer words by the DW SSI IP-core design (judging by the constraints
> > applied to the SSI_RX_FIFO_DEPTH and SSI_TX_FIFO_DEPTH synthesize
> > parameters). So the dws->fifo_len can be easily converted to u16 type.
> > The problem is in the tx_len field casting to u16. It's a rare case,
> > but the SPI xfers length can be greater than 64K. The
> > spi_transfer.len field is of the unsigned int type and the SPI-core
> > doesn't have any constraints to that (except the one defined by the
> > controller drivers).
> >
> > So to make sure I correctly understand what you meant. Do you suggest
> > to do something like this (it was my first version of the fix):
> > - level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
> > + level = min_t(u32, dws->fifo_len / 2, dws->tx_len);
> > or even like this
> > - level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
> > + level = min_t(typeof(dws->tx_len), dws->fifo_len / 2, dws->tx_len);
> > ?
>

> No, I suggest
>
> level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);
>
> So, we do not care about changing of the fifo_len type, and we won't issue
> a compiler warning if it becomes, let's say, u8. While your solution will
> still produce it.
>
> > Personally I would prefer either my solution with just min() macros
> > usage (which in case of the types change will give the compile-time
> > warning about the types mismatch) or using the min_t(u32, ...) version
> > (using typeof() seems overkill). I don't see much different (do you?).
>
> Yes, hence personally I prefer my proposal.

Ok. min_t(unsigned int, ...) it's then. I'll resubmit v2 shortly.

-Serge(y)

>
> > Both versions have their pros and cons.
>
> Right.
>
> --
> With Best Regards,
> Andy Shevchenko
>
>

2023-01-13 19:11:03

by Serge Semin

[permalink] [raw]
Subject: [PATCH v2] spi: dw: Fix wrong FIFO level setting for long xfers

Due to using the u16 type in the min_t() macros the SPI transfer length
will be cast to word before participating in the conditional statement
implied by the macro. Thus if the transfer length is greater than 64KB the
Tx/Rx FIFO threshold level value will be determined by the leftover of the
truncated after the type-case length. In the worst case it will cause the
dramatical performance drop due to the "Tx FIFO Empty" or "Rx FIFO Full"
interrupts triggered on each xfer word sent/received to/from the bus.

The problem can be easily fixed by specifying the unsigned int type in the
min_t() macros thus preventing the possible data loss.

Fixes: ea11370fffdf ("spi: dw: get TX level without an additional variable")
Reported-by: Sergey Nazarov <[email protected]>
Signed-off-by: Serge Semin <[email protected]>

---

Changelog v2:
- Use min_t(unisgned int, ...) macros instead of just min(). (@Andy)
---
drivers/spi/spi-dw-core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 99edddf9958b..c3bfb6c84cab 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -366,7 +366,7 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
* will be adjusted at the final stage of the IRQ-based SPI transfer
* execution so not to lose the leftover of the incoming data.
*/
- level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
+ level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);
dw_writel(dws, DW_SPI_TXFTLR, level);
dw_writel(dws, DW_SPI_RXFTLR, level - 1);

--
2.39.0


2023-01-13 19:42:40

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v2] spi: dw: Fix wrong FIFO level setting for long xfers

On Fri, Jan 13, 2023 at 09:59:42PM +0300, Serge Semin wrote:
> Due to using the u16 type in the min_t() macros the SPI transfer length
> will be cast to word before participating in the conditional statement
> implied by the macro. Thus if the transfer length is greater than 64KB the
> Tx/Rx FIFO threshold level value will be determined by the leftover of the
> truncated after the type-case length. In the worst case it will cause the
> dramatical performance drop due to the "Tx FIFO Empty" or "Rx FIFO Full"
> interrupts triggered on each xfer word sent/received to/from the bus.
>
> The problem can be easily fixed by specifying the unsigned int type in the
> min_t() macros thus preventing the possible data loss.

LGTM,

Reviewed-by: Andy Shevchenko <[email protected]>

thanks!

> Fixes: ea11370fffdf ("spi: dw: get TX level without an additional variable")
> Reported-by: Sergey Nazarov <[email protected]>
> Signed-off-by: Serge Semin <[email protected]>
>
> ---
>
> Changelog v2:
> - Use min_t(unisgned int, ...) macros instead of just min(). (@Andy)
> ---
> drivers/spi/spi-dw-core.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
> index 99edddf9958b..c3bfb6c84cab 100644
> --- a/drivers/spi/spi-dw-core.c
> +++ b/drivers/spi/spi-dw-core.c
> @@ -366,7 +366,7 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
> * will be adjusted at the final stage of the IRQ-based SPI transfer
> * execution so not to lose the leftover of the incoming data.
> */
> - level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
> + level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);
> dw_writel(dws, DW_SPI_TXFTLR, level);
> dw_writel(dws, DW_SPI_RXFTLR, level - 1);
>
> --
> 2.39.0
>
>

--
With Best Regards,
Andy Shevchenko


2023-01-16 13:53:53

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v2] spi: dw: Fix wrong FIFO level setting for long xfers

On Fri, 13 Jan 2023 21:59:42 +0300, Serge Semin wrote:
> Due to using the u16 type in the min_t() macros the SPI transfer length
> will be cast to word before participating in the conditional statement
> implied by the macro. Thus if the transfer length is greater than 64KB the
> Tx/Rx FIFO threshold level value will be determined by the leftover of the
> truncated after the type-case length. In the worst case it will cause the
> dramatical performance drop due to the "Tx FIFO Empty" or "Rx FIFO Full"
> interrupts triggered on each xfer word sent/received to/from the bus.
>
> [...]

Applied to

broonie/spi.git for-next

Thanks!

[1/1] spi: dw: Fix wrong FIFO level setting for long xfers
commit: 9ef7b7b43eb708c114bb3ce6c0acadd74065bf4e

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark