Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christian Marangi and committed by
David S. Miller
9807ae69 d0395358

+35 -10
+35 -10
drivers/net/dsa/qca/qca8k-8xxx.c
··· 146 146 147 147 command = get_unaligned_le32(&mgmt_ethhdr->command); 148 148 cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command); 149 + 149 150 len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command); 151 + /* Special case for len of 15 as this is the max value for len and needs to 152 + * be increased before converting it from word to dword. 153 + */ 154 + if (len == 15) 155 + len++; 156 + 157 + /* We can ignore odd value, we always round up them in the alloc function. */ 158 + len *= sizeof(u16); 150 159 151 160 /* Make sure the seq match the requested packet */ 152 161 if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq) ··· 202 193 if (!skb) 203 194 return NULL; 204 195 205 - /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte 206 - * Actually for some reason the steps are: 207 - * 0: nothing 208 - * 1-4: first 4 byte 209 - * 5-6: first 12 byte 210 - * 7-15: all 16 byte 196 + /* Hdr mgmt length value is in step of word size. 197 + * As an example to process 4 byte of data the correct length to set is 2. 198 + * To process 8 byte 4, 12 byte 6, 16 byte 8... 199 + * 200 + * Odd values will always return the next size on the ack packet. 201 + * (length of 3 (6 byte) will always return 8 bytes of data) 202 + * 203 + * This means that a value of 15 (0xf) actually means reading/writing 32 bytes 204 + * of data. 205 + * 206 + * To correctly calculate the length we devide the requested len by word and 207 + * round up. 208 + * On the ack function we can skip the odd check as we already handle the 209 + * case here. 211 210 */ 212 - if (len == 16) 213 - real_len = 15; 214 - else 215 - real_len = len; 211 + real_len = DIV_ROUND_UP(len, sizeof(u16)); 212 + 213 + /* We check if the result len is odd and we round up another time to 214 + * the next size. (length of 3 will be increased to 4 as switch will always 215 + * return 8 bytes) 216 + */ 217 + if (real_len % sizeof(u16) != 0) 218 + real_len++; 219 + 220 + /* Max reg value is 0xf(15) but switch will always return the next size (32 byte) */ 221 + if (real_len == 16) 222 + real_len--; 216 223 217 224 skb_reset_mac_header(skb); 218 225 skb_set_network_header(skb, skb->len);