lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20210520135031.2969183-1-olteanv@gmail.com>
Date:   Thu, 20 May 2021 16:50:31 +0300
From:   Vladimir Oltean <olteanv@...il.com>
To:     Jakub Kicinski <kuba@...nel.org>,
        "David S. Miller" <davem@...emloft.net>, netdev@...r.kernel.org
Cc:     Florian Fainelli <f.fainelli@...il.com>,
        Andrew Lunn <andrew@...n.ch>,
        Vivien Didelot <vivien.didelot@...il.com>,
        Mark Brown <broonie@...nel.org>, linux-spi@...r.kernel.org,
        Guenter Roeck <linux@...ck-us.net>,
        Vladimir Oltean <vladimir.oltean@....com>
Subject: [PATCH net-next] net: dsa: sja1105: adapt to a SPI controller with a limited max transfer size

From: Vladimir Oltean <vladimir.oltean@....com>

The static config of the sja1105 switch is a long stream of bytes which
is programmed to the hardware in chunks (portions with the chip select
continuously asserted) of max 256 bytes each.

Only that certain SPI controllers, such as the spi-sc18is602 I2C-to-SPI
bridge, cannot keep the chip select asserted for that long.
The spi_max_transfer_size() and spi_max_message_size() functions are how
the controller can impose its hardware limitations upon the SPI
peripheral driver.

The sja1105 sends its static config to the SPI master as a huge
scatter/gather spi_message - commit 08839c06e96f ("net: dsa: sja1105:
Switch to scatter/gather API for SPI") contains a description of that.
That spi_message contains the following list of spi_transfers:

                |                         |      cs_change
 spi_transfer # |       Contents          | (deassert chip select)
 ---------------|-------------------------|-----------------------
       1        |   SPI message header 1  |         no
       2        |  Static config chunk 1  |         yes
       3        |   SPI message header 2  |         no
       4        |  Static config chunk 2  |         yes
      ...       |           ...           |         ...

Since what the SPI master does not support is keeping the CS asserted
for more than, say, 200 bytes, we must limit the summed length of the
spi_transfers with cs_change deasserted (1+2, 3+4 etc) lower than 200.

This is a bit fuzzy, but I think the proper way to handle this is to
just look at the spi_max_transfer_size() reported by the master, and
adapt to that. We can disregard the spi_max_message_size() limit since I
suppose that assumes no cs_change - but then again, spi_max_transfer_size
is itself capped by spi_max_message_size, which makes sense.

Regression-tested on a switch connected to a controller with no
limitations (spi-fsl-dspi) as well as with one with caps for both
max_transfer_size and max_message_size (spi-sc18is602).

Signed-off-by: Vladimir Oltean <vladimir.oltean@....com>
---
 drivers/net/dsa/sja1105/sja1105_spi.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c
index f7a1514f81e8..ac766def45c8 100644
--- a/drivers/net/dsa/sja1105/sja1105_spi.c
+++ b/drivers/net/dsa/sja1105/sja1105_spi.c
@@ -46,18 +46,22 @@ static int sja1105_xfer(const struct sja1105_private *priv,
 			sja1105_spi_rw_mode_t rw, u64 reg_addr, u8 *buf,
 			size_t len, struct ptp_system_timestamp *ptp_sts)
 {
+	struct spi_device *spi = priv->spidev;
 	struct sja1105_chunk chunk = {
-		.len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN),
 		.reg_addr = reg_addr,
 		.buf = buf,
 	};
-	struct spi_device *spi = priv->spidev;
 	struct spi_transfer *xfers;
+	size_t xfer_len;
 	int num_chunks;
 	int rc, i = 0;
 	u8 *hdr_bufs;
 
-	num_chunks = DIV_ROUND_UP(len, SJA1105_SIZE_SPI_MSG_MAXLEN);
+	xfer_len = min_t(size_t, SJA1105_SIZE_SPI_MSG_MAXLEN,
+			 spi_max_transfer_size(spi) - SJA1105_SIZE_SPI_MSG_HEADER);
+
+	num_chunks = DIV_ROUND_UP(len, xfer_len);
+	chunk.len = min(len, xfer_len);
 
 	/* One transfer for each message header, one for each message
 	 * payload (chunk).
@@ -127,7 +131,7 @@ static int sja1105_xfer(const struct sja1105_private *priv,
 		chunk.buf += chunk.len;
 		chunk.reg_addr += chunk.len / 4;
 		chunk.len = min_t(size_t, (ptrdiff_t)(buf + len - chunk.buf),
-				  SJA1105_SIZE_SPI_MSG_MAXLEN);
+				  xfer_len);
 
 		/* De-assert the chip select after each chunk. */
 		if (chunk.len)
-- 
2.25.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ