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

Configure Feed

Select the types of activity you want to include in your feed.

eth: fbnic: Read module EEPROM

Add support to read module EEPROM for fbnic. Towards this, add required
support to issue a new command to the firmware and to receive the response
to the corresponding command.

Create a local copy of the data in the completion struct before writing to
ethtool_module_eeprom to avoid writing to data in case it is freed. Given
that EEPROM pages are small, the overhead of additional copy is
negligible.

Do not block API with explicit checks since API has appropriate checks in
place for length, offset, and page.

Explicitly check bank, page, offset, and length in
fbnic_fw_parse_qsfp_read_resp() to match EEPROM read responses to the
correct request. This is important because if the driver times out waiting
for an EEPROM read response, a subsequent read request with different
values is susceptible to receiving an erroneous response (i.e., the
response to the previous request).

Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com>
Link: https://patch.msgid.link/20250922231855.3717483-1-mohsin.bashr@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Mohsin Bashir and committed by
Paolo Abeni
bb6a2265 a1f1f242

+217
+60
drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c
··· 1635 1635 } 1636 1636 } 1637 1637 1638 + static int 1639 + fbnic_get_module_eeprom_by_page(struct net_device *netdev, 1640 + const struct ethtool_module_eeprom *page_data, 1641 + struct netlink_ext_ack *extack) 1642 + { 1643 + struct fbnic_net *fbn = netdev_priv(netdev); 1644 + struct fbnic_fw_completion *fw_cmpl; 1645 + struct fbnic_dev *fbd = fbn->fbd; 1646 + int err; 1647 + 1648 + if (page_data->i2c_address != 0x50) { 1649 + NL_SET_ERR_MSG_MOD(extack, 1650 + "Invalid i2c address. Only 0x50 is supported"); 1651 + return -EINVAL; 1652 + } 1653 + 1654 + fw_cmpl = __fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_QSFP_READ_RESP, 1655 + page_data->length); 1656 + if (!fw_cmpl) 1657 + return -ENOMEM; 1658 + 1659 + /* Initialize completion and queue it for FW to process */ 1660 + fw_cmpl->u.qsfp.length = page_data->length; 1661 + fw_cmpl->u.qsfp.offset = page_data->offset; 1662 + fw_cmpl->u.qsfp.page = page_data->page; 1663 + fw_cmpl->u.qsfp.bank = page_data->bank; 1664 + 1665 + err = fbnic_fw_xmit_qsfp_read_msg(fbd, fw_cmpl, page_data->page, 1666 + page_data->bank, page_data->offset, 1667 + page_data->length); 1668 + if (err) { 1669 + NL_SET_ERR_MSG_MOD(extack, 1670 + "Failed to transmit EEPROM read request"); 1671 + goto exit_free; 1672 + } 1673 + 1674 + if (!wait_for_completion_timeout(&fw_cmpl->done, 2 * HZ)) { 1675 + err = -ETIMEDOUT; 1676 + NL_SET_ERR_MSG_MOD(extack, 1677 + "Timed out waiting for firmware response"); 1678 + goto exit_cleanup; 1679 + } 1680 + 1681 + if (fw_cmpl->result) { 1682 + err = fw_cmpl->result; 1683 + NL_SET_ERR_MSG_MOD(extack, "Failed to read EEPROM"); 1684 + goto exit_cleanup; 1685 + } 1686 + 1687 + memcpy(page_data->data, fw_cmpl->u.qsfp.data, page_data->length); 1688 + 1689 + exit_cleanup: 1690 + fbnic_mbx_clear_cmpl(fbd, fw_cmpl); 1691 + exit_free: 1692 + fbnic_fw_put_cmpl(fw_cmpl); 1693 + 1694 + return err ? : page_data->length; 1695 + } 1696 + 1638 1697 static void fbnic_set_counter(u64 *stat, struct fbnic_stat_counter *counter) 1639 1698 { 1640 1699 if (counter->reported) ··· 1900 1841 .get_link_ksettings = fbnic_phylink_ethtool_ksettings_get, 1901 1842 .get_fec_stats = fbnic_get_fec_stats, 1902 1843 .get_fecparam = fbnic_phylink_get_fecparam, 1844 + .get_module_eeprom_by_page = fbnic_get_module_eeprom_by_page, 1903 1845 .get_eth_phy_stats = fbnic_get_eth_phy_stats, 1904 1846 .get_eth_mac_stats = fbnic_get_eth_mac_stats, 1905 1847 .get_eth_ctrl_stats = fbnic_get_eth_ctrl_stats,
+135
drivers/net/ethernet/meta/fbnic/fbnic_fw.c
··· 1185 1185 } 1186 1186 1187 1187 /** 1188 + * fbnic_fw_xmit_qsfp_read_msg - Transmit a QSFP read request 1189 + * @fbd: FBNIC device structure 1190 + * @cmpl_data: Structure to store EEPROM response in 1191 + * @page: Refers to page number on page enabled QSFP modules 1192 + * @bank: Refers to a collection of pages 1193 + * @offset: Offset into QSFP EEPROM requested 1194 + * @length: Length of section of QSFP EEPROM to fetch 1195 + * 1196 + * Return: zero on success, negative value on failure 1197 + * 1198 + * Asks the firmware to provide a section of the QSFP EEPROM back in a 1199 + * message. The response will have an offset and size matching the values 1200 + * provided. 1201 + */ 1202 + int fbnic_fw_xmit_qsfp_read_msg(struct fbnic_dev *fbd, 1203 + struct fbnic_fw_completion *cmpl_data, 1204 + u32 page, u32 bank, u32 offset, u32 length) 1205 + { 1206 + struct fbnic_tlv_msg *msg; 1207 + int err = 0; 1208 + 1209 + if (!length || length > TLV_MAX_DATA) 1210 + return -EINVAL; 1211 + 1212 + msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_QSFP_READ_REQ); 1213 + if (!msg) 1214 + return -ENOMEM; 1215 + 1216 + err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_QSFP_BANK, bank); 1217 + if (err) 1218 + goto free_message; 1219 + 1220 + err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_QSFP_PAGE, page); 1221 + if (err) 1222 + goto free_message; 1223 + 1224 + err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_QSFP_OFFSET, offset); 1225 + if (err) 1226 + goto free_message; 1227 + 1228 + err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_QSFP_LENGTH, length); 1229 + if (err) 1230 + goto free_message; 1231 + 1232 + err = fbnic_mbx_map_req_w_cmpl(fbd, msg, cmpl_data); 1233 + if (err) 1234 + goto free_message; 1235 + 1236 + return 0; 1237 + 1238 + free_message: 1239 + free_page((unsigned long)msg); 1240 + return err; 1241 + } 1242 + 1243 + static const struct fbnic_tlv_index fbnic_qsfp_read_resp_index[] = { 1244 + FBNIC_TLV_ATTR_U32(FBNIC_FW_QSFP_BANK), 1245 + FBNIC_TLV_ATTR_U32(FBNIC_FW_QSFP_PAGE), 1246 + FBNIC_TLV_ATTR_U32(FBNIC_FW_QSFP_OFFSET), 1247 + FBNIC_TLV_ATTR_U32(FBNIC_FW_QSFP_LENGTH), 1248 + FBNIC_TLV_ATTR_RAW_DATA(FBNIC_FW_QSFP_DATA), 1249 + FBNIC_TLV_ATTR_S32(FBNIC_FW_QSFP_ERROR), 1250 + FBNIC_TLV_ATTR_LAST 1251 + }; 1252 + 1253 + static int fbnic_fw_parse_qsfp_read_resp(void *opaque, 1254 + struct fbnic_tlv_msg **results) 1255 + { 1256 + struct fbnic_fw_completion *cmpl_data; 1257 + struct fbnic_dev *fbd = opaque; 1258 + struct fbnic_tlv_msg *data_hdr; 1259 + u32 length, offset, page, bank; 1260 + u8 *data; 1261 + s32 err; 1262 + 1263 + /* Verify we have a completion pointer to provide with data */ 1264 + cmpl_data = fbnic_fw_get_cmpl_by_type(fbd, 1265 + FBNIC_TLV_MSG_ID_QSFP_READ_RESP); 1266 + if (!cmpl_data) 1267 + return -ENOSPC; 1268 + 1269 + bank = fta_get_uint(results, FBNIC_FW_QSFP_BANK); 1270 + if (bank != cmpl_data->u.qsfp.bank) { 1271 + dev_warn(fbd->dev, "bank not equal to bank requested: %d vs %d\n", 1272 + bank, cmpl_data->u.qsfp.bank); 1273 + err = -EINVAL; 1274 + goto msg_err; 1275 + } 1276 + 1277 + page = fta_get_uint(results, FBNIC_FW_QSFP_PAGE); 1278 + if (page != cmpl_data->u.qsfp.page) { 1279 + dev_warn(fbd->dev, "page not equal to page requested: %d vs %d\n", 1280 + page, cmpl_data->u.qsfp.page); 1281 + err = -EINVAL; 1282 + goto msg_err; 1283 + } 1284 + 1285 + offset = fta_get_uint(results, FBNIC_FW_QSFP_OFFSET); 1286 + length = fta_get_uint(results, FBNIC_FW_QSFP_LENGTH); 1287 + 1288 + if (length != cmpl_data->u.qsfp.length || 1289 + offset != cmpl_data->u.qsfp.offset) { 1290 + dev_warn(fbd->dev, 1291 + "offset/length not equal to size requested: %d/%d vs %d/%d\n", 1292 + offset, length, 1293 + cmpl_data->u.qsfp.offset, cmpl_data->u.qsfp.length); 1294 + err = -EINVAL; 1295 + goto msg_err; 1296 + } 1297 + 1298 + err = fta_get_sint(results, FBNIC_FW_QSFP_ERROR); 1299 + if (err) 1300 + goto msg_err; 1301 + 1302 + data_hdr = results[FBNIC_FW_QSFP_DATA]; 1303 + if (!data_hdr) { 1304 + err = -ENODATA; 1305 + goto msg_err; 1306 + } 1307 + 1308 + /* Copy data */ 1309 + data = fbnic_tlv_attr_get_value_ptr(data_hdr); 1310 + memcpy(cmpl_data->u.qsfp.data, data, length); 1311 + msg_err: 1312 + cmpl_data->result = err; 1313 + complete(&cmpl_data->done); 1314 + fbnic_fw_put_cmpl(cmpl_data); 1315 + 1316 + return err; 1317 + } 1318 + 1319 + /** 1188 1320 * fbnic_fw_xmit_tsene_read_msg - Create and transmit a sensor read request 1189 1321 * @fbd: FBNIC device structure 1190 1322 * @cmpl_data: Completion data structure to store sensor response ··· 1577 1445 FBNIC_TLV_PARSER(FW_FINISH_UPGRADE_REQ, 1578 1446 fbnic_fw_finish_upgrade_req_index, 1579 1447 fbnic_fw_parse_fw_finish_upgrade_req), 1448 + FBNIC_TLV_PARSER(QSFP_READ_RESP, 1449 + fbnic_qsfp_read_resp_index, 1450 + fbnic_fw_parse_qsfp_read_resp), 1580 1451 FBNIC_TLV_PARSER(TSENE_READ_RESP, 1581 1452 fbnic_tsene_read_resp_index, 1582 1453 fbnic_fw_parse_tsene_read_resp),
+22
drivers/net/ethernet/meta/fbnic/fbnic_fw.h
··· 79 79 u32 length; 80 80 } fw_update; 81 81 struct { 82 + u16 length; 83 + u8 offset; 84 + u8 page; 85 + u8 bank; 86 + u8 data[] __aligned(sizeof(u32)) __counted_by(length); 87 + } qsfp; 88 + struct { 82 89 s32 millivolts; 83 90 s32 millidegrees; 84 91 } tsene; ··· 116 109 int fbnic_fw_xmit_fw_write_chunk(struct fbnic_dev *fbd, 117 110 const u8 *data, u32 offset, u16 length, 118 111 int cancel_error); 112 + int fbnic_fw_xmit_qsfp_read_msg(struct fbnic_dev *fbd, 113 + struct fbnic_fw_completion *cmpl_data, 114 + u32 page, u32 bank, u32 offset, u32 length); 119 115 int fbnic_fw_xmit_tsene_read_msg(struct fbnic_dev *fbd, 120 116 struct fbnic_fw_completion *cmpl_data); 121 117 int fbnic_fw_xmit_send_logs(struct fbnic_dev *fbd, bool enable, ··· 171 161 FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_RESP = 0x25, 172 162 FBNIC_TLV_MSG_ID_FW_FINISH_UPGRADE_REQ = 0x28, 173 163 FBNIC_TLV_MSG_ID_FW_FINISH_UPGRADE_RESP = 0x29, 164 + FBNIC_TLV_MSG_ID_QSFP_READ_REQ = 0x38, 165 + FBNIC_TLV_MSG_ID_QSFP_READ_RESP = 0x39, 174 166 FBNIC_TLV_MSG_ID_TSENE_READ_REQ = 0x3C, 175 167 FBNIC_TLV_MSG_ID_TSENE_READ_RESP = 0x3D, 176 168 FBNIC_TLV_MSG_ID_LOG_SEND_LOGS_REQ = 0x43, ··· 219 207 FBNIC_FW_LINK_FEC_NONE = 1, 220 208 FBNIC_FW_LINK_FEC_RS = 2, 221 209 FBNIC_FW_LINK_FEC_BASER = 3, 210 + }; 211 + 212 + enum { 213 + FBNIC_FW_QSFP_BANK = 0x0, 214 + FBNIC_FW_QSFP_PAGE = 0x1, 215 + FBNIC_FW_QSFP_OFFSET = 0x2, 216 + FBNIC_FW_QSFP_LENGTH = 0x3, 217 + FBNIC_FW_QSFP_ERROR = 0x4, 218 + FBNIC_FW_QSFP_DATA = 0x5, 219 + FBNIC_FW_QSFP_MSG_MAX 222 220 }; 223 221 224 222 enum {