mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
103 lines
3.8 KiB
Diff
103 lines
3.8 KiB
Diff
|
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);
|