aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-5.15/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/backport-5.15/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch')
-rw-r--r--target/linux/generic/backport-5.15/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch102
1 files changed, 102 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.15/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch b/target/linux/generic/backport-5.15/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch
new file mode 100644
index 0000000000..b61e7ede49
--- /dev/null
+++ b/target/linux/generic/backport-5.15/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch
@@ -0,0 +1,102 @@
+From 9807ae69746196ee4bbffe7d22d22ab2b61c6ed0 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 29 Dec 2022 17:33:32 +0100
+Subject: [PATCH 1/5] net: dsa: qca8k: fix wrong length value for mgmt eth
+ packet
+
+The assumption that Documentation was right about how this value work was
+wrong. It was discovered that the length value of the mgmt header is in
+step of word size.
+
+As an example to process 4 byte of data the correct length to set is 2.
+To process 8 byte 4, 12 byte 6, 16 byte 8...
+
+Odd values will always return the next size on the ack packet.
+(length of 3 (6 byte) will always return 8 bytes of data)
+
+This means that a value of 15 (0xf) actually means reading/writing 32 bytes
+of data instead of 16 bytes. This behaviour is totally absent and not
+documented in the switch Documentation.
+
+In fact from Documentation the max value that mgmt eth can process is
+16 byte of data while in reality it can process 32 bytes at once.
+
+To handle this we always round up the length after deviding it for word
+size. We check if the result is odd and we round another time to align
+to what the switch will provide in the ack packet.
+The workaround for the length limit of 15 is still needed as the length
+reg max value is 0xf(15)
+
+Reported-by: Ronald Wahl <ronald.wahl@raritan.com>
+Tested-by: Ronald Wahl <ronald.wahl@raritan.com>
+Fixes: 90386223f44e ("net: dsa: qca8k: add support for larger read/write size with mgmt Ethernet")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Cc: stable@vger.kernel.org # v5.18+
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 45 +++++++++++++++++++++++++-------
+ 1 file changed, 35 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -146,7 +146,16 @@ static void qca8k_rw_reg_ack_handler(str
+
+ command = get_unaligned_le32(&mgmt_ethhdr->command);
+ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command);
++
+ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command);
++ /* Special case for len of 15 as this is the max value for len and needs to
++ * be increased before converting it from word to dword.
++ */
++ if (len == 15)
++ len++;
++
++ /* We can ignore odd value, we always round up them in the alloc function. */
++ len *= sizeof(u16);
+
+ /* Make sure the seq match the requested packet */
+ if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
+@@ -193,17 +202,33 @@ static struct sk_buff *qca8k_alloc_mdio_
+ if (!skb)
+ return NULL;
+
+- /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte
+- * Actually for some reason the steps are:
+- * 0: nothing
+- * 1-4: first 4 byte
+- * 5-6: first 12 byte
+- * 7-15: all 16 byte
++ /* Hdr mgmt length value is in step of word size.
++ * As an example to process 4 byte of data the correct length to set is 2.
++ * To process 8 byte 4, 12 byte 6, 16 byte 8...
++ *
++ * Odd values will always return the next size on the ack packet.
++ * (length of 3 (6 byte) will always return 8 bytes of data)
++ *
++ * This means that a value of 15 (0xf) actually means reading/writing 32 bytes
++ * of data.
++ *
++ * To correctly calculate the length we devide the requested len by word and
++ * round up.
++ * On the ack function we can skip the odd check as we already handle the
++ * case here.
+ */
+- if (len == 16)
+- real_len = 15;
+- else
+- real_len = len;
++ real_len = DIV_ROUND_UP(len, sizeof(u16));
++
++ /* We check if the result len is odd and we round up another time to
++ * the next size. (length of 3 will be increased to 4 as switch will always
++ * return 8 bytes)
++ */
++ if (real_len % sizeof(u16) != 0)
++ real_len++;
++
++ /* Max reg value is 0xf(15) but switch will always return the next size (32 byte) */
++ if (real_len == 16)
++ real_len--;
+
+ skb_reset_mac_header(skb);
+ skb_set_network_header(skb, skb->len);