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

ethtool: cmis_cdb: Fix incorrect read / write length extension

The 'read_write_len_ext' field in 'struct ethtool_cmis_cdb_cmd_args'
stores the maximum number of bytes that can be read from or written to
the Local Payload (LPL) page in a single multi-byte access.

Cited commit started overwriting this field with the maximum number of
bytes that can be read from or written to the Extended Payload (LPL)
pages in a single multi-byte access. Transceiver modules that support
auto paging can advertise a number larger than 255 which is problematic
as 'read_write_len_ext' is a 'u8', resulting in the number getting
truncated and firmware flashing failing [1].

Fix by ignoring the maximum EPL access size as the kernel does not
currently support auto paging (even if the transceiver module does) and
will not try to read / write more than 128 bytes at once.

[1]
Transceiver module firmware flashing started for device enp177s0np0
Transceiver module firmware flashing in progress for device enp177s0np0
Progress: 0%
Transceiver module firmware flashing encountered an error for device enp177s0np0
Status message: Write FW block EPL command failed, LPL length is longer
than CDB read write length extension allows.

Fixes: 9a3b0d078bd8 ("net: ethtool: Add support for writing firmware blocks using EPL payload")
Reported-by: Damodharam Ammepalli <damodharam.ammepalli@broadcom.com>
Closes: https://lore.kernel.org/netdev/20250402183123.321036-3-michael.chan@broadcom.com/
Tested-by: Damodharam Ammepalli <damodharam.ammepalli@broadcom.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Damodharam Ammepalli <damodharam.ammepalli@broadcom.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
Link: https://patch.msgid.link/20250409112440.365672-1-idosch@nvidia.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Ido Schimmel and committed by
Paolo Abeni
eaa517b7 69ddc652

+3 -16
-1
net/ethtool/cmis.h
··· 101 101 }; 102 102 103 103 u32 ethtool_cmis_get_max_lpl_size(u8 num_of_byte_octs); 104 - u32 ethtool_cmis_get_max_epl_size(u8 num_of_byte_octs); 105 104 106 105 void ethtool_cmis_cdb_compose_args(struct ethtool_cmis_cdb_cmd_args *args, 107 106 enum ethtool_cmis_cdb_cmd_id cmd, u8 *lpl,
+3 -15
net/ethtool/cmis_cdb.c
··· 16 16 return 8 * (1 + min_t(u8, num_of_byte_octs, 15)); 17 17 } 18 18 19 - /* For accessing the EPL field on page 9Fh, the allowable length extension is 20 - * min(i, 255) byte octets where i specifies the allowable additional number of 21 - * byte octets in a READ or a WRITE. 22 - */ 23 - u32 ethtool_cmis_get_max_epl_size(u8 num_of_byte_octs) 24 - { 25 - return 8 * (1 + min_t(u8, num_of_byte_octs, 255)); 26 - } 27 - 28 19 void ethtool_cmis_cdb_compose_args(struct ethtool_cmis_cdb_cmd_args *args, 29 20 enum ethtool_cmis_cdb_cmd_id cmd, u8 *lpl, 30 21 u8 lpl_len, u8 *epl, u16 epl_len, ··· 24 33 { 25 34 args->req.id = cpu_to_be16(cmd); 26 35 args->req.lpl_len = lpl_len; 27 - if (lpl) { 36 + if (lpl) 28 37 memcpy(args->req.payload, lpl, args->req.lpl_len); 29 - args->read_write_len_ext = 30 - ethtool_cmis_get_max_lpl_size(read_write_len_ext); 31 - } 32 38 if (epl) { 33 39 args->req.epl_len = cpu_to_be16(epl_len); 34 40 args->req.epl = epl; 35 - args->read_write_len_ext = 36 - ethtool_cmis_get_max_epl_size(read_write_len_ext); 37 41 } 38 42 39 43 args->max_duration = max_duration; 44 + args->read_write_len_ext = 45 + ethtool_cmis_get_max_lpl_size(read_write_len_ext); 40 46 args->msleep_pre_rpl = msleep_pre_rpl; 41 47 args->rpl_exp_len = rpl_exp_len; 42 48 args->flags = flags;