diff --git a/SOURCES/0001-sff-8636-Fix-parsing-of-Page-03h-in-IOCTL-path.patch b/SOURCES/0001-sff-8636-Fix-parsing-of-Page-03h-in-IOCTL-path.patch new file mode 100644 index 0000000..d08ab1e --- /dev/null +++ b/SOURCES/0001-sff-8636-Fix-parsing-of-Page-03h-in-IOCTL-path.patch @@ -0,0 +1,110 @@ +From bb89624c1a62de701f87d7deb669e40586c920d2 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 14 Sep 2021 14:27:34 +0300 +Subject: [PATCH 01/35] sff-8636: Fix parsing of Page 03h in IOCTL path + +The offset of Page 03h compared to the base address of the Lower Memory +is 512 bytes. However, all the offsets to the page start at address 128, +which is the address that separates Lower and Upper memory (see Figure +6-1 in SFF-8636). Therefore, reading these offsets compared to the start +of Page 03h results in incorrect memory accesses as can be seen in the +output below. + +Instead, pass Page 03h with the correct offset. + +This is a temporary solution until SFF-8636 is refactored to use a +memory map for parsing. + +Before patch: + + # ethtool -m swp13 + ... + Laser bias current high alarm threshold : 16.448 mA + Laser bias current low alarm threshold : 16.500 mA + Laser bias current high warning threshold : 16.480 mA + Laser bias current low warning threshold : 61.538 mA + Laser output power high alarm threshold : 1.2576 mW / 1.00 dBm + Laser output power low alarm threshold : 1.0321 mW / 0.14 dBm + Laser output power high warning threshold : 2.1318 mW / 3.29 dBm + Laser output power low warning threshold : 2.0530 mW / 3.12 dBm + Module temperature high alarm threshold : 0.00 degrees C / 32.00 degrees F + Module temperature low alarm threshold : 0.00 degrees C / 32.00 degrees F + Module temperature high warning threshold : 0.00 degrees C / 32.00 degrees F + Module temperature low warning threshold : 0.00 degrees C / 32.00 degrees F + Module voltage high alarm threshold : 0.2377 V + Module voltage low alarm threshold : 2.5701 V + Module voltage high warning threshold : 2.8276 V + Module voltage low warning threshold : 2.6982 V + Laser rx power high alarm threshold : 0.8224 mW / -0.85 dBm + Laser rx power low alarm threshold : 0.8224 mW / -0.85 dBm + Laser rx power high warning threshold : 0.8224 mW / -0.85 dBm + Laser rx power low warning threshold : 0.8224 mW / -0.85 dBm + +After patch: + + # ethtool -m swp13 + ... + Laser bias current high alarm threshold : 8.500 mA + Laser bias current low alarm threshold : 5.492 mA + Laser bias current high warning threshold : 8.000 mA + Laser bias current low warning threshold : 6.000 mA + Laser output power high alarm threshold : 3.4673 mW / 5.40 dBm + Laser output power low alarm threshold : 0.0724 mW / -11.40 dBm + Laser output power high warning threshold : 1.7378 mW / 2.40 dBm + Laser output power low warning threshold : 0.1445 mW / -8.40 dBm + Module temperature high alarm threshold : 80.00 degrees C / 176.00 degrees F + Module temperature low alarm threshold : -10.00 degrees C / 14.00 degrees F + Module temperature high warning threshold : 70.00 degrees C / 158.00 degrees F + Module temperature low warning threshold : 0.00 degrees C / 32.00 degrees F + Module voltage high alarm threshold : 3.5000 V + Module voltage low alarm threshold : 3.1000 V + Module voltage high warning threshold : 3.4650 V + Module voltage low warning threshold : 3.1350 V + Laser rx power high alarm threshold : 3.4673 mW / 5.40 dBm + Laser rx power low alarm threshold : 0.0467 mW / -13.31 dBm + Laser rx power high warning threshold : 1.7378 mW / 2.40 dBm + Laser rx power low warning threshold : 0.0933 mW / -10.30 dBm + +The following AddressSanitizer report is fixed: + +==44670==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x617000000320 at pc 0x00000047ad93 bp 0x7ffcb4dc0070 sp 0x7ffcb4dc0068 +READ of size 1 at 0x617000000320 thread T0 + #0 0x47ad92 in sff8636_dom_parse qsfp.c:683 + #1 0x47c5d6 in sff8636_show_dom qsfp.c:771 + #2 0x47d21f in sff8636_show_all qsfp.c:870 + #3 0x42130b in do_getmodule ethtool.c:4908 + #4 0x42a38a in main ethtool.c:6383 + #5 0x7f500bf421e1 in __libc_start_main (/lib64/libc.so.6+0x281e1) + #6 0x40258d in _start (ethtool+0x40258d) + +0x617000000320 is located 16 bytes to the right of 656-byte region [0x617000000080,0x617000000310) +allocated by thread T0 here: + #0 0x7f500c2d6527 in __interceptor_calloc (/lib64/libasan.so.6+0xab527) + #1 0x420d8c in do_getmodule ethtool.c:4859 + #2 0x42a38a in main ethtool.c:6383 + #3 0x7f500bf421e1 in __libc_start_main (/lib64/libc.so.6+0x281e1) + +SUMMARY: AddressSanitizer: heap-buffer-overflow qsfp.c:683 in sff8636_dom_parse + +Fixes: fc47fdb7c364 ("ethtool: Refactor human-readable module EEPROM output for new API") +Signed-off-by: Ido Schimmel +--- + qsfp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/qsfp.c b/qsfp.c +index 644fe148a5aa..e84226bc1554 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -867,7 +867,7 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len) + (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) || + (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) { + sff6836_show_page_zero(id); +- sff8636_show_dom(id, id + SFF8636_PAGE03H_OFFSET, eeprom_len); ++ sff8636_show_dom(id, id + 3 * 0x80, eeprom_len); + } + } + +-- +2.35.1 + diff --git a/SOURCES/0002-cmis-Fix-invalid-memory-access-in-IOCTL-path.patch b/SOURCES/0002-cmis-Fix-invalid-memory-access-in-IOCTL-path.patch new file mode 100644 index 0000000..4be2b97 --- /dev/null +++ b/SOURCES/0002-cmis-Fix-invalid-memory-access-in-IOCTL-path.patch @@ -0,0 +1,55 @@ +From 1c14a6d8ebad07bc6ff090164ca15ab7656e7167 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 14 Sep 2021 14:27:35 +0300 +Subject: [PATCH 02/35] cmis: Fix invalid memory access in IOCTL path + +Page 01h is an optional page that is not available for flat memory +modules. Trying to blindly access it results in the following report +from AddressSanitizer [1]. + +Instead, pass the base address of the Lower Memory. This results in +wrong information being parsed, but this never worked correctly since +CMIS support first appeared in cited commit. + +The information will be parsed correctly in a follow-up submission that +reworks the EEPROM parsing code to use a memory map with pointers to +individual pages instead of passing one large buffer. + +[1] +==968785==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6120000001d4 at pc 0x0000004806ee bp 0x7ffefbc977a0 sp 0x7ffefbc97798 +READ of size 1 at 0x6120000001d4 thread T0 + #0 0x4806ed in cmis_print_smf_cbl_len cmis.c:127 + #1 0x48113e in cmis_show_link_len_from_page cmis.c:279 + #2 0x4811e3 in cmis_show_link_len cmis.c:300 + #3 0x481358 in qsfp_dd_show_all cmis.c:336 + #4 0x47d190 in sff8636_show_all qsfp.c:861 + #5 0x42130b in do_getmodule ethtool.c:4908 + #6 0x42a38a in main ethtool.c:6383 + #7 0x7f11db6c51e1 in __libc_start_main (/lib64/libc.so.6+0x281e1) + #8 0x40258d in _start (ethtool+0x40258d) + +Address 0x6120000001d4 is a wild pointer. +SUMMARY: AddressSanitizer: heap-buffer-overflow cmis.c:127 in cmis_print_smf_cbl_len + +Fixes: 88ca347ef35a ("Add QSFP-DD support"). +Signed-off-by: Ido Schimmel +--- + cmis.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/cmis.c b/cmis.c +index 361b721f332f..1a91e798e4b8 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -297,7 +297,7 @@ static void cmis_show_link_len_from_page(const __u8 *page_one_data) + */ + static void cmis_show_link_len(const __u8 *id) + { +- cmis_show_link_len_from_page(id + PAG01H_UPPER_OFFSET); ++ cmis_show_link_len_from_page(id); + } + + /** +-- +2.35.1 + diff --git a/SOURCES/0003-netlink-eeprom-Fallback-to-IOCTL-when-a-complete-hex.patch b/SOURCES/0003-netlink-eeprom-Fallback-to-IOCTL-when-a-complete-hex.patch new file mode 100644 index 0000000..1122e32 --- /dev/null +++ b/SOURCES/0003-netlink-eeprom-Fallback-to-IOCTL-when-a-complete-hex.patch @@ -0,0 +1,87 @@ +From 04d36d7c373db7069554a6d21ece628e2cf6b21c Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 14 Sep 2021 14:27:36 +0300 +Subject: [PATCH 03/35] netlink: eeprom: Fallback to IOCTL when a complete + hex/raw dump is requested + +The IOCTL backend provides a complete hex/raw dump of the module EEPROM +contents: + + # ethtool -m swp11 hex on | wc -l + 34 + + # ethtool -m swp11 raw on | wc -c + 512 + +With the netlink backend, only the first 128 bytes from I2C address 0x50 +are dumped: + + # ethtool -m swp11 hex on | wc -l + 10 + + # ethtool -m swp11 raw on | wc -c + 128 + +The presence of optional / banked pages is unknown without parsing the +EEPROM contents which is unavailable when pretty printing is disabled +(i.e., configure --disable-pretty-dump). With the IOCTL backend, this +parsing happens inside the kernel. + +Therefore, when a complete hex/raw dump is requested, fallback to the +IOCTL backend. + +After the patch: + + # ethtool -m swp11 hex on | wc -l + 34 + + # ethtool -m swp11 raw on | wc -c + 512 + +This avoids breaking users that are relying on current behavior. + +If users want a hex/raw dump of optional/banked pages that are not +returned with the IOCTL backend, they will be required to request these +explicitly via the netlink backend. For example: + + # ethtool -m swp11 hex on page 0x2 + +This is desirable as that way there is no ambiguity regarding the +location of optional/banked pages in the dump. + +Another way to implement the above would be to use the 'nlchk' callback +added in commit 67a9ef551661 ("ethtool: add nlchk for redirecting to +netlink"). However, it is called before the netlink instance is +initialized and before the command line parameters are parsed via +nl_parser(). + +Fixes: 25b64c66f58d ("ethtool: Add netlink handler for getmodule (-m)") +Signed-off-by: Ido Schimmel +--- + netlink/module-eeprom.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index 38e7d2cd6cf3..e9a122df3259 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -365,6 +365,16 @@ int nl_getmodule(struct cmd_context *ctx) + return -EINVAL; + } + ++ /* When complete hex/raw dump of the EEPROM is requested, fallback to ++ * ioctl. Netlink can only request specific pages. ++ */ ++ if ((getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) && ++ !getmodule_cmd_params.page && !getmodule_cmd_params.bank && ++ !getmodule_cmd_params.i2c_address) { ++ nlctx->ioctl_fallback = true; ++ return -EOPNOTSUPP; ++ } ++ + request.i2c_address = ETH_I2C_ADDRESS_LOW; + request.length = 128; + ret = page_fetch(nlctx, &request); +-- +2.35.1 + diff --git a/SOURCES/0004-ethtool-Fix-compilation-warning-when-pretty-dump-is-.patch b/SOURCES/0004-ethtool-Fix-compilation-warning-when-pretty-dump-is-.patch new file mode 100644 index 0000000..050bd91 --- /dev/null +++ b/SOURCES/0004-ethtool-Fix-compilation-warning-when-pretty-dump-is-.patch @@ -0,0 +1,84 @@ +From 3960c91ade7b1ca9979eec5200a90dc11f339cfd Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 14 Sep 2021 14:27:37 +0300 +Subject: [PATCH 04/35] ethtool: Fix compilation warning when pretty dump is + disabled +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When pretty dump is disabled (i.e., configure --disable-pretty-dump), +gcc 11.2.1 emits the following warning: + +ethtool.c: In function ‘dump_regs’: +ethtool.c:1160:31: warning: comparison is always false due to limited range of data type [-Wtype-limits] + 1160 | for (i = 0; i < ARRAY_SIZE(driver_list); i++) + | ^ + +Fix it by avoiding iterating over 'driver_list' when pretty dump is +disabled. + +Signed-off-by: Ido Schimmel +--- + ethtool.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/ethtool.c b/ethtool.c +index 33a0a492cb15..1b79e9f8d958 100644 +--- a/ethtool.c ++++ b/ethtool.c +@@ -1089,12 +1089,12 @@ static int parse_hkey(char **rss_hkey, u32 key_size, + return 0; + } + ++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP + static const struct { + const char *name; + int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs); + + } driver_list[] = { +-#ifdef ETHTOOL_ENABLE_PRETTY_DUMP + { "8139cp", realtek_dump_regs }, + { "8139too", realtek_dump_regs }, + { "r8169", realtek_dump_regs }, +@@ -1129,8 +1129,8 @@ static const struct { + { "fec", fec_dump_regs }, + { "igc", igc_dump_regs }, + { "bnxt_en", bnxt_dump_regs }, +-#endif + }; ++#endif + + void dump_hex(FILE *file, const u8 *data, int len, int offset) + { +@@ -1149,14 +1149,15 @@ void dump_hex(FILE *file, const u8 *data, int len, int offset) + static int dump_regs(int gregs_dump_raw, int gregs_dump_hex, + struct ethtool_drvinfo *info, struct ethtool_regs *regs) + { +- unsigned int i; +- + if (gregs_dump_raw) { + fwrite(regs->data, regs->len, 1, stdout); + goto nested; + } + +- if (!gregs_dump_hex) ++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP ++ if (!gregs_dump_hex) { ++ unsigned int i; ++ + for (i = 0; i < ARRAY_SIZE(driver_list); i++) + if (!strncmp(driver_list[i].name, info->driver, + ETHTOOL_BUSINFO_LEN)) { +@@ -1168,6 +1169,8 @@ static int dump_regs(int gregs_dump_raw, int gregs_dump_hex, + */ + break; + } ++ } ++#endif + + dump_hex(stdout, regs->data, regs->len, 0); + +-- +2.35.1 + diff --git a/SOURCES/0005-netlink-eeprom-Fix-compilation-when-pretty-dump-is-d.patch b/SOURCES/0005-netlink-eeprom-Fix-compilation-when-pretty-dump-is-d.patch new file mode 100644 index 0000000..e5f1b9a --- /dev/null +++ b/SOURCES/0005-netlink-eeprom-Fix-compilation-when-pretty-dump-is-d.patch @@ -0,0 +1,62 @@ +From 08d9f72f5e4ce12e2cc1fc47d1ffde9aa1326c8c Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 14 Sep 2021 14:27:38 +0300 +Subject: [PATCH 05/35] netlink: eeprom: Fix compilation when pretty dump is + disabled + +When pretty dump is disabled (i.e., configure --disable-pretty-dump), +the following errors are emitted: + +/usr/bin/ld: netlink/module-eeprom.o: in function `decoder_print': +netlink/module-eeprom.c:330: undefined reference to `sff8636_show_all_paged' +netlink/module-eeprom.c:334: undefined reference to `cmis_show_all' +netlink/module-eeprom.c:325: undefined reference to `sff8079_show_all' + +The else clause is unreachable when pretty dump is disabled, so wrap it +with ifdef directive. + +This will be re-worked in future patches where the netlink code only +queries the SFF-8024 Identifier Value and defers page requests to +individual parsers. + +Signed-off-by: Ido Schimmel +--- + netlink/module-eeprom.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index e9a122df3259..48cd2cc55bee 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -275,6 +275,7 @@ static int page_fetch(struct nl_context *nlctx, const struct ethtool_module_eepr + return nlsock_process_reply(nlsock, nomsg_reply_cb, NULL); + } + ++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP + static int decoder_prefetch(struct nl_context *nlctx) + { + struct ethtool_module_eeprom *page_zero_lower = cache_get(0, 0, ETH_I2C_ADDRESS_LOW); +@@ -338,6 +339,7 @@ static void decoder_print(void) + break; + } + } ++#endif + + int nl_getmodule(struct cmd_context *ctx) + { +@@ -414,10 +416,12 @@ int nl_getmodule(struct cmd_context *ctx) + else + dump_hex(stdout, eeprom_data, dump_length, request.offset); + } else { ++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP + ret = decoder_prefetch(nlctx); + if (ret) + goto cleanup; + decoder_print(); ++#endif + } + + cleanup: +-- +2.35.1 + diff --git a/SOURCES/0006-cmis-Fix-CLEI-code-parsing.patch b/SOURCES/0006-cmis-Fix-CLEI-code-parsing.patch new file mode 100644 index 0000000..826fc0f --- /dev/null +++ b/SOURCES/0006-cmis-Fix-CLEI-code-parsing.patch @@ -0,0 +1,66 @@ +From 5b46ca06c9888c663a74bdd804b0ecb7199cfb62 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Fri, 1 Oct 2021 18:06:21 +0300 +Subject: [PATCH 06/35] cmis: Fix CLEI code parsing + +In CMIS, unlike SFF-8636, there is no presence indication for the CLEI +code (Common Language Equipment Identification) field. The field is +always present, but might not be supported. In which case, "a value of +all ASCII 20h (spaces) shall be entered". + +Therefore, remove the erroneous check which seems to be influenced from +SFF-8636 and only print the string if it is supported and has a non-zero +length. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 8 +++++--- + cmis.h | 4 ++-- + 2 files changed, 7 insertions(+), 5 deletions(-) + +diff --git a/cmis.c b/cmis.c +index 1a91e798e4b8..499355d0e024 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -307,6 +307,8 @@ static void cmis_show_link_len(const __u8 *id) + */ + static void cmis_show_vendor_info(const __u8 *id) + { ++ const char *clei = (const char *)(id + CMIS_CLEI_START_OFFSET); ++ + sff_show_ascii(id, CMIS_VENDOR_NAME_START_OFFSET, + CMIS_VENDOR_NAME_END_OFFSET, "Vendor name"); + cmis_show_oui(id); +@@ -319,9 +321,9 @@ static void cmis_show_vendor_info(const __u8 *id) + sff_show_ascii(id, CMIS_DATE_YEAR_OFFSET, + CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code"); + +- if (id[CMIS_CLEI_PRESENT_BYTE] & CMIS_CLEI_PRESENT_MASK) +- sff_show_ascii(id, CMIS_CLEI_START_OFFSET, +- CMIS_CLEI_END_OFFSET, "CLEI code"); ++ if (*clei && strncmp(clei, CMIS_CLEI_BLANK, CMIS_CLEI_LEN)) ++ sff_show_ascii(id, CMIS_CLEI_START_OFFSET, CMIS_CLEI_END_OFFSET, ++ "CLEI code"); + } + + void qsfp_dd_show_all(const __u8 *id) +diff --git a/cmis.h b/cmis.h +index 78ee1495bc33..cfac08f42904 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -34,10 +34,10 @@ + #define CMIS_DATE_VENDOR_LOT_OFFSET 0xBC + + /* CLEI Code (Page 0) */ +-#define CMIS_CLEI_PRESENT_BYTE 0x02 +-#define CMIS_CLEI_PRESENT_MASK 0x20 + #define CMIS_CLEI_START_OFFSET 0xBE + #define CMIS_CLEI_END_OFFSET 0xC7 ++#define CMIS_CLEI_BLANK " " ++#define CMIS_CLEI_LEN 0x0A + + /* Cable assembly length */ + #define CMIS_CBL_ASM_LEN_OFFSET 0xCA +-- +2.35.1 + diff --git a/SOURCES/0007-cmis-Fix-wrong-define-name.patch b/SOURCES/0007-cmis-Fix-wrong-define-name.patch new file mode 100644 index 0000000..cbd337d --- /dev/null +++ b/SOURCES/0007-cmis-Fix-wrong-define-name.patch @@ -0,0 +1,42 @@ +From a934091a0b42cd7c71c9e71a235e57af718c9952 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Fri, 1 Oct 2021 18:06:22 +0300 +Subject: [PATCH 07/35] cmis: Fix wrong define name + +Offset 0x10 in the Lower Memory stores the "VccMonVoltage". + +Signed-off-by: Ido Schimmel +--- + cmis.c | 2 +- + cmis.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/cmis.c b/cmis.c +index 499355d0e024..408db6f26c3b 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -271,7 +271,7 @@ static void cmis_show_mod_lvl_monitors(const __u8 *id) + PRINT_TEMP("Module temperature", + OFFSET_TO_TEMP(CMIS_CURR_TEMP_OFFSET)); + PRINT_VCC("Module voltage", +- OFFSET_TO_U16(CMIS_CURR_CURR_OFFSET)); ++ OFFSET_TO_U16(CMIS_CURR_VCC_OFFSET)); + } + + static void cmis_show_link_len_from_page(const __u8 *page_one_data) +diff --git a/cmis.h b/cmis.h +index cfac08f42904..e3012ccfdd79 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -11,7 +11,7 @@ + + /* Module-Level Monitors (Page 0) */ + #define CMIS_CURR_TEMP_OFFSET 0x0E +-#define CMIS_CURR_CURR_OFFSET 0x10 ++#define CMIS_CURR_VCC_OFFSET 0x10 + + #define CMIS_CTOR_OFFSET 0xCB + +-- +2.35.1 + diff --git a/SOURCES/0008-cmis-Correct-comment.patch b/SOURCES/0008-cmis-Correct-comment.patch new file mode 100644 index 0000000..8f163be --- /dev/null +++ b/SOURCES/0008-cmis-Correct-comment.patch @@ -0,0 +1,29 @@ +From 030ba06f12761d13722a12773bb8063748242b50 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Fri, 1 Oct 2021 18:06:23 +0300 +Subject: [PATCH 08/35] cmis: Correct comment + +The file is concerned with CMIS support, not QSFP-DD which is the +physical form factor. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/cmis.c b/cmis.c +index 408db6f26c3b..591cc72953b7 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -1,7 +1,7 @@ + /** + * Description: + * +- * This module adds QSFP-DD support to ethtool. The changes are similar to ++ * This module adds CMIS support to ethtool. The changes are similar to + * the ones already existing in qsfp.c, but customized to use the memory + * addresses and logic as defined in the specification's document. + * +-- +2.35.1 + diff --git a/SOURCES/0009-sff-8636-Remove-incorrect-comment.patch b/SOURCES/0009-sff-8636-Remove-incorrect-comment.patch new file mode 100644 index 0000000..9e6145c --- /dev/null +++ b/SOURCES/0009-sff-8636-Remove-incorrect-comment.patch @@ -0,0 +1,30 @@ +From f1b4bafbb30e77fedb10a23d1b70119b9059f684 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Fri, 1 Oct 2021 18:06:24 +0300 +Subject: [PATCH 09/35] sff-8636: Remove incorrect comment + +The comment was copied from SFF-8472 (i.e., sfpdiag.c) where the +diagnostic page is at I2C address 0x51. SFF-8636 only uses I2C address +0x50. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/qsfp.c b/qsfp.c +index e84226bc1554..263cf188377d 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -64,7 +64,7 @@ + + static struct sff8636_aw_flags { + const char *str; /* Human-readable string, null at the end */ +- int offset; /* A2-relative address offset */ ++ int offset; + __u8 value; /* Alarm is on if (offset & value) != 0. */ + } sff8636_aw_flags[] = { + { "Laser bias current high alarm (Chan 1)", +-- +2.35.1 + diff --git a/SOURCES/0010-sff-8636-Fix-incorrect-function-name.patch b/SOURCES/0010-sff-8636-Fix-incorrect-function-name.patch new file mode 100644 index 0000000..d97a379 --- /dev/null +++ b/SOURCES/0010-sff-8636-Fix-incorrect-function-name.patch @@ -0,0 +1,48 @@ +From dc4a75242e8674da02a579e6c875ad4ac77a8c20 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Fri, 1 Oct 2021 18:06:25 +0300 +Subject: [PATCH 10/35] sff-8636: Fix incorrect function name + +The specification is called SFF-8636, not SFF-6836. + +Rename the function accordingly. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/qsfp.c b/qsfp.c +index 263cf188377d..3401db84352d 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -820,7 +820,7 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro + } + + +-static void sff6836_show_page_zero(const __u8 *id) ++static void sff8636_show_page_zero(const __u8 *id) + { + sff8636_show_ext_identifier(id); + sff8636_show_connector(id); +@@ -866,7 +866,7 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len) + if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) || + (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) || + (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) { +- sff6836_show_page_zero(id); ++ sff8636_show_page_zero(id); + sff8636_show_dom(id, id + 3 * 0x80, eeprom_len); + } + } +@@ -875,7 +875,7 @@ void sff8636_show_all_paged(const struct ethtool_module_eeprom *page_zero, + const struct ethtool_module_eeprom *page_three) + { + sff8636_show_identifier(page_zero->data); +- sff6836_show_page_zero(page_zero->data); ++ sff8636_show_page_zero(page_zero->data); + if (page_three) + sff8636_show_dom(page_zero->data, page_three->data - 0x80, + ETH_MODULE_SFF_8636_MAX_LEN); +-- +2.35.1 + diff --git a/SOURCES/0011-sff-8636-Convert-if-statement-to-switch-case.patch b/SOURCES/0011-sff-8636-Convert-if-statement-to-switch-case.patch new file mode 100644 index 0000000..6bad605 --- /dev/null +++ b/SOURCES/0011-sff-8636-Convert-if-statement-to-switch-case.patch @@ -0,0 +1,37 @@ +From 9d6316a62b6ab24dfd3cb800841df7fbbdc648ae Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Fri, 1 Oct 2021 18:06:26 +0300 +Subject: [PATCH 11/35] sff-8636: Convert if statement to switch-case + +The indentation is wrong and the statement can be more clearly +represented using a switch-case statement. Convert it. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/qsfp.c b/qsfp.c +index 3401db84352d..d1464cb50fdc 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -863,11 +863,13 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len) + } + + sff8636_show_identifier(id); +- if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) || +- (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) || +- (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) { ++ switch (id[SFF8636_ID_OFFSET]) { ++ case SFF8024_ID_QSFP: ++ case SFF8024_ID_QSFP_PLUS: ++ case SFF8024_ID_QSFP28: + sff8636_show_page_zero(id); + sff8636_show_dom(id, id + 3 * 0x80, eeprom_len); ++ break; + } + } + +-- +2.35.1 + diff --git a/SOURCES/0012-sff-8636-Remove-extra-blank-lines.patch b/SOURCES/0012-sff-8636-Remove-extra-blank-lines.patch new file mode 100644 index 0000000..e52f6a7 --- /dev/null +++ b/SOURCES/0012-sff-8636-Remove-extra-blank-lines.patch @@ -0,0 +1,35 @@ +From 539a255fab8401a197e2a98c0c3dd39800ca65e5 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Fri, 1 Oct 2021 18:06:27 +0300 +Subject: [PATCH 12/35] sff-8636: Remove extra blank lines + +Not needed, so remove them. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/qsfp.c b/qsfp.c +index d1464cb50fdc..3f37f1036e96 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -738,7 +738,6 @@ static void sff8636_dom_parse(const __u8 *id, const __u8 *page_three, struct sff + sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset); + sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset); + } +- + } + + static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eeprom_len) +@@ -819,7 +818,6 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro + } + } + +- + static void sff8636_show_page_zero(const __u8 *id) + { + sff8636_show_ext_identifier(id); +-- +2.35.1 + diff --git a/SOURCES/0013-cmis-Rename-CMIS-parsing-functions.patch b/SOURCES/0013-cmis-Rename-CMIS-parsing-functions.patch new file mode 100644 index 0000000..6974ac8 --- /dev/null +++ b/SOURCES/0013-cmis-Rename-CMIS-parsing-functions.patch @@ -0,0 +1,95 @@ +From 56c6dc7ab5f9170c6d399c12a87bbdb4c8de8958 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:12 +0300 +Subject: [PATCH 13/35] cmis: Rename CMIS parsing functions + +Currently, there are two CMIS parsing functions. qsfp_dd_show_all() and +cmis_show_all(). The former is called from the IOCTL path with a buffer +containing EEPROM contents and the latter is called from the netlink +path with pointer to individual EEPROM pages. + +Rename them with '_ioctl' and '_nl' suffixes to make the distinction +clear. + +In subsequent patches, these two functions will only differ in the way +they initialize the CMIS memory map for parsing, while the parsing code +itself will be shared between the two. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 6 +++--- + cmis.h | 6 +++--- + netlink/module-eeprom.c | 2 +- + qsfp.c | 2 +- + 4 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/cmis.c b/cmis.c +index 591cc72953b7..68c5b2d3277b 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -326,7 +326,7 @@ static void cmis_show_vendor_info(const __u8 *id) + "CLEI code"); + } + +-void qsfp_dd_show_all(const __u8 *id) ++void cmis_show_all_ioctl(const __u8 *id) + { + cmis_show_identifier(id); + cmis_show_power_info(id); +@@ -340,8 +340,8 @@ void qsfp_dd_show_all(const __u8 *id) + cmis_show_rev_compliance(id); + } + +-void cmis_show_all(const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_one) ++void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero, ++ const struct ethtool_module_eeprom *page_one) + { + const __u8 *page_zero_data = page_zero->data; + +diff --git a/cmis.h b/cmis.h +index e3012ccfdd79..734b90f4ddb4 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -120,9 +120,9 @@ + #define YESNO(x) (((x) != 0) ? "Yes" : "No") + #define ONOFF(x) (((x) != 0) ? "On" : "Off") + +-void qsfp_dd_show_all(const __u8 *id); ++void cmis_show_all_ioctl(const __u8 *id); + +-void cmis_show_all(const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_one); ++void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero, ++ const struct ethtool_module_eeprom *page_one); + + #endif /* CMIS_H__ */ +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index 48cd2cc55bee..fc4ef1a53aff 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -332,7 +332,7 @@ static void decoder_print(void) + break; + case SFF8024_ID_QSFP_DD: + case SFF8024_ID_DSFP: +- cmis_show_all(page_zero, page_one); ++ cmis_show_all_nl(page_zero, page_one); + break; + default: + dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset); +diff --git a/qsfp.c b/qsfp.c +index 3f37f1036e96..27fdd3bd1771 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -856,7 +856,7 @@ static void sff8636_show_page_zero(const __u8 *id) + void sff8636_show_all(const __u8 *id, __u32 eeprom_len) + { + if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) { +- qsfp_dd_show_all(id); ++ cmis_show_all_ioctl(id); + return; + } + +-- +2.35.1 + diff --git a/SOURCES/0014-cmis-Initialize-CMIS-memory-map.patch b/SOURCES/0014-cmis-Initialize-CMIS-memory-map.patch new file mode 100644 index 0000000..86cd1d2 --- /dev/null +++ b/SOURCES/0014-cmis-Initialize-CMIS-memory-map.patch @@ -0,0 +1,145 @@ +From 912115ebf8ca6eb76dfdadbe8881b7c348743f27 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:13 +0300 +Subject: [PATCH 14/35] cmis: Initialize CMIS memory map + +The CMIS memory map [1] consists of Lower Memory and Upper Memory. + +The content of the Lower Memory is fixed and can be addressed using an +offset between 0 and 127 (inclusive). + +The Upper Memory is variable and optional and can be addressed by +specifying a bank number, a page number and an offset between 128 and +255 (inclusive). + +Create a structure describing this memory map and initialize it with +pointers to available pages. + +In the IOCTL path, the structure holds pointers to regions of the +continuous buffer passed to user space via the 'ETHTOOL_GMODULEEEPROM' +command. + +In the netlink path, the structure holds pointers to individual pages +passed to user space via the 'MODULE_EEPROM_GET' message. + +This structure will later allow us to consolidate the IOCTL and netlink +parsing code paths and also easily support additional EEPROM pages. + +[1] CMIS Rev. 5, pag. 97, section 8.1.1, Figure 8-1 + +Signed-off-by: Ido Schimmel +--- + cmis.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + cmis.h | 2 ++ + 2 files changed, 65 insertions(+) + +diff --git a/cmis.c b/cmis.c +index 68c5b2d3277b..8a6788416a00 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -13,6 +13,15 @@ + #include "sff-common.h" + #include "cmis.h" + ++struct cmis_memory_map { ++ const __u8 *lower_memory; ++ const __u8 *upper_memory[1][2]; /* Bank, Page */ ++#define page_00h upper_memory[0x0][0x0] ++#define page_01h upper_memory[0x0][0x1] ++}; ++ ++#define CMIS_PAGE_SIZE 0x80 ++ + static void cmis_show_identifier(const __u8 *id) + { + sff8024_show_identifier(id, CMIS_ID_OFFSET); +@@ -326,8 +335,34 @@ static void cmis_show_vendor_info(const __u8 *id) + "CLEI code"); + } + ++static void cmis_memory_map_init_buf(struct cmis_memory_map *map, ++ const __u8 *id) ++{ ++ /* Lower Memory and Page 00h are always present. ++ * ++ * Offset into Upper Memory is between page size and twice the page ++ * size. Therefore, set the base address of each page to base address ++ * plus page size multiplied by the page number. ++ */ ++ map->lower_memory = id; ++ map->page_00h = id; ++ ++ /* Page 01h is only present when the module memory model is paged and ++ * not flat. ++ */ ++ if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & ++ CMIS_MEMORY_MODEL_MASK) ++ return; ++ ++ map->page_01h = id + CMIS_PAGE_SIZE; ++} ++ + void cmis_show_all_ioctl(const __u8 *id) + { ++ struct cmis_memory_map map = {}; ++ ++ cmis_memory_map_init_buf(&map, id); ++ + cmis_show_identifier(id); + cmis_show_power_info(id); + cmis_show_connector(id); +@@ -340,10 +375,38 @@ void cmis_show_all_ioctl(const __u8 *id) + cmis_show_rev_compliance(id); + } + ++static void ++cmis_memory_map_init_pages(struct cmis_memory_map *map, ++ const struct ethtool_module_eeprom *page_zero, ++ const struct ethtool_module_eeprom *page_one) ++{ ++ /* Lower Memory and Page 00h are always present. ++ * ++ * Offset into Upper Memory is between page size and twice the page ++ * size. Therefore, set the base address of each page to its base ++ * address minus page size. For Page 00h, this is the address of the ++ * Lower Memory. ++ */ ++ map->lower_memory = page_zero->data; ++ map->page_00h = page_zero->data; ++ ++ /* Page 01h is only present when the module memory model is paged and ++ * not flat. ++ */ ++ if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & ++ CMIS_MEMORY_MODEL_MASK) ++ return; ++ ++ map->page_01h = page_one->data - CMIS_PAGE_SIZE; ++} ++ + void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero, + const struct ethtool_module_eeprom *page_one) + { + const __u8 *page_zero_data = page_zero->data; ++ struct cmis_memory_map map = {}; ++ ++ cmis_memory_map_init_pages(&map, page_zero, page_one); + + cmis_show_identifier(page_zero_data); + cmis_show_power_info(page_zero_data); +diff --git a/cmis.h b/cmis.h +index 734b90f4ddb4..53cbb5f57127 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -4,6 +4,8 @@ + /* Identifier and revision compliance (Page 0) */ + #define CMIS_ID_OFFSET 0x00 + #define CMIS_REV_COMPLIANCE_OFFSET 0x01 ++#define CMIS_MEMORY_MODEL_OFFSET 0x02 ++#define CMIS_MEMORY_MODEL_MASK 0x80 + + #define CMIS_MODULE_TYPE_OFFSET 0x55 + #define CMIS_MT_MMF 0x01 +-- +2.35.1 + diff --git a/SOURCES/0015-cmis-Use-memory-map-during-parsing.patch b/SOURCES/0015-cmis-Use-memory-map-during-parsing.patch new file mode 100644 index 0000000..b4f5122 --- /dev/null +++ b/SOURCES/0015-cmis-Use-memory-map-during-parsing.patch @@ -0,0 +1,394 @@ +From 6e6aed12948d2d191660252a4be9bb33dc283bed Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:14 +0300 +Subject: [PATCH 15/35] cmis: Use memory map during parsing + +Instead of passing one large buffer to the individual parsing functions, +use the memory map structure from the previous patch. + +This has the added benefit of checking which optional pages are actually +available and it will also allow us to consolidate the IOCTL and netlink +parsing code paths. + +Tested by making sure that the only differences in output in both the +IOCTL and netlink paths before and after the patch are in a few +registers in Page 01h that were previously parsed from Page 00h. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 175 +++++++++++++++++++++++++++++---------------------------- + cmis.h | 1 - + 2 files changed, 88 insertions(+), 88 deletions(-) + +diff --git a/cmis.c b/cmis.c +index 8a6788416a00..2e01446b2315 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -22,19 +22,19 @@ struct cmis_memory_map { + + #define CMIS_PAGE_SIZE 0x80 + +-static void cmis_show_identifier(const __u8 *id) ++static void cmis_show_identifier(const struct cmis_memory_map *map) + { +- sff8024_show_identifier(id, CMIS_ID_OFFSET); ++ sff8024_show_identifier(map->lower_memory, CMIS_ID_OFFSET); + } + +-static void cmis_show_connector(const __u8 *id) ++static void cmis_show_connector(const struct cmis_memory_map *map) + { +- sff8024_show_connector(id, CMIS_CTOR_OFFSET); ++ sff8024_show_connector(map->page_00h, CMIS_CTOR_OFFSET); + } + +-static void cmis_show_oui(const __u8 *id) ++static void cmis_show_oui(const struct cmis_memory_map *map) + { +- sff8024_show_oui(id, CMIS_VENDOR_OUI_OFFSET); ++ sff8024_show_oui(map->page_00h, CMIS_VENDOR_OUI_OFFSET); + } + + /** +@@ -42,9 +42,9 @@ static void cmis_show_oui(const __u8 *id) + * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18 + * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2 + */ +-static void cmis_show_rev_compliance(const __u8 *id) ++static void cmis_show_rev_compliance(const struct cmis_memory_map *map) + { +- __u8 rev = id[CMIS_REV_COMPLIANCE_OFFSET]; ++ __u8 rev = map->lower_memory[CMIS_REV_COMPLIANCE_OFFSET]; + int major = (rev >> 4) & 0x0F; + int minor = rev & 0x0F; + +@@ -58,17 +58,17 @@ static void cmis_show_rev_compliance(const __u8 *id) + * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18 + * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1 + */ +-static void cmis_show_power_info(const __u8 *id) ++static void cmis_show_power_info(const struct cmis_memory_map *map) + { + float max_power = 0.0f; + __u8 base_power = 0; + __u8 power_class; + + /* Get the power class (first 3 most significat bytes) */ +- power_class = (id[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07; ++ power_class = (map->page_00h[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07; + + /* Get the base power in multiples of 0.25W */ +- base_power = id[CMIS_PWR_MAX_POWER_OFFSET]; ++ base_power = map->page_00h[CMIS_PWR_MAX_POWER_OFFSET]; + max_power = base_power * 0.25f; + + printf("\t%-41s : %d\n", "Power class", power_class + 1); +@@ -83,20 +83,20 @@ static void cmis_show_power_info(const __u8 *id) + * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31 + * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19 + */ +-static void cmis_show_cbl_asm_len(const __u8 *id) ++static void cmis_show_cbl_asm_len(const struct cmis_memory_map *map) + { + static const char *fn = "Cable assembly length"; + float mul = 1.0f; + float val = 0.0f; + + /* Check if max length */ +- if (id[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) { ++ if (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) { + printf("\t%-41s : > 6.3km\n", fn); + return; + } + + /* Get the multiplier from the first two bits */ +- switch (id[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) { ++ switch (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) { + case CMIS_MULTIPLIER_00: + mul = 0.1f; + break; +@@ -114,7 +114,7 @@ static void cmis_show_cbl_asm_len(const __u8 *id) + } + + /* Get base value from first 6 bits and multiply by mul */ +- val = (id[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_VAL_MASK); ++ val = (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_VAL_MASK); + val = (float)val * mul; + printf("\t%-41s : %0.2fm\n", fn, val); + } +@@ -126,14 +126,17 @@ static void cmis_show_cbl_asm_len(const __u8 *id) + * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39 + * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27 + */ +-static void cmis_print_smf_cbl_len(const __u8 *id) ++static void cmis_print_smf_cbl_len(const struct cmis_memory_map *map) + { + static const char *fn = "Length (SMF)"; + float mul = 1.0f; + float val = 0.0f; + ++ if (!map->page_01h) ++ return; ++ + /* Get the multiplier from the first two bits */ +- switch (id[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) { ++ switch (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) { + case CMIS_MULTIPLIER_00: + mul = 0.1f; + break; +@@ -145,7 +148,7 @@ static void cmis_print_smf_cbl_len(const __u8 *id) + } + + /* Get base value from first 6 bits and multiply by mul */ +- val = (id[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_VAL_MASK); ++ val = (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_VAL_MASK); + val = (float)val * mul; + printf("\t%-41s : %0.2fkm\n", fn, val); + } +@@ -155,21 +158,24 @@ static void cmis_print_smf_cbl_len(const __u8 *id) + * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46 + * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34 + */ +-static void cmis_show_sig_integrity(const __u8 *id) ++static void cmis_show_sig_integrity(const struct cmis_memory_map *map) + { ++ if (!map->page_01h) ++ return; ++ + /* CDR Bypass control: 2nd bit from each byte */ + printf("\t%-41s : ", "Tx CDR bypass control"); +- printf("%s\n", YESNO(id[CMIS_SIG_INTEG_TX_OFFSET] & 0x02)); ++ printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x02)); + + printf("\t%-41s : ", "Rx CDR bypass control"); +- printf("%s\n", YESNO(id[CMIS_SIG_INTEG_RX_OFFSET] & 0x02)); ++ printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x02)); + + /* CDR Implementation: 1st bit from each byte */ + printf("\t%-41s : ", "Tx CDR"); +- printf("%s\n", YESNO(id[CMIS_SIG_INTEG_TX_OFFSET] & 0x01)); ++ printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x01)); + + printf("\t%-41s : ", "Rx CDR"); +- printf("%s\n", YESNO(id[CMIS_SIG_INTEG_RX_OFFSET] & 0x01)); ++ printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x01)); + } + + /** +@@ -182,14 +188,14 @@ static void cmis_show_sig_integrity(const __u8 *id) + * --> pag. 98, section 8.4, Table 8-25 + * --> page 100, section 8.4.3, 8.4.4 + */ +-static void cmis_show_mit_compliance(const __u8 *id) ++static void cmis_show_mit_compliance(const struct cmis_memory_map *map) + { + static const char *cc = " (Copper cable,"; + + printf("\t%-41s : 0x%02x", "Transmitter technology", +- id[CMIS_MEDIA_INTF_TECH_OFFSET]); ++ map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]); + +- switch (id[CMIS_MEDIA_INTF_TECH_OFFSET]) { ++ switch (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]) { + case CMIS_850_VCSEL: + printf(" (850 nm VCSEL)\n"); + break; +@@ -240,22 +246,22 @@ static void cmis_show_mit_compliance(const __u8 *id) + break; + } + +- if (id[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) { ++ if (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) { + printf("\t%-41s : %udb\n", "Attenuation at 5GHz", +- id[CMIS_COPPER_ATT_5GHZ]); ++ map->page_00h[CMIS_COPPER_ATT_5GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 7GHz", +- id[CMIS_COPPER_ATT_7GHZ]); ++ map->page_00h[CMIS_COPPER_ATT_7GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz", +- id[CMIS_COPPER_ATT_12P9GHZ]); ++ map->page_00h[CMIS_COPPER_ATT_12P9GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz", +- id[CMIS_COPPER_ATT_25P8GHZ]); +- } else { ++ map->page_00h[CMIS_COPPER_ATT_25P8GHZ]); ++ } else if (map->page_01h) { + printf("\t%-41s : %.3lfnm\n", "Laser wavelength", +- (((id[CMIS_NOM_WAVELENGTH_MSB] << 8) | +- id[CMIS_NOM_WAVELENGTH_LSB]) * 0.05)); ++ (((map->page_01h[CMIS_NOM_WAVELENGTH_MSB] << 8) | ++ map->page_01h[CMIS_NOM_WAVELENGTH_LSB]) * 0.05)); + printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance", +- (((id[CMIS_WAVELENGTH_TOL_MSB] << 8) | +- id[CMIS_WAVELENGTH_TOL_LSB]) * 0.005)); ++ (((map->page_01h[CMIS_WAVELENGTH_TOL_MSB] << 8) | ++ map->page_01h[CMIS_WAVELENGTH_TOL_LSB]) * 0.005)); + } + } + +@@ -275,28 +281,16 @@ static void cmis_show_mit_compliance(const __u8 *id) + * [2] CMIS Rev. 4: + * --> pag. 84, section 8.2.4, Table 8-6 + */ +-static void cmis_show_mod_lvl_monitors(const __u8 *id) ++static void cmis_show_mod_lvl_monitors(const struct cmis_memory_map *map) + { ++ const __u8 *id = map->lower_memory; ++ + PRINT_TEMP("Module temperature", + OFFSET_TO_TEMP(CMIS_CURR_TEMP_OFFSET)); + PRINT_VCC("Module voltage", + OFFSET_TO_U16(CMIS_CURR_VCC_OFFSET)); + } + +-static void cmis_show_link_len_from_page(const __u8 *page_one_data) +-{ +- cmis_print_smf_cbl_len(page_one_data); +- sff_show_value_with_unit(page_one_data, CMIS_OM5_LEN_OFFSET, +- "Length (OM5)", 2, "m"); +- sff_show_value_with_unit(page_one_data, CMIS_OM4_LEN_OFFSET, +- "Length (OM4)", 2, "m"); +- sff_show_value_with_unit(page_one_data, CMIS_OM3_LEN_OFFSET, +- "Length (OM3 50/125um)", 2, "m"); +- sff_show_value_with_unit(page_one_data, CMIS_OM2_LEN_OFFSET, +- "Length (OM2 50/125um)", 1, "m"); +-} +- +- + /** + * Print relevant info about the maximum supported fiber media length + * for each type of fiber media at the maximum module-supported bit rate. +@@ -304,9 +298,19 @@ static void cmis_show_link_len_from_page(const __u8 *page_one_data) + * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39 + * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27 + */ +-static void cmis_show_link_len(const __u8 *id) ++static void cmis_show_link_len(const struct cmis_memory_map *map) + { +- cmis_show_link_len_from_page(id); ++ cmis_print_smf_cbl_len(map); ++ if (!map->page_01h) ++ return; ++ sff_show_value_with_unit(map->page_01h, CMIS_OM5_LEN_OFFSET, ++ "Length (OM5)", 2, "m"); ++ sff_show_value_with_unit(map->page_01h, CMIS_OM4_LEN_OFFSET, ++ "Length (OM4)", 2, "m"); ++ sff_show_value_with_unit(map->page_01h, CMIS_OM3_LEN_OFFSET, ++ "Length (OM3 50/125um)", 2, "m"); ++ sff_show_value_with_unit(map->page_01h, CMIS_OM2_LEN_OFFSET, ++ "Length (OM2 50/125um)", 1, "m"); + } + + /** +@@ -314,25 +318,26 @@ static void cmis_show_link_len(const __u8 *id) + * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27 + * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15 + */ +-static void cmis_show_vendor_info(const __u8 *id) ++static void cmis_show_vendor_info(const struct cmis_memory_map *map) + { +- const char *clei = (const char *)(id + CMIS_CLEI_START_OFFSET); ++ const char *clei; + +- sff_show_ascii(id, CMIS_VENDOR_NAME_START_OFFSET, ++ sff_show_ascii(map->page_00h, CMIS_VENDOR_NAME_START_OFFSET, + CMIS_VENDOR_NAME_END_OFFSET, "Vendor name"); +- cmis_show_oui(id); +- sff_show_ascii(id, CMIS_VENDOR_PN_START_OFFSET, ++ cmis_show_oui(map); ++ sff_show_ascii(map->page_00h, CMIS_VENDOR_PN_START_OFFSET, + CMIS_VENDOR_PN_END_OFFSET, "Vendor PN"); +- sff_show_ascii(id, CMIS_VENDOR_REV_START_OFFSET, ++ sff_show_ascii(map->page_00h, CMIS_VENDOR_REV_START_OFFSET, + CMIS_VENDOR_REV_END_OFFSET, "Vendor rev"); +- sff_show_ascii(id, CMIS_VENDOR_SN_START_OFFSET, ++ sff_show_ascii(map->page_00h, CMIS_VENDOR_SN_START_OFFSET, + CMIS_VENDOR_SN_END_OFFSET, "Vendor SN"); +- sff_show_ascii(id, CMIS_DATE_YEAR_OFFSET, ++ sff_show_ascii(map->page_00h, CMIS_DATE_YEAR_OFFSET, + CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code"); + ++ clei = (const char *)(map->page_00h + CMIS_CLEI_START_OFFSET); + if (*clei && strncmp(clei, CMIS_CLEI_BLANK, CMIS_CLEI_LEN)) +- sff_show_ascii(id, CMIS_CLEI_START_OFFSET, CMIS_CLEI_END_OFFSET, +- "CLEI code"); ++ sff_show_ascii(map->page_00h, CMIS_CLEI_START_OFFSET, ++ CMIS_CLEI_END_OFFSET, "CLEI code"); + } + + static void cmis_memory_map_init_buf(struct cmis_memory_map *map, +@@ -363,16 +368,16 @@ void cmis_show_all_ioctl(const __u8 *id) + + cmis_memory_map_init_buf(&map, id); + +- cmis_show_identifier(id); +- cmis_show_power_info(id); +- cmis_show_connector(id); +- cmis_show_cbl_asm_len(id); +- cmis_show_sig_integrity(id); +- cmis_show_mit_compliance(id); +- cmis_show_mod_lvl_monitors(id); +- cmis_show_link_len(id); +- cmis_show_vendor_info(id); +- cmis_show_rev_compliance(id); ++ cmis_show_identifier(&map); ++ cmis_show_power_info(&map); ++ cmis_show_connector(&map); ++ cmis_show_cbl_asm_len(&map); ++ cmis_show_sig_integrity(&map); ++ cmis_show_mit_compliance(&map); ++ cmis_show_mod_lvl_monitors(&map); ++ cmis_show_link_len(&map); ++ cmis_show_vendor_info(&map); ++ cmis_show_rev_compliance(&map); + } + + static void +@@ -403,22 +408,18 @@ cmis_memory_map_init_pages(struct cmis_memory_map *map, + void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero, + const struct ethtool_module_eeprom *page_one) + { +- const __u8 *page_zero_data = page_zero->data; + struct cmis_memory_map map = {}; + + cmis_memory_map_init_pages(&map, page_zero, page_one); + +- cmis_show_identifier(page_zero_data); +- cmis_show_power_info(page_zero_data); +- cmis_show_connector(page_zero_data); +- cmis_show_cbl_asm_len(page_zero_data); +- cmis_show_sig_integrity(page_zero_data); +- cmis_show_mit_compliance(page_zero_data); +- cmis_show_mod_lvl_monitors(page_zero_data); +- +- if (page_one) +- cmis_show_link_len_from_page(page_one->data - 0x80); +- +- cmis_show_vendor_info(page_zero_data); +- cmis_show_rev_compliance(page_zero_data); ++ cmis_show_identifier(&map); ++ cmis_show_power_info(&map); ++ cmis_show_connector(&map); ++ cmis_show_cbl_asm_len(&map); ++ cmis_show_sig_integrity(&map); ++ cmis_show_mit_compliance(&map); ++ cmis_show_mod_lvl_monitors(&map); ++ cmis_show_link_len(&map); ++ cmis_show_vendor_info(&map); ++ cmis_show_rev_compliance(&map); + } +diff --git a/cmis.h b/cmis.h +index 53cbb5f57127..c878e3bc5afd 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -100,7 +100,6 @@ + * that are unique to active modules and cable assemblies. + * GlobalOffset = 2 * 0x80 + LocalOffset + */ +-#define PAG01H_UPPER_OFFSET (0x02 * 0x80) + + /* Supported Link Length (Page 1) */ + #define CMIS_SMF_LEN_OFFSET 0x84 +-- +2.35.1 + diff --git a/SOURCES/0016-cmis-Consolidate-code-between-IOCTL-and-netlink-path.patch b/SOURCES/0016-cmis-Consolidate-code-between-IOCTL-and-netlink-path.patch new file mode 100644 index 0000000..98e6680 --- /dev/null +++ b/SOURCES/0016-cmis-Consolidate-code-between-IOCTL-and-netlink-path.patch @@ -0,0 +1,77 @@ +From 284886fbb4a85103cd82d061ebe4d1c93730b783 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:15 +0300 +Subject: [PATCH 16/35] cmis: Consolidate code between IOCTL and netlink paths + +Now that both the netlink and IOCTL paths use the same memory map +structure for parsing, the code can be easily consolidated. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 38 ++++++++++++++++---------------------- + 1 file changed, 16 insertions(+), 22 deletions(-) + +diff --git a/cmis.c b/cmis.c +index 2e01446b2315..eb7791dd59df 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -340,6 +340,20 @@ static void cmis_show_vendor_info(const struct cmis_memory_map *map) + CMIS_CLEI_END_OFFSET, "CLEI code"); + } + ++static void cmis_show_all_common(const struct cmis_memory_map *map) ++{ ++ cmis_show_identifier(map); ++ cmis_show_power_info(map); ++ cmis_show_connector(map); ++ cmis_show_cbl_asm_len(map); ++ cmis_show_sig_integrity(map); ++ cmis_show_mit_compliance(map); ++ cmis_show_mod_lvl_monitors(map); ++ cmis_show_link_len(map); ++ cmis_show_vendor_info(map); ++ cmis_show_rev_compliance(map); ++} ++ + static void cmis_memory_map_init_buf(struct cmis_memory_map *map, + const __u8 *id) + { +@@ -367,17 +381,7 @@ void cmis_show_all_ioctl(const __u8 *id) + struct cmis_memory_map map = {}; + + cmis_memory_map_init_buf(&map, id); +- +- cmis_show_identifier(&map); +- cmis_show_power_info(&map); +- cmis_show_connector(&map); +- cmis_show_cbl_asm_len(&map); +- cmis_show_sig_integrity(&map); +- cmis_show_mit_compliance(&map); +- cmis_show_mod_lvl_monitors(&map); +- cmis_show_link_len(&map); +- cmis_show_vendor_info(&map); +- cmis_show_rev_compliance(&map); ++ cmis_show_all_common(&map); + } + + static void +@@ -411,15 +415,5 @@ void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero, + struct cmis_memory_map map = {}; + + cmis_memory_map_init_pages(&map, page_zero, page_one); +- +- cmis_show_identifier(&map); +- cmis_show_power_info(&map); +- cmis_show_connector(&map); +- cmis_show_cbl_asm_len(&map); +- cmis_show_sig_integrity(&map); +- cmis_show_mit_compliance(&map); +- cmis_show_mod_lvl_monitors(&map); +- cmis_show_link_len(&map); +- cmis_show_vendor_info(&map); +- cmis_show_rev_compliance(&map); ++ cmis_show_all_common(&map); + } +-- +2.35.1 + diff --git a/SOURCES/0017-sff-8636-Rename-SFF-8636-parsing-functions.patch b/SOURCES/0017-sff-8636-Rename-SFF-8636-parsing-functions.patch new file mode 100644 index 0000000..ec866f5 --- /dev/null +++ b/SOURCES/0017-sff-8636-Rename-SFF-8636-parsing-functions.patch @@ -0,0 +1,97 @@ +From a8419e965891901217756254e0ed1a3351b2a3cb Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:16 +0300 +Subject: [PATCH 17/35] sff-8636: Rename SFF-8636 parsing functions + +Currently, there are two SFF-8636 parsing functions. sff8636_show_all() +and sff8636_show_all_paged(). The former is called from the IOCTL path +with a buffer containing EEPROM contents and the latter is called from +the netlink path with pointer to individual EEPROM pages. + +Rename them with '_ioctl' and '_nl' suffixes to make the distinction +clear. + +In subsequent patches, these two functions will only differ in the way +they initialize the SFF-8636 memory map for parsing, while the parsing +code itself will be shared between the two. + +Signed-off-by: Ido Schimmel +--- + ethtool.c | 4 ++-- + internal.h | 6 +++--- + netlink/module-eeprom.c | 2 +- + qsfp.c | 6 +++--- + 4 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/ethtool.c b/ethtool.c +index 1b79e9f8d958..6c744ff84eb9 100644 +--- a/ethtool.c ++++ b/ethtool.c +@@ -4908,8 +4908,8 @@ static int do_getmodule(struct cmd_context *ctx) + break; + case ETH_MODULE_SFF_8436: + case ETH_MODULE_SFF_8636: +- sff8636_show_all(eeprom->data, +- modinfo.eeprom_len); ++ sff8636_show_all_ioctl(eeprom->data, ++ modinfo.eeprom_len); + break; + #endif + default: +diff --git a/internal.h b/internal.h +index 33e619b3ac53..7ca6066d4e12 100644 +--- a/internal.h ++++ b/internal.h +@@ -390,9 +390,9 @@ void sff8079_show_all(const __u8 *id); + void sff8472_show_all(const __u8 *id); + + /* QSFP Optics diagnostics */ +-void sff8636_show_all(const __u8 *id, __u32 eeprom_len); +-void sff8636_show_all_paged(const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_three); ++void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len); ++void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero, ++ const struct ethtool_module_eeprom *page_three); + + /* FUJITSU Extended Socket network device */ + int fjes_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index fc4ef1a53aff..18b1abbe1252 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -328,7 +328,7 @@ static void decoder_print(void) + case SFF8024_ID_QSFP: + case SFF8024_ID_QSFP28: + case SFF8024_ID_QSFP_PLUS: +- sff8636_show_all_paged(page_zero, page_three); ++ sff8636_show_all_nl(page_zero, page_three); + break; + case SFF8024_ID_QSFP_DD: + case SFF8024_ID_DSFP: +diff --git a/qsfp.c b/qsfp.c +index 27fdd3bd1771..dc6407d3ef6f 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -853,7 +853,7 @@ static void sff8636_show_page_zero(const __u8 *id) + + } + +-void sff8636_show_all(const __u8 *id, __u32 eeprom_len) ++void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len) + { + if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) { + cmis_show_all_ioctl(id); +@@ -871,8 +871,8 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len) + } + } + +-void sff8636_show_all_paged(const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_three) ++void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero, ++ const struct ethtool_module_eeprom *page_three) + { + sff8636_show_identifier(page_zero->data); + sff8636_show_page_zero(page_zero->data); +-- +2.35.1 + diff --git a/SOURCES/0018-sff-8636-Initialize-SFF-8636-memory-map.patch b/SOURCES/0018-sff-8636-Initialize-SFF-8636-memory-map.patch new file mode 100644 index 0000000..36709da --- /dev/null +++ b/SOURCES/0018-sff-8636-Initialize-SFF-8636-memory-map.patch @@ -0,0 +1,137 @@ +From c77b5adfb3f94762a08554d1b4d75f6cbd8a6abe Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:17 +0300 +Subject: [PATCH 18/35] sff-8636: Initialize SFF-8636 memory map + +The SFF-8636 memory map [1] consists of Lower Memory and Upper Memory. + +The content of the Lower Memory is fixed and can be addressed using an +offset between 0 and 127 (inclusive). + +The Upper Memory is variable and optional and can be addressed by +specifying a page number and an offset between 128 and 255 (inclusive). + +Create a structure describing this memory map and initialize it with +pointers to available pages. + +In the IOCTL path, the structure holds pointers to regions of the +continuous buffer passed to user space via the 'ETHTOOL_GMODULEEEPROM' +command. + +In the netlink path, the structure holds pointers to individual pages +passed to user space via the 'MODULE_EEPROM_GET' message. + +This structure will later allow us to consolidate the IOCTL and netlink +parsing code paths and also easily support additional EEPROM pages, when +needed. + +[1] SFF-8636 Rev. 2.10a, pag. 30, section 6.1, Figure 6-1 + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 65 insertions(+) + +diff --git a/qsfp.c b/qsfp.c +index dc6407d3ef6f..80000d40f6e8 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -60,6 +60,15 @@ + #include "qsfp.h" + #include "cmis.h" + ++struct sff8636_memory_map { ++ const __u8 *lower_memory; ++ const __u8 *upper_memory[4]; ++#define page_00h upper_memory[0x0] ++#define page_03h upper_memory[0x3] ++}; ++ ++#define SFF8636_PAGE_SIZE 0x80 ++ + #define MAX_DESC_SIZE 42 + + static struct sff8636_aw_flags { +@@ -853,13 +862,40 @@ static void sff8636_show_page_zero(const __u8 *id) + + } + ++static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map, ++ const __u8 *id, __u32 eeprom_len) ++{ ++ /* Lower Memory and Page 00h are always present. ++ * ++ * Offset into Upper Memory is between page size and twice the page ++ * size. Therefore, set the base address of each page to base address ++ * plus page size multiplied by the page number. ++ */ ++ map->lower_memory = id; ++ map->page_00h = id; ++ ++ /* Page 03h is only present when the module memory model is paged and ++ * not flat and when we got a big enough buffer from the kernel. ++ */ ++ if (map->lower_memory[SFF8636_STATUS_2_OFFSET] & ++ SFF8636_STATUS_PAGE_3_PRESENT || ++ eeprom_len != ETH_MODULE_SFF_8636_MAX_LEN) ++ return; ++ ++ map->page_03h = id + 3 * SFF8636_PAGE_SIZE; ++} ++ + void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len) + { ++ struct sff8636_memory_map map = {}; ++ + if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) { + cmis_show_all_ioctl(id); + return; + } + ++ sff8636_memory_map_init_buf(&map, id, eeprom_len); ++ + sff8636_show_identifier(id); + switch (id[SFF8636_ID_OFFSET]) { + case SFF8024_ID_QSFP: +@@ -871,9 +907,38 @@ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len) + } + } + ++static void ++sff8636_memory_map_init_pages(struct sff8636_memory_map *map, ++ const struct ethtool_module_eeprom *page_zero, ++ const struct ethtool_module_eeprom *page_three) ++{ ++ /* Lower Memory and Page 00h are always present. ++ * ++ * Offset into Upper Memory is between page size and twice the page ++ * size. Therefore, set the base address of each page to its base ++ * address minus page size. For Page 00h, this is the address of the ++ * Lower Memory. ++ */ ++ map->lower_memory = page_zero->data; ++ map->page_00h = page_zero->data; ++ ++ /* Page 03h is only present when the module memory model is paged and ++ * not flat. ++ */ ++ if (map->lower_memory[SFF8636_STATUS_2_OFFSET] & ++ SFF8636_STATUS_PAGE_3_PRESENT) ++ return; ++ ++ map->page_03h = page_three->data - SFF8636_PAGE_SIZE; ++} ++ + void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero, + const struct ethtool_module_eeprom *page_three) + { ++ struct sff8636_memory_map map = {}; ++ ++ sff8636_memory_map_init_pages(&map, page_zero, page_three); ++ + sff8636_show_identifier(page_zero->data); + sff8636_show_page_zero(page_zero->data); + if (page_three) +-- +2.35.1 + diff --git a/SOURCES/0019-sff-8636-Use-memory-map-during-parsing.patch b/SOURCES/0019-sff-8636-Use-memory-map-during-parsing.patch new file mode 100644 index 0000000..158073b --- /dev/null +++ b/SOURCES/0019-sff-8636-Use-memory-map-during-parsing.patch @@ -0,0 +1,607 @@ +From 85023fa5dd7e79ce46a92ab567b4b675c3145775 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:18 +0300 +Subject: [PATCH 19/35] sff-8636: Use memory map during parsing + +Instead of passing one large buffer to the individual parsing functions, +use the memory map structure from the previous patch. + +This has the added benefit of checking which optional pages are actually +available and it will also allow us to consolidate the IOCTL and netlink +parsing code paths. + +Tested by making sure that there are no differences in output in both +the IOCTL and netlink paths before and after the patch. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 368 +++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 201 insertions(+), 167 deletions(-) + +diff --git a/qsfp.c b/qsfp.c +index 80000d40f6e8..354b3b1ce9ff 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -205,20 +205,21 @@ static struct sff8636_aw_flags { + { NULL, 0, 0 }, + }; + +-static void sff8636_show_identifier(const __u8 *id) ++static void sff8636_show_identifier(const struct sff8636_memory_map *map) + { +- sff8024_show_identifier(id, SFF8636_ID_OFFSET); ++ sff8024_show_identifier(map->lower_memory, SFF8636_ID_OFFSET); + } + +-static void sff8636_show_ext_identifier(const __u8 *id) ++static void sff8636_show_ext_identifier(const struct sff8636_memory_map *map) + { + printf("\t%-41s : 0x%02x\n", "Extended identifier", +- id[SFF8636_EXT_ID_OFFSET]); ++ map->page_00h[SFF8636_EXT_ID_OFFSET]); + + static const char *pfx = + "\tExtended identifier description :"; + +- switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_PWR_CLASS_MASK) { ++ switch (map->page_00h[SFF8636_EXT_ID_OFFSET] & ++ SFF8636_EXT_ID_PWR_CLASS_MASK) { + case SFF8636_EXT_ID_PWR_CLASS_1: + printf("%s 1.5W max. Power consumption\n", pfx); + break; +@@ -233,17 +234,18 @@ static void sff8636_show_ext_identifier(const __u8 *id) + break; + } + +- if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK) ++ if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK) + printf("%s CDR present in TX,", pfx); + else + printf("%s No CDR in TX,", pfx); + +- if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK) ++ if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK) + printf(" CDR present in RX\n"); + else + printf(" No CDR in RX\n"); + +- switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_EPWR_CLASS_MASK) { ++ switch (map->page_00h[SFF8636_EXT_ID_OFFSET] & ++ SFF8636_EXT_ID_EPWR_CLASS_MASK) { + case SFF8636_EXT_ID_PWR_CLASS_LEGACY: + printf("%s", pfx); + break; +@@ -257,18 +259,19 @@ static void sff8636_show_ext_identifier(const __u8 *id) + printf("%s 5.0W max. Power consumption, ", pfx); + break; + } +- if (id[SFF8636_PWR_MODE_OFFSET] & SFF8636_HIGH_PWR_ENABLE) ++ if (map->lower_memory[SFF8636_PWR_MODE_OFFSET] & ++ SFF8636_HIGH_PWR_ENABLE) + printf(" High Power Class (> 3.5 W) enabled\n"); + else + printf(" High Power Class (> 3.5 W) not enabled\n"); + } + +-static void sff8636_show_connector(const __u8 *id) ++static void sff8636_show_connector(const struct sff8636_memory_map *map) + { +- sff8024_show_connector(id, SFF8636_CTOR_OFFSET); ++ sff8024_show_connector(map->page_00h, SFF8636_CTOR_OFFSET); + } + +-static void sff8636_show_transceiver(const __u8 *id) ++static void sff8636_show_transceiver(const struct sff8636_memory_map *map) + { + static const char *pfx = + "\tTransceiver type :"; +@@ -276,33 +279,41 @@ static void sff8636_show_transceiver(const __u8 *id) + printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + "Transceiver codes", +- id[SFF8636_ETHERNET_COMP_OFFSET], +- id[SFF8636_SONET_COMP_OFFSET], +- id[SFF8636_SAS_COMP_OFFSET], +- id[SFF8636_GIGE_COMP_OFFSET], +- id[SFF8636_FC_LEN_OFFSET], +- id[SFF8636_FC_TECH_OFFSET], +- id[SFF8636_FC_TRANS_MEDIA_OFFSET], +- id[SFF8636_FC_SPEED_OFFSET]); ++ map->page_00h[SFF8636_ETHERNET_COMP_OFFSET], ++ map->page_00h[SFF8636_SONET_COMP_OFFSET], ++ map->page_00h[SFF8636_SAS_COMP_OFFSET], ++ map->page_00h[SFF8636_GIGE_COMP_OFFSET], ++ map->page_00h[SFF8636_FC_LEN_OFFSET], ++ map->page_00h[SFF8636_FC_TECH_OFFSET], ++ map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET], ++ map->page_00h[SFF8636_FC_SPEED_OFFSET]); + + /* 10G/40G Ethernet Compliance Codes */ +- if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LRM) ++ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & ++ SFF8636_ETHERNET_10G_LRM) + printf("%s 10G Ethernet: 10G Base-LRM\n", pfx); +- if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LR) ++ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & ++ SFF8636_ETHERNET_10G_LR) + printf("%s 10G Ethernet: 10G Base-LR\n", pfx); +- if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_SR) ++ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & ++ SFF8636_ETHERNET_10G_SR) + printf("%s 10G Ethernet: 10G Base-SR\n", pfx); +- if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_CR4) ++ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & ++ SFF8636_ETHERNET_40G_CR4) + printf("%s 40G Ethernet: 40G Base-CR4\n", pfx); +- if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_SR4) ++ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & ++ SFF8636_ETHERNET_40G_SR4) + printf("%s 40G Ethernet: 40G Base-SR4\n", pfx); +- if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_LR4) ++ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & ++ SFF8636_ETHERNET_40G_LR4) + printf("%s 40G Ethernet: 40G Base-LR4\n", pfx); +- if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_ACTIVE) ++ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & ++ SFF8636_ETHERNET_40G_ACTIVE) + printf("%s 40G Ethernet: 40G Active Cable (XLPPI)\n", pfx); + /* Extended Specification Compliance Codes from SFF-8024 */ +- if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_RSRVD) { +- switch (id[SFF8636_OPTION_1_OFFSET]) { ++ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] & ++ SFF8636_ETHERNET_RSRVD) { ++ switch (map->page_00h[SFF8636_OPTION_1_OFFSET]) { + case SFF8636_ETHERNET_UNSPECIFIED: + printf("%s (reserved or unknown)\n", pfx); + break; +@@ -493,113 +504,122 @@ static void sff8636_show_transceiver(const __u8 *id) + } + + /* SONET Compliance Codes */ +- if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_40G_OTN)) ++ if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & ++ (SFF8636_SONET_40G_OTN)) + printf("%s 40G OTN (OTU3B/OTU3C)\n", pfx); +- if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR)) ++ if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR)) + printf("%s SONET: OC-48, long reach\n", pfx); +- if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR)) ++ if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR)) + printf("%s SONET: OC-48, intermediate reach\n", pfx); +- if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR)) ++ if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR)) + printf("%s SONET: OC-48, short reach\n", pfx); + + /* SAS/SATA Compliance Codes */ +- if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G)) ++ if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G)) + printf("%s SAS 6.0G\n", pfx); +- if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G)) ++ if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G)) + printf("%s SAS 3.0G\n", pfx); + + /* Ethernet Compliance Codes */ +- if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T) ++ if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T) + printf("%s Ethernet: 1000BASE-T\n", pfx); +- if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX) ++ if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX) + printf("%s Ethernet: 1000BASE-CX\n", pfx); +- if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX) ++ if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX) + printf("%s Ethernet: 1000BASE-LX\n", pfx); +- if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX) ++ if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX) + printf("%s Ethernet: 1000BASE-SX\n", pfx); + + /* Fibre Channel link length */ +- if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG) ++ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG) + printf("%s FC: very long distance (V)\n", pfx); +- if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT) ++ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT) + printf("%s FC: short distance (S)\n", pfx); +- if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT) ++ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT) + printf("%s FC: intermediate distance (I)\n", pfx); +- if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG) ++ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG) + printf("%s FC: long distance (L)\n", pfx); +- if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED) ++ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED) + printf("%s FC: medium distance (M)\n", pfx); + + /* Fibre Channel transmitter technology */ +- if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC) ++ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC) + printf("%s FC: Longwave laser (LC)\n", pfx); +- if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER) ++ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER) + printf("%s FC: Electrical inter-enclosure (EL)\n", pfx); +- if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA) ++ if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA) + printf("%s FC: Electrical intra-enclosure (EL)\n", pfx); +- if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_WO_OFC) ++ if (map->page_00h[SFF8636_FC_TECH_OFFSET] & ++ SFF8636_FC_TECH_SHORT_WO_OFC) + printf("%s FC: Shortwave laser w/o OFC (SN)\n", pfx); +- if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC) ++ if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC) + printf("%s FC: Shortwave laser with OFC (SL)\n", pfx); +- if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL) ++ if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL) + printf("%s FC: Longwave laser (LL)\n", pfx); + + /* Fibre Channel transmission media */ +- if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TW) ++ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & ++ SFF8636_FC_TRANS_MEDIA_TW) + printf("%s FC: Twin Axial Pair (TW)\n", pfx); +- if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TP) ++ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & ++ SFF8636_FC_TRANS_MEDIA_TP) + printf("%s FC: Twisted Pair (TP)\n", pfx); +- if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_MI) ++ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & ++ SFF8636_FC_TRANS_MEDIA_MI) + printf("%s FC: Miniature Coax (MI)\n", pfx); +- if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TV) ++ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & ++ SFF8636_FC_TRANS_MEDIA_TV) + printf("%s FC: Video Coax (TV)\n", pfx); +- if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M6) ++ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & ++ SFF8636_FC_TRANS_MEDIA_M6) + printf("%s FC: Multimode, 62.5m (M6)\n", pfx); +- if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M5) ++ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & ++ SFF8636_FC_TRANS_MEDIA_M5) + printf("%s FC: Multimode, 50m (M5)\n", pfx); +- if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_OM3) ++ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & ++ SFF8636_FC_TRANS_MEDIA_OM3) + printf("%s FC: Multimode, 50um (OM3)\n", pfx); +- if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_SM) ++ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] & ++ SFF8636_FC_TRANS_MEDIA_SM) + printf("%s FC: Single Mode (SM)\n", pfx); + + /* Fibre Channel speed */ +- if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS) ++ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS) + printf("%s FC: 1200 MBytes/sec\n", pfx); +- if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS) ++ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS) + printf("%s FC: 800 MBytes/sec\n", pfx); +- if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS) ++ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS) + printf("%s FC: 1600 MBytes/sec\n", pfx); +- if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS) ++ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS) + printf("%s FC: 400 MBytes/sec\n", pfx); +- if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS) ++ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS) + printf("%s FC: 200 MBytes/sec\n", pfx); +- if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS) ++ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS) + printf("%s FC: 100 MBytes/sec\n", pfx); + } + +-static void sff8636_show_encoding(const __u8 *id) ++static void sff8636_show_encoding(const struct sff8636_memory_map *map) + { +- sff8024_show_encoding(id, SFF8636_ENCODING_OFFSET, ETH_MODULE_SFF_8636); ++ sff8024_show_encoding(map->page_00h, SFF8636_ENCODING_OFFSET, ++ ETH_MODULE_SFF_8636); + } + +-static void sff8636_show_rate_identifier(const __u8 *id) ++static void sff8636_show_rate_identifier(const struct sff8636_memory_map *map) + { + /* TODO: Need to fix rate select logic */ + printf("\t%-41s : 0x%02x\n", "Rate identifier", +- id[SFF8636_EXT_RS_OFFSET]); ++ map->page_00h[SFF8636_EXT_RS_OFFSET]); + } + +-static void sff8636_show_oui(const __u8 *id, int id_offset) +-{ +- sff8024_show_oui(id, id_offset); +-} +- +-static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id) ++static void ++sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map *map) + { + printf("\t%-41s : 0x%02x", "Transmitter technology", +- (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK)); ++ map->page_00h[SFF8636_DEVICE_TECH_OFFSET] & ++ SFF8636_TRANS_TECH_MASK); + +- switch (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK) { ++ switch (map->page_00h[SFF8636_DEVICE_TECH_OFFSET] & ++ SFF8636_TRANS_TECH_MASK) { + case SFF8636_TRANS_850_VCSEL: + printf(" (850 nm VCSEL)\n"); + break; +@@ -650,31 +670,26 @@ static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id) + break; + } + +- if ((id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK) +- >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) { ++ if ((map->page_00h[SFF8636_DEVICE_TECH_OFFSET] & ++ SFF8636_TRANS_TECH_MASK) >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) { + printf("\t%-41s : %udb\n", "Attenuation at 2.5GHz", +- id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]); ++ map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]); + printf("\t%-41s : %udb\n", "Attenuation at 5.0GHz", +- id[SFF8636_WAVELEN_LOW_BYTE_OFFSET]); ++ map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]); + printf("\t%-41s : %udb\n", "Attenuation at 7.0GHz", +- id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]); ++ map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]); + printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz", +- id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]); ++ map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]); + } else { + printf("\t%-41s : %.3lfnm\n", "Laser wavelength", +- (((id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) | +- id[SFF8636_WAVELEN_LOW_BYTE_OFFSET])*0.05)); ++ (((map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) | ++ map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]) * 0.05)); + printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance", +- (((id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) | +- id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET])*0.005)); ++ (((map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) | ++ map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]) * 0.005)); + } + } + +-static void sff8636_show_revision_compliance(const __u8 *id) +-{ +- sff_show_revision_compliance(id, SFF8636_REV_COMPLIANCE_OFFSET); +-} +- + /* + * 2-byte internal temperature conversions: + * First byte is a signed 8-bit integer, which is the temp decimal part +@@ -683,39 +698,65 @@ static void sff8636_show_revision_compliance(const __u8 *id) + #define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset)) + #define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1]) + +-static void sff8636_dom_parse(const __u8 *id, const __u8 *page_three, struct sff_diags *sd) ++static void sff8636_dom_parse(const struct sff8636_memory_map *map, ++ struct sff_diags *sd) + { ++ const __u8 *id = map->lower_memory; + int i = 0; + + /* Monitoring Thresholds for Alarms and Warnings */ + sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(id, SFF8636_VCC_CURR); +- sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_HALRM); +- sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_LALRM); +- sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_HWARN); +- sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_LWARN); +- + sd->sfp_temp[MCURR] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_CURR); +- sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_HALRM); +- sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_LALRM); +- sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_HWARN); +- sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_LWARN); +- +- sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_HALRM); +- sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_LALRM); +- sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_HWARN); +- sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_LWARN); +- +- sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_HALRM); +- sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_LALRM); +- sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_HWARN); +- sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_LWARN); +- +- sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_HALRM); +- sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_LALRM); +- sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_HWARN); +- sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_LWARN); +- + ++ if (!map->page_03h) ++ goto out; ++ ++ sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_VCC_HALRM); ++ sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_VCC_LALRM); ++ sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_VCC_HWARN); ++ sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_VCC_LWARN); ++ ++ sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TEMP_HALRM); ++ sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TEMP_LALRM); ++ sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TEMP_HWARN); ++ sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TEMP_LWARN); ++ ++ sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TX_BIAS_HALRM); ++ sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TX_BIAS_LALRM); ++ sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TX_BIAS_HWARN); ++ sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TX_BIAS_LWARN); ++ ++ sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TX_PWR_HALRM); ++ sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TX_PWR_LALRM); ++ sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TX_PWR_HWARN); ++ sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_TX_PWR_LWARN); ++ ++ sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_RX_PWR_HALRM); ++ sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_RX_PWR_LALRM); ++ sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_RX_PWR_HWARN); ++ sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h, ++ SFF8636_RX_PWR_LWARN); ++ ++out: + /* Channel Specific Data */ + for (i = 0; i < MAX_CHANNEL_NUM; i++) { + u8 rx_power_offset, tx_bias_offset; +@@ -749,7 +790,7 @@ static void sff8636_dom_parse(const __u8 *id, const __u8 *page_three, struct sff + } + } + +-static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eeprom_len) ++static void sff8636_show_dom(const struct sff8636_memory_map *map) + { + struct sff_diags sd = {0}; + char *rx_power_string = NULL; +@@ -763,20 +804,15 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro + * and thresholds + * If pagging support exists, then supports_alarms is marked as 1 + */ ++ if (map->page_03h) ++ sd.supports_alarms = 1; + +- if (eeprom_len == ETH_MODULE_SFF_8636_MAX_LEN) { +- if (!(id[SFF8636_STATUS_2_OFFSET] & +- SFF8636_STATUS_PAGE_3_PRESENT)) { +- sd.supports_alarms = 1; +- } +- } ++ sd.rx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] & ++ SFF8636_RX_PWR_TYPE_MASK; ++ sd.tx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] & ++ SFF8636_RX_PWR_TYPE_MASK; + +- sd.rx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] & +- SFF8636_RX_PWR_TYPE_MASK; +- sd.tx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] & +- SFF8636_RX_PWR_TYPE_MASK; +- +- sff8636_dom_parse(id, page_three, &sd); ++ sff8636_dom_parse(map, &sd); + + PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]); + PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]); +@@ -819,7 +855,7 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro + if (sd.supports_alarms) { + for (i = 0; sff8636_aw_flags[i].str; ++i) { + printf("\t%-41s : %s\n", sff8636_aw_flags[i].str, +- id[sff8636_aw_flags[i].offset] ++ map->lower_memory[sff8636_aw_flags[i].offset] + & sff8636_aw_flags[i].value ? "On" : "Off"); + } + +@@ -827,39 +863,39 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro + } + } + +-static void sff8636_show_page_zero(const __u8 *id) ++static void sff8636_show_page_zero(const struct sff8636_memory_map *map) + { +- sff8636_show_ext_identifier(id); +- sff8636_show_connector(id); +- sff8636_show_transceiver(id); +- sff8636_show_encoding(id); +- sff_show_value_with_unit(id, SFF8636_BR_NOMINAL_OFFSET, +- "BR, Nominal", 100, "Mbps"); +- sff8636_show_rate_identifier(id); +- sff_show_value_with_unit(id, SFF8636_SM_LEN_OFFSET, +- "Length (SMF,km)", 1, "km"); +- sff_show_value_with_unit(id, SFF8636_OM3_LEN_OFFSET, +- "Length (OM3 50um)", 2, "m"); +- sff_show_value_with_unit(id, SFF8636_OM2_LEN_OFFSET, +- "Length (OM2 50um)", 1, "m"); +- sff_show_value_with_unit(id, SFF8636_OM1_LEN_OFFSET, +- "Length (OM1 62.5um)", 1, "m"); +- sff_show_value_with_unit(id, SFF8636_CBL_LEN_OFFSET, +- "Length (Copper or Active cable)", 1, "m"); +- sff8636_show_wavelength_or_copper_compliance(id); +- sff_show_ascii(id, SFF8636_VENDOR_NAME_START_OFFSET, ++ sff8636_show_ext_identifier(map); ++ sff8636_show_connector(map); ++ sff8636_show_transceiver(map); ++ sff8636_show_encoding(map); ++ sff_show_value_with_unit(map->page_00h, SFF8636_BR_NOMINAL_OFFSET, ++ "BR, Nominal", 100, "Mbps"); ++ sff8636_show_rate_identifier(map); ++ sff_show_value_with_unit(map->page_00h, SFF8636_SM_LEN_OFFSET, ++ "Length (SMF,km)", 1, "km"); ++ sff_show_value_with_unit(map->page_00h, SFF8636_OM3_LEN_OFFSET, ++ "Length (OM3 50um)", 2, "m"); ++ sff_show_value_with_unit(map->page_00h, SFF8636_OM2_LEN_OFFSET, ++ "Length (OM2 50um)", 1, "m"); ++ sff_show_value_with_unit(map->page_00h, SFF8636_OM1_LEN_OFFSET, ++ "Length (OM1 62.5um)", 1, "m"); ++ sff_show_value_with_unit(map->page_00h, SFF8636_CBL_LEN_OFFSET, ++ "Length (Copper or Active cable)", 1, "m"); ++ sff8636_show_wavelength_or_copper_compliance(map); ++ sff_show_ascii(map->page_00h, SFF8636_VENDOR_NAME_START_OFFSET, + SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name"); +- sff8636_show_oui(id, SFF8636_VENDOR_OUI_OFFSET); +- sff_show_ascii(id, SFF8636_VENDOR_PN_START_OFFSET, ++ sff8024_show_oui(map->page_00h, SFF8636_VENDOR_OUI_OFFSET); ++ sff_show_ascii(map->page_00h, SFF8636_VENDOR_PN_START_OFFSET, + SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN"); +- sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET, ++ sff_show_ascii(map->page_00h, SFF8636_VENDOR_REV_START_OFFSET, + SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev"); +- sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET, ++ sff_show_ascii(map->page_00h, SFF8636_VENDOR_SN_START_OFFSET, + SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN"); +- sff_show_ascii(id, SFF8636_DATE_YEAR_OFFSET, ++ sff_show_ascii(map->page_00h, SFF8636_DATE_YEAR_OFFSET, + SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code"); +- sff8636_show_revision_compliance(id); +- ++ sff_show_revision_compliance(map->lower_memory, ++ SFF8636_REV_COMPLIANCE_OFFSET); + } + + static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map, +@@ -896,13 +932,13 @@ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len) + + sff8636_memory_map_init_buf(&map, id, eeprom_len); + +- sff8636_show_identifier(id); +- switch (id[SFF8636_ID_OFFSET]) { ++ sff8636_show_identifier(&map); ++ switch (map.lower_memory[SFF8636_ID_OFFSET]) { + case SFF8024_ID_QSFP: + case SFF8024_ID_QSFP_PLUS: + case SFF8024_ID_QSFP28: +- sff8636_show_page_zero(id); +- sff8636_show_dom(id, id + 3 * 0x80, eeprom_len); ++ sff8636_show_page_zero(&map); ++ sff8636_show_dom(&map); + break; + } + } +@@ -939,9 +975,7 @@ void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero, + + sff8636_memory_map_init_pages(&map, page_zero, page_three); + +- sff8636_show_identifier(page_zero->data); +- sff8636_show_page_zero(page_zero->data); +- if (page_three) +- sff8636_show_dom(page_zero->data, page_three->data - 0x80, +- ETH_MODULE_SFF_8636_MAX_LEN); ++ sff8636_show_identifier(&map); ++ sff8636_show_page_zero(&map); ++ sff8636_show_dom(&map); + } +-- +2.35.1 + diff --git a/SOURCES/0020-sff-8636-Consolidate-code-between-IOCTL-and-netlink-.patch b/SOURCES/0020-sff-8636-Consolidate-code-between-IOCTL-and-netlink-.patch new file mode 100644 index 0000000..3ab7d8a --- /dev/null +++ b/SOURCES/0020-sff-8636-Consolidate-code-between-IOCTL-and-netlink-.patch @@ -0,0 +1,73 @@ +From 2e122ad9aa2aa0259df1035e3ec2765d8e008394 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:19 +0300 +Subject: [PATCH 20/35] sff-8636: Consolidate code between IOCTL and netlink + paths + +Now that both the netlink and IOCTL paths use the same memory map +structure for parsing, the code can be easily consolidated. + +Note that the switch-case statement is not necessary for the netlink +path, as the netlink code (i.e., netlink/module-eeprom.c) already +performed the check, but it is required for the IOCTL path. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/qsfp.c b/qsfp.c +index 354b3b1ce9ff..4aa49351e6b7 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -898,6 +898,19 @@ static void sff8636_show_page_zero(const struct sff8636_memory_map *map) + SFF8636_REV_COMPLIANCE_OFFSET); + } + ++static void sff8636_show_all_common(const struct sff8636_memory_map *map) ++{ ++ sff8636_show_identifier(map); ++ switch (map->lower_memory[SFF8636_ID_OFFSET]) { ++ case SFF8024_ID_QSFP: ++ case SFF8024_ID_QSFP_PLUS: ++ case SFF8024_ID_QSFP28: ++ sff8636_show_page_zero(map); ++ sff8636_show_dom(map); ++ break; ++ } ++} ++ + static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map, + const __u8 *id, __u32 eeprom_len) + { +@@ -931,16 +944,7 @@ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len) + } + + sff8636_memory_map_init_buf(&map, id, eeprom_len); +- +- sff8636_show_identifier(&map); +- switch (map.lower_memory[SFF8636_ID_OFFSET]) { +- case SFF8024_ID_QSFP: +- case SFF8024_ID_QSFP_PLUS: +- case SFF8024_ID_QSFP28: +- sff8636_show_page_zero(&map); +- sff8636_show_dom(&map); +- break; +- } ++ sff8636_show_all_common(&map); + } + + static void +@@ -974,8 +978,5 @@ void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero, + struct sff8636_memory_map map = {}; + + sff8636_memory_map_init_pages(&map, page_zero, page_three); +- +- sff8636_show_identifier(&map); +- sff8636_show_page_zero(&map); +- sff8636_show_dom(&map); ++ sff8636_show_all_common(&map); + } +-- +2.35.1 + diff --git a/SOURCES/0021-sff-8079-Split-SFF-8079-parsing-function.patch b/SOURCES/0021-sff-8079-Split-SFF-8079-parsing-function.patch new file mode 100644 index 0000000..89af1b3 --- /dev/null +++ b/SOURCES/0021-sff-8079-Split-SFF-8079-parsing-function.patch @@ -0,0 +1,101 @@ +From 1f20fb8e8b94d049672e48388ae57f89e89e880b Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:20 +0300 +Subject: [PATCH 21/35] sff-8079: Split SFF-8079 parsing function + +SFF-8079, unlike CMIS and SFF-8636, only has a single page and therefore +its parsing function (i.e., sff8079_show_all()) is called from both the +IOCTL and netlink paths with a buffer pointing to that single page. + +In future patches, the netlink code (i.e., netlink/module-eeprom.c) will +no longer call the SFF-8079 code with a buffer pointing to the first 128 +bytes of the EEPROM. Instead, the SFF-8079 code will need to request the +needed EEPROM data, as will be done in CMIS and SFF-8636. + +Therefore, as a preparation for this change, split the main parsing +function into IOCTL and netlink variants. + +No functional changes intended. + +Signed-off-by: Ido Schimmel +--- + ethtool.c | 4 ++-- + internal.h | 3 ++- + netlink/module-eeprom.c | 2 +- + sfpid.c | 12 +++++++++++- + 4 files changed, 16 insertions(+), 5 deletions(-) + +diff --git a/ethtool.c b/ethtool.c +index 6c744ff84eb9..5d4b5afbfd47 100644 +--- a/ethtool.c ++++ b/ethtool.c +@@ -4900,10 +4900,10 @@ static int do_getmodule(struct cmd_context *ctx) + switch (modinfo.type) { + #ifdef ETHTOOL_ENABLE_PRETTY_DUMP + case ETH_MODULE_SFF_8079: +- sff8079_show_all(eeprom->data); ++ sff8079_show_all_ioctl(eeprom->data); + break; + case ETH_MODULE_SFF_8472: +- sff8079_show_all(eeprom->data); ++ sff8079_show_all_ioctl(eeprom->data); + sff8472_show_all(eeprom->data); + break; + case ETH_MODULE_SFF_8436: +diff --git a/internal.h b/internal.h +index 7ca6066d4e12..a77efd385698 100644 +--- a/internal.h ++++ b/internal.h +@@ -384,7 +384,8 @@ int rxclass_rule_ins(struct cmd_context *ctx, + int rxclass_rule_del(struct cmd_context *ctx, __u32 loc); + + /* Module EEPROM parsing code */ +-void sff8079_show_all(const __u8 *id); ++void sff8079_show_all_ioctl(const __u8 *id); ++void sff8079_show_all_nl(const __u8 *id); + + /* Optics diagnostics */ + void sff8472_show_all(const __u8 *id); +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index 18b1abbe1252..101d5943c2bc 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -323,7 +323,7 @@ static void decoder_print(void) + + switch (module_id) { + case SFF8024_ID_SFP: +- sff8079_show_all(page_zero->data); ++ sff8079_show_all_nl(page_zero->data); + break; + case SFF8024_ID_QSFP: + case SFF8024_ID_QSFP28: +diff --git a/sfpid.c b/sfpid.c +index da2b3f4df3d2..c214820226d1 100644 +--- a/sfpid.c ++++ b/sfpid.c +@@ -396,7 +396,7 @@ static void sff8079_show_options(const __u8 *id) + printf("%s Power level 3 requirement\n", pfx); + } + +-void sff8079_show_all(const __u8 *id) ++static void sff8079_show_all_common(const __u8 *id) + { + sff8079_show_identifier(id); + if (((id[0] == 0x02) || (id[0] == 0x03)) && (id[1] == 0x04)) { +@@ -439,3 +439,13 @@ void sff8079_show_all(const __u8 *id) + sff8079_show_ascii(id, 84, 91, "Date code"); + } + } ++ ++void sff8079_show_all_ioctl(const __u8 *id) ++{ ++ sff8079_show_all_common(id); ++} ++ ++void sff8079_show_all_nl(const __u8 *id) ++{ ++ sff8079_show_all_common(id); ++} +-- +2.35.1 + diff --git a/SOURCES/0022-netlink-eeprom-Export-a-function-to-request-an-EEPRO.patch b/SOURCES/0022-netlink-eeprom-Export-a-function-to-request-an-EEPRO.patch new file mode 100644 index 0000000..69cb7c3 --- /dev/null +++ b/SOURCES/0022-netlink-eeprom-Export-a-function-to-request-an-EEPRO.patch @@ -0,0 +1,174 @@ +From fdb457a0ebb57c99fb987d0e34b2549f10dd4161 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:21 +0300 +Subject: [PATCH 22/35] netlink: eeprom: Export a function to request an EEPROM + page + +The function will be used by the EEPROM parsing code (e.g., cmis.c) to +request a specific page for parsing. + +All the data buffers used to store EEPROM page contents are stored on a +linked list that is flushed on exit. This relieves callers from the need +to explicitly free the requested pages. + +Signed-off-by: Ido Schimmel +--- + netlink/extapi.h | 11 +++++ + netlink/module-eeprom.c | 105 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 116 insertions(+) + +diff --git a/netlink/extapi.h b/netlink/extapi.h +index 91bf02b5e3be..129e2931d01d 100644 +--- a/netlink/extapi.h ++++ b/netlink/extapi.h +@@ -48,6 +48,9 @@ int nl_getmodule(struct cmd_context *ctx); + + void nl_monitor_usage(void); + ++int nl_get_eeprom_page(struct cmd_context *ctx, ++ struct ethtool_module_eeprom *request); ++ + #else /* ETHTOOL_ENABLE_NETLINK */ + + static inline void netlink_run_handler(struct cmd_context *ctx __maybe_unused, +@@ -73,6 +76,14 @@ static inline void nl_monitor_usage(void) + { + } + ++static inline int ++nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused, ++ struct ethtool_module_eeprom *request __maybe_unused) ++{ ++ fprintf(stderr, "Netlink not supported by ethtool.\n"); ++ return -EOPNOTSUPP; ++} ++ + #define nl_gset NULL + #define nl_sset NULL + #define nl_permaddr NULL +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index 101d5943c2bc..ee5508840157 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -341,6 +341,110 @@ static void decoder_print(void) + } + #endif + ++static struct list_head eeprom_page_list = LIST_HEAD_INIT(eeprom_page_list); ++ ++struct eeprom_page_entry { ++ struct list_head list; /* Member of eeprom_page_list */ ++ void *data; ++}; ++ ++static int eeprom_page_list_add(void *data) ++{ ++ struct eeprom_page_entry *entry; ++ ++ entry = malloc(sizeof(*entry)); ++ if (!entry) ++ return -ENOMEM; ++ ++ entry->data = data; ++ list_add(&entry->list, &eeprom_page_list); ++ ++ return 0; ++} ++ ++static void eeprom_page_list_flush(void) ++{ ++ struct eeprom_page_entry *entry; ++ struct list_head *head, *next; ++ ++ list_for_each_safe(head, next, &eeprom_page_list) { ++ entry = (struct eeprom_page_entry *) head; ++ free(entry->data); ++ list_del(head); ++ free(entry); ++ } ++} ++ ++static int get_eeprom_page_reply_cb(const struct nlmsghdr *nlhdr, void *data) ++{ ++ const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {}; ++ struct ethtool_module_eeprom *request = data; ++ DECLARE_ATTR_TB_INFO(tb); ++ u8 *eeprom_data; ++ int ret; ++ ++ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); ++ if (ret < 0) ++ return ret; ++ ++ if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA]) ++ return MNL_CB_ERROR; ++ ++ eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]); ++ request->data = malloc(request->length); ++ if (!request->data) ++ return MNL_CB_ERROR; ++ memcpy(request->data, eeprom_data, request->length); ++ ++ ret = eeprom_page_list_add(request->data); ++ if (ret < 0) ++ goto err_list_add; ++ ++ return MNL_CB_OK; ++ ++err_list_add: ++ free(request->data); ++ return MNL_CB_ERROR; ++} ++ ++int nl_get_eeprom_page(struct cmd_context *ctx, ++ struct ethtool_module_eeprom *request) ++{ ++ struct nl_context *nlctx = ctx->nlctx; ++ struct nl_socket *nlsock; ++ struct nl_msg_buff *msg; ++ int ret; ++ ++ if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS) ++ return -EINVAL; ++ ++ nlsock = nlctx->ethnl_socket; ++ msg = &nlsock->msgbuff; ++ ++ ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET, ++ ETHTOOL_A_MODULE_EEPROM_HEADER, 0); ++ if (ret < 0) ++ return ret; ++ ++ if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH, ++ request->length) || ++ ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET, ++ request->offset) || ++ ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE, ++ request->page) || ++ ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK, ++ request->bank) || ++ ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, ++ request->i2c_address)) ++ return -EMSGSIZE; ++ ++ ret = nlsock_sendmsg(nlsock, NULL); ++ if (ret < 0) ++ return ret; ++ return nlsock_process_reply(nlsock, get_eeprom_page_reply_cb, ++ (void *)request); ++} ++ + int nl_getmodule(struct cmd_context *ctx) + { + struct cmd_params getmodule_cmd_params = {}; +@@ -425,6 +529,7 @@ int nl_getmodule(struct cmd_context *ctx) + } + + cleanup: ++ eeprom_page_list_flush(); + cache_free(); + return ret; + } +-- +2.35.1 + diff --git a/SOURCES/0023-cmis-Request-specific-pages-for-parsing-in-netlink-p.patch b/SOURCES/0023-cmis-Request-specific-pages-for-parsing-in-netlink-p.patch new file mode 100644 index 0000000..5dbd998 --- /dev/null +++ b/SOURCES/0023-cmis-Request-specific-pages-for-parsing-in-netlink-p.patch @@ -0,0 +1,194 @@ +From 5f45f370e132f144cdbab9ea718393bd37ee23db Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:22 +0300 +Subject: [PATCH 23/35] cmis: Request specific pages for parsing in netlink + path + +In the netlink path, unlike the IOCTL path, user space requests specific +EEPROM pages from the kernel. The presence of optional and banked pages +is advertised via various bits in the EEPROM contents. + +Currently, for CMIS, the Lower Memory, Page 00h and the optional Page +01h are requested by the netlink code (i.e., netlink/module-eeprom.c) +and passed to the CMIS code (i.e., cmis.c) as two arguments for parsing. + +This is problematic for several reasons. First, this approach is not +very scaleable as CMIS supports a lot of optional and banked pages. +Passing them as separate arguments to the CMIS code is not going to +work. + +Second, the knowledge of which optional and banked pages are available +is encapsulated in the CMIS parsing code. As such, the common netlink +code has no business of fetching optional and banked pages that might be +invalid. + +Instead, pass the command context to the CMIS parsing function and allow +it to fetch only valid pages via the 'MODULE_EEPROM_GET' netlink +message. + +Tested by making sure that the output of 'ethtool -m' does not change +before and after the patch. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 60 ++++++++++++++++++++++++++++++++--------- + cmis.h | 3 +-- + netlink/module-eeprom.c | 7 +++-- + 3 files changed, 51 insertions(+), 19 deletions(-) + +diff --git a/cmis.c b/cmis.c +index eb7791dd59df..4798fd4c7d68 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -9,9 +9,11 @@ + + #include + #include ++#include + #include "internal.h" + #include "sff-common.h" + #include "cmis.h" ++#include "netlink/extapi.h" + + struct cmis_memory_map { + const __u8 *lower_memory; +@@ -21,6 +23,7 @@ struct cmis_memory_map { + }; + + #define CMIS_PAGE_SIZE 0x80 ++#define CMIS_I2C_ADDRESS 0x50 + + static void cmis_show_identifier(const struct cmis_memory_map *map) + { +@@ -384,36 +387,67 @@ void cmis_show_all_ioctl(const __u8 *id) + cmis_show_all_common(&map); + } + +-static void +-cmis_memory_map_init_pages(struct cmis_memory_map *map, +- const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_one) ++static void cmis_request_init(struct ethtool_module_eeprom *request, u8 bank, ++ u8 page, u32 offset) + { ++ request->offset = offset; ++ request->length = CMIS_PAGE_SIZE; ++ request->page = page; ++ request->bank = bank; ++ request->i2c_address = CMIS_I2C_ADDRESS; ++ request->data = NULL; ++} ++ ++static int ++cmis_memory_map_init_pages(struct cmd_context *ctx, ++ struct cmis_memory_map *map) ++{ ++ struct ethtool_module_eeprom request; ++ int ret; ++ + /* Lower Memory and Page 00h are always present. + * + * Offset into Upper Memory is between page size and twice the page + * size. Therefore, set the base address of each page to its base +- * address minus page size. For Page 00h, this is the address of the +- * Lower Memory. ++ * address minus page size. + */ +- map->lower_memory = page_zero->data; +- map->page_00h = page_zero->data; ++ cmis_request_init(&request, 0, 0x0, 0); ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ map->lower_memory = request.data; ++ ++ cmis_request_init(&request, 0, 0x0, CMIS_PAGE_SIZE); ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ map->page_00h = request.data - CMIS_PAGE_SIZE; + + /* Page 01h is only present when the module memory model is paged and + * not flat. + */ + if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & + CMIS_MEMORY_MODEL_MASK) +- return; ++ return 0; ++ ++ cmis_request_init(&request, 0, 0x1, CMIS_PAGE_SIZE); ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ map->page_01h = request.data - CMIS_PAGE_SIZE; + +- map->page_01h = page_one->data - CMIS_PAGE_SIZE; ++ return 0; + } + +-void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_one) ++int cmis_show_all_nl(struct cmd_context *ctx) + { + struct cmis_memory_map map = {}; ++ int ret; + +- cmis_memory_map_init_pages(&map, page_zero, page_one); ++ ret = cmis_memory_map_init_pages(ctx, &map); ++ if (ret < 0) ++ return ret; + cmis_show_all_common(&map); ++ ++ return 0; + } +diff --git a/cmis.h b/cmis.h +index c878e3bc5afd..911491dc5c8f 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -123,7 +123,6 @@ + + void cmis_show_all_ioctl(const __u8 *id); + +-void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_one); ++int cmis_show_all_nl(struct cmd_context *ctx); + + #endif /* CMIS_H__ */ +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index ee5508840157..a8e2662e0b8c 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -314,11 +314,10 @@ static int decoder_prefetch(struct nl_context *nlctx) + return page_fetch(nlctx, &request); + } + +-static void decoder_print(void) ++static void decoder_print(struct cmd_context *ctx) + { + struct ethtool_module_eeprom *page_three = cache_get(3, 0, ETH_I2C_ADDRESS_LOW); + struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW); +- struct ethtool_module_eeprom *page_one = cache_get(1, 0, ETH_I2C_ADDRESS_LOW); + u8 module_id = page_zero->data[SFF8636_ID_OFFSET]; + + switch (module_id) { +@@ -332,7 +331,7 @@ static void decoder_print(void) + break; + case SFF8024_ID_QSFP_DD: + case SFF8024_ID_DSFP: +- cmis_show_all_nl(page_zero, page_one); ++ cmis_show_all_nl(ctx); + break; + default: + dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset); +@@ -524,7 +523,7 @@ int nl_getmodule(struct cmd_context *ctx) + ret = decoder_prefetch(nlctx); + if (ret) + goto cleanup; +- decoder_print(); ++ decoder_print(ctx); + #endif + } + +-- +2.35.1 + diff --git a/SOURCES/0024-sff-8636-Request-specific-pages-for-parsing-in-netli.patch b/SOURCES/0024-sff-8636-Request-specific-pages-for-parsing-in-netli.patch new file mode 100644 index 0000000..143d915 --- /dev/null +++ b/SOURCES/0024-sff-8636-Request-specific-pages-for-parsing-in-netli.patch @@ -0,0 +1,181 @@ +From fdae2732b25090f9d41e192a5dd47a45a6516a94 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:23 +0300 +Subject: [PATCH 24/35] sff-8636: Request specific pages for parsing in netlink + path + +In the netlink path, unlike the IOCTL path, user space requests specific +EEPROM pages from the kernel. The presence of optional pages is +advertised via various bits in the EEPROM contents. + +Currently, for SFF-8636, the Lower Memory, Page 00h and the optional +Page 03h are requested by the netlink code (i.e., +netlink/module-eeprom.c) and passed to the SFF-8636 code (i.e., qsfp.c) +as two arguments for parsing. + +This is problematic for several reasons. First, this approach is not +very scaleable as SFF-8636 supports a lot of optional pages. Passing +them as separate arguments to the SFF-8636 code is not going to work. + +Second, the knowledge of which optional pages are available is +encapsulated in the SFF-8636 parsing code. As such, the common netlink +code has no business of fetching optional pages that might be invalid. + +Instead, pass the command context to the SFF-8636 parsing function and +allow it to fetch only valid pages via the 'MODULE_EEPROM_GET' netlink +message. + +Tested by making sure that the output of 'ethtool -m' does not change +before and after the patch. + +Signed-off-by: Ido Schimmel +--- + internal.h | 3 +-- + netlink/module-eeprom.c | 3 +-- + qsfp.c | 60 ++++++++++++++++++++++++++++++++--------- + 3 files changed, 49 insertions(+), 17 deletions(-) + +diff --git a/internal.h b/internal.h +index a77efd385698..2407d3c223fa 100644 +--- a/internal.h ++++ b/internal.h +@@ -392,8 +392,7 @@ void sff8472_show_all(const __u8 *id); + + /* QSFP Optics diagnostics */ + void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len); +-void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_three); ++int sff8636_show_all_nl(struct cmd_context *ctx); + + /* FUJITSU Extended Socket network device */ + int fjes_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index a8e2662e0b8c..f04f8e134223 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -316,7 +316,6 @@ static int decoder_prefetch(struct nl_context *nlctx) + + static void decoder_print(struct cmd_context *ctx) + { +- struct ethtool_module_eeprom *page_three = cache_get(3, 0, ETH_I2C_ADDRESS_LOW); + struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW); + u8 module_id = page_zero->data[SFF8636_ID_OFFSET]; + +@@ -327,7 +326,7 @@ static void decoder_print(struct cmd_context *ctx) + case SFF8024_ID_QSFP: + case SFF8024_ID_QSFP28: + case SFF8024_ID_QSFP_PLUS: +- sff8636_show_all_nl(page_zero, page_three); ++ sff8636_show_all_nl(ctx); + break; + case SFF8024_ID_QSFP_DD: + case SFF8024_ID_DSFP: +diff --git a/qsfp.c b/qsfp.c +index 4aa49351e6b7..e7c2f51cd9c6 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -55,10 +55,12 @@ + **/ + #include + #include ++#include + #include "internal.h" + #include "sff-common.h" + #include "qsfp.h" + #include "cmis.h" ++#include "netlink/extapi.h" + + struct sff8636_memory_map { + const __u8 *lower_memory; +@@ -68,6 +70,7 @@ struct sff8636_memory_map { + }; + + #define SFF8636_PAGE_SIZE 0x80 ++#define SFF8636_I2C_ADDRESS 0x50 + + #define MAX_DESC_SIZE 42 + +@@ -947,36 +950,67 @@ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len) + sff8636_show_all_common(&map); + } + +-static void +-sff8636_memory_map_init_pages(struct sff8636_memory_map *map, +- const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_three) ++static void sff8636_request_init(struct ethtool_module_eeprom *request, u8 page, ++ u32 offset) ++{ ++ request->offset = offset; ++ request->length = SFF8636_PAGE_SIZE; ++ request->page = page; ++ request->bank = 0; ++ request->i2c_address = SFF8636_I2C_ADDRESS; ++ request->data = NULL; ++} ++ ++static int ++sff8636_memory_map_init_pages(struct cmd_context *ctx, ++ struct sff8636_memory_map *map) + { ++ struct ethtool_module_eeprom request; ++ int ret; ++ + /* Lower Memory and Page 00h are always present. + * + * Offset into Upper Memory is between page size and twice the page + * size. Therefore, set the base address of each page to its base +- * address minus page size. For Page 00h, this is the address of the +- * Lower Memory. ++ * address minus page size. + */ +- map->lower_memory = page_zero->data; +- map->page_00h = page_zero->data; ++ sff8636_request_init(&request, 0x0, 0); ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ map->lower_memory = request.data; ++ ++ sff8636_request_init(&request, 0x0, SFF8636_PAGE_SIZE); ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ map->page_00h = request.data - SFF8636_PAGE_SIZE; + + /* Page 03h is only present when the module memory model is paged and + * not flat. + */ + if (map->lower_memory[SFF8636_STATUS_2_OFFSET] & + SFF8636_STATUS_PAGE_3_PRESENT) +- return; ++ return 0; + +- map->page_03h = page_three->data - SFF8636_PAGE_SIZE; ++ sff8636_request_init(&request, 0x3, SFF8636_PAGE_SIZE); ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ map->page_03h = request.data - SFF8636_PAGE_SIZE; ++ ++ return 0; + } + +-void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero, +- const struct ethtool_module_eeprom *page_three) ++int sff8636_show_all_nl(struct cmd_context *ctx) + { + struct sff8636_memory_map map = {}; ++ int ret; + +- sff8636_memory_map_init_pages(&map, page_zero, page_three); ++ ret = sff8636_memory_map_init_pages(ctx, &map); ++ if (ret < 0) ++ return ret; + sff8636_show_all_common(&map); ++ ++ return 0; + } +-- +2.35.1 + diff --git a/SOURCES/0025-sff-8079-Request-specific-pages-for-parsing-in-netli.patch b/SOURCES/0025-sff-8079-Request-specific-pages-for-parsing-in-netli.patch new file mode 100644 index 0000000..b013d36 --- /dev/null +++ b/SOURCES/0025-sff-8079-Request-specific-pages-for-parsing-in-netli.patch @@ -0,0 +1,92 @@ +From bda06d81f41bb1cde16a319728d479cda4b9f295 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:24 +0300 +Subject: [PATCH 25/35] sff-8079: Request specific pages for parsing in netlink + path + +Convert the SFF-8079 code to request the required EEPROM contents in the +netlink path as was done for CMIS and SFF-8636. It will allow us to +remove standard-specific code from the netlink code (i.e., +netlink/module-eeprom.c). + +In addition, in the future, it will allow the netlink path to support +parsing of SFF-8472. + +Tested by making sure that the output of 'ethtool -m' does not change +before and after the patch. + +Signed-off-by: Ido Schimmel +--- + internal.h | 2 +- + netlink/module-eeprom.c | 2 +- + sfpid.c | 20 ++++++++++++++++++-- + 3 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/internal.h b/internal.h +index 2407d3c223fa..0d9d816ab563 100644 +--- a/internal.h ++++ b/internal.h +@@ -385,7 +385,7 @@ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc); + + /* Module EEPROM parsing code */ + void sff8079_show_all_ioctl(const __u8 *id); +-void sff8079_show_all_nl(const __u8 *id); ++int sff8079_show_all_nl(struct cmd_context *ctx); + + /* Optics diagnostics */ + void sff8472_show_all(const __u8 *id); +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index f04f8e134223..6d76b8a96461 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -321,7 +321,7 @@ static void decoder_print(struct cmd_context *ctx) + + switch (module_id) { + case SFF8024_ID_SFP: +- sff8079_show_all_nl(page_zero->data); ++ sff8079_show_all_nl(ctx); + break; + case SFF8024_ID_QSFP: + case SFF8024_ID_QSFP28: +diff --git a/sfpid.c b/sfpid.c +index c214820226d1..621d1e86c278 100644 +--- a/sfpid.c ++++ b/sfpid.c +@@ -8,8 +8,13 @@ + */ + + #include ++#include + #include "internal.h" + #include "sff-common.h" ++#include "netlink/extapi.h" ++ ++#define SFF8079_PAGE_SIZE 0x80 ++#define SFF8079_I2C_ADDRESS_LOW 0x50 + + static void sff8079_show_identifier(const __u8 *id) + { +@@ -445,7 +450,18 @@ void sff8079_show_all_ioctl(const __u8 *id) + sff8079_show_all_common(id); + } + +-void sff8079_show_all_nl(const __u8 *id) ++int sff8079_show_all_nl(struct cmd_context *ctx) + { +- sff8079_show_all_common(id); ++ struct ethtool_module_eeprom request = { ++ .length = SFF8079_PAGE_SIZE, ++ .i2c_address = SFF8079_I2C_ADDRESS_LOW, ++ }; ++ int ret; ++ ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ sff8079_show_all_common(request.data); ++ ++ return 0; + } +-- +2.35.1 + diff --git a/SOURCES/0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch b/SOURCES/0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch new file mode 100644 index 0000000..99dc9a1 --- /dev/null +++ b/SOURCES/0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch @@ -0,0 +1,428 @@ +From b6005ecf2ce2aaeb86995fa50df97e7384f46f99 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 12 Oct 2021 16:25:25 +0300 +Subject: [PATCH 26/35] netlink: eeprom: Defer page requests to individual + parsers + +The individual EEPROM parsers (e.g., CMIS, SFF-8636) now request the +EEPROM pages they intend to parse and populate their memory maps before +parsing them. + +Therefore, there is no need for the common netlink code to request +potentially invalid pages and pass them as blobs to these parsers. + +Instead, only query the SFF-8024 Identifier Value which is located at +I2C address 0x50, byte 0 and dispatch to the relevant EEPROM parser. + +Tested by making sure that the output of 'ethtool -m' does not change +for SFF-8079, SFF-8636 and CMIS before and after the patch. + +Signed-off-by: Ido Schimmel +--- + netlink/module-eeprom.c | 347 +++++++--------------------------------- + 1 file changed, 59 insertions(+), 288 deletions(-) + +diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c +index 6d76b8a96461..f359aeec4ddf 100644 +--- a/netlink/module-eeprom.c ++++ b/netlink/module-eeprom.c +@@ -19,7 +19,6 @@ + #include "parser.h" + + #define ETH_I2C_ADDRESS_LOW 0x50 +-#define ETH_I2C_ADDRESS_HIGH 0x51 + #define ETH_I2C_MAX_ADDRESS 0x7F + + struct cmd_params { +@@ -78,267 +77,6 @@ static const struct param_parser getmodule_params[] = { + {} + }; + +-struct page_entry { +- struct list_head link; +- struct ethtool_module_eeprom *page; +-}; +- +-static struct list_head page_list = LIST_HEAD_INIT(page_list); +- +-static int cache_add(struct ethtool_module_eeprom *page) +-{ +- struct page_entry *list_element; +- +- if (!page) +- return -1; +- list_element = malloc(sizeof(*list_element)); +- if (!list_element) +- return -ENOMEM; +- list_element->page = page; +- +- list_add(&list_element->link, &page_list); +- return 0; +-} +- +-static void page_free(struct ethtool_module_eeprom *page) +-{ +- free(page->data); +- free(page); +-} +- +-static void cache_del(struct ethtool_module_eeprom *page) +-{ +- struct ethtool_module_eeprom *entry; +- struct list_head *head, *next; +- +- list_for_each_safe(head, next, &page_list) { +- entry = ((struct page_entry *)head)->page; +- if (entry == page) { +- list_del(head); +- free(head); +- page_free(entry); +- break; +- } +- } +-} +- +-static void cache_free(void) +-{ +- struct ethtool_module_eeprom *entry; +- struct list_head *head, *next; +- +- list_for_each_safe(head, next, &page_list) { +- entry = ((struct page_entry *)head)->page; +- list_del(head); +- free(head); +- page_free(entry); +- } +-} +- +-static struct ethtool_module_eeprom *page_join(struct ethtool_module_eeprom *page_a, +- struct ethtool_module_eeprom *page_b) +-{ +- struct ethtool_module_eeprom *joined_page; +- u32 total_length; +- +- if (!page_a || !page_b || +- page_a->page != page_b->page || +- page_a->bank != page_b->bank || +- page_a->i2c_address != page_b->i2c_address) +- return NULL; +- +- total_length = page_a->length + page_b->length; +- joined_page = calloc(1, sizeof(*joined_page)); +- joined_page->data = calloc(1, total_length); +- joined_page->page = page_a->page; +- joined_page->bank = page_a->bank; +- joined_page->length = total_length; +- joined_page->i2c_address = page_a->i2c_address; +- +- if (page_a->offset < page_b->offset) { +- memcpy(joined_page->data, page_a->data, page_a->length); +- memcpy(joined_page->data + page_a->length, page_b->data, page_b->length); +- joined_page->offset = page_a->offset; +- } else { +- memcpy(joined_page->data, page_b->data, page_b->length); +- memcpy(joined_page->data + page_b->length, page_a->data, page_a->length); +- joined_page->offset = page_b->offset; +- } +- +- return joined_page; +-} +- +-static struct ethtool_module_eeprom *cache_get(u32 page, u32 bank, u8 i2c_address) +-{ +- struct ethtool_module_eeprom *entry; +- struct list_head *head, *next; +- +- list_for_each_safe(head, next, &page_list) { +- entry = ((struct page_entry *)head)->page; +- if (entry->page == page && entry->bank == bank && +- entry->i2c_address == i2c_address) +- return entry; +- } +- +- return NULL; +-} +- +-static int getmodule_page_fetch_reply_cb(const struct nlmsghdr *nlhdr, +- void *data) +-{ +- const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {}; +- DECLARE_ATTR_TB_INFO(tb); +- struct ethtool_module_eeprom *lower_page; +- struct ethtool_module_eeprom *response; +- struct ethtool_module_eeprom *request; +- struct ethtool_module_eeprom *joined; +- u8 *eeprom_data; +- int ret; +- +- ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); +- if (ret < 0) +- return ret; +- +- if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA]) { +- fprintf(stderr, "Malformed netlink message (getmodule)\n"); +- return MNL_CB_ERROR; +- } +- +- response = calloc(1, sizeof(*response)); +- if (!response) +- return -ENOMEM; +- +- request = (struct ethtool_module_eeprom *)data; +- response->offset = request->offset; +- response->page = request->page; +- response->bank = request->bank; +- response->i2c_address = request->i2c_address; +- response->length = mnl_attr_get_payload_len(tb[ETHTOOL_A_MODULE_EEPROM_DATA]); +- eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]); +- +- response->data = malloc(response->length); +- if (!response->data) { +- free(response); +- return -ENOMEM; +- } +- memcpy(response->data, eeprom_data, response->length); +- +- if (!request->page) { +- lower_page = cache_get(request->page, request->bank, response->i2c_address); +- if (lower_page) { +- joined = page_join(lower_page, response); +- page_free(response); +- cache_del(lower_page); +- return cache_add(joined); +- } +- } +- +- return cache_add(response); +-} +- +-static int page_fetch(struct nl_context *nlctx, const struct ethtool_module_eeprom *request) +-{ +- struct nl_socket *nlsock = nlctx->ethnl_socket; +- struct nl_msg_buff *msg = &nlsock->msgbuff; +- struct ethtool_module_eeprom *page; +- int ret; +- +- if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS) +- return -EINVAL; +- +- /* Satisfy request right away, if region is already in cache */ +- page = cache_get(request->page, request->bank, request->i2c_address); +- if (page && page->offset <= request->offset && +- page->offset + page->length >= request->offset + request->length) { +- return 0; +- } +- +- ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET, +- ETHTOOL_A_MODULE_EEPROM_HEADER, 0); +- if (ret < 0) +- return ret; +- +- if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH, request->length) || +- ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET, request->offset) || +- ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE, request->page) || +- ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK, request->bank) || +- ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, request->i2c_address)) +- return -EMSGSIZE; +- +- ret = nlsock_sendmsg(nlsock, NULL); +- if (ret < 0) +- return ret; +- ret = nlsock_process_reply(nlsock, getmodule_page_fetch_reply_cb, (void *)request); +- if (ret < 0) +- return ret; +- +- return nlsock_process_reply(nlsock, nomsg_reply_cb, NULL); +-} +- +-#ifdef ETHTOOL_ENABLE_PRETTY_DUMP +-static int decoder_prefetch(struct nl_context *nlctx) +-{ +- struct ethtool_module_eeprom *page_zero_lower = cache_get(0, 0, ETH_I2C_ADDRESS_LOW); +- struct ethtool_module_eeprom request = {0}; +- u8 module_id = page_zero_lower->data[0]; +- int err = 0; +- +- /* Fetch rest of page 00 */ +- request.i2c_address = ETH_I2C_ADDRESS_LOW; +- request.offset = 128; +- request.length = 128; +- err = page_fetch(nlctx, &request); +- if (err) +- return err; +- +- switch (module_id) { +- case SFF8024_ID_QSFP: +- case SFF8024_ID_QSFP28: +- case SFF8024_ID_QSFP_PLUS: +- memset(&request, 0, sizeof(request)); +- request.i2c_address = ETH_I2C_ADDRESS_LOW; +- request.offset = 128; +- request.length = 128; +- request.page = 3; +- break; +- case SFF8024_ID_QSFP_DD: +- case SFF8024_ID_DSFP: +- memset(&request, 0, sizeof(request)); +- request.i2c_address = ETH_I2C_ADDRESS_LOW; +- request.offset = 128; +- request.length = 128; +- request.page = 1; +- break; +- } +- +- return page_fetch(nlctx, &request); +-} +- +-static void decoder_print(struct cmd_context *ctx) +-{ +- struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW); +- u8 module_id = page_zero->data[SFF8636_ID_OFFSET]; +- +- switch (module_id) { +- case SFF8024_ID_SFP: +- sff8079_show_all_nl(ctx); +- break; +- case SFF8024_ID_QSFP: +- case SFF8024_ID_QSFP28: +- case SFF8024_ID_QSFP_PLUS: +- sff8636_show_all_nl(ctx); +- break; +- case SFF8024_ID_QSFP_DD: +- case SFF8024_ID_DSFP: +- cmis_show_all_nl(ctx); +- break; +- default: +- dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset); +- break; +- } +-} +-#endif +- + static struct list_head eeprom_page_list = LIST_HEAD_INIT(eeprom_page_list); + + struct eeprom_page_entry { +@@ -443,14 +181,64 @@ int nl_get_eeprom_page(struct cmd_context *ctx, + (void *)request); + } + ++static int eeprom_dump_hex(struct cmd_context *ctx) ++{ ++ struct ethtool_module_eeprom request = { ++ .length = 128, ++ .i2c_address = ETH_I2C_ADDRESS_LOW, ++ }; ++ int ret; ++ ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ ++ dump_hex(stdout, request.data, request.length, request.offset); ++ ++ return 0; ++} ++ ++static int eeprom_parse(struct cmd_context *ctx) ++{ ++ struct ethtool_module_eeprom request = { ++ .length = 1, ++ .i2c_address = ETH_I2C_ADDRESS_LOW, ++ }; ++ int ret; ++ ++ /* Fetch the SFF-8024 Identifier Value. For all supported standards, it ++ * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024, ++ * revision 4.9. ++ */ ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ ++ switch (request.data[0]) { ++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP ++ case SFF8024_ID_SFP: ++ return sff8079_show_all_nl(ctx); ++ case SFF8024_ID_QSFP: ++ case SFF8024_ID_QSFP28: ++ case SFF8024_ID_QSFP_PLUS: ++ return sff8636_show_all_nl(ctx); ++ case SFF8024_ID_QSFP_DD: ++ case SFF8024_ID_DSFP: ++ return cmis_show_all_nl(ctx); ++#endif ++ default: ++ /* If we cannot recognize the memory map, default to dumping ++ * the first 128 bytes in hex. ++ */ ++ return eeprom_dump_hex(ctx); ++ } ++} ++ + int nl_getmodule(struct cmd_context *ctx) + { + struct cmd_params getmodule_cmd_params = {}; + struct ethtool_module_eeprom request = {0}; +- struct ethtool_module_eeprom *reply_page; + struct nl_context *nlctx = ctx->nlctx; +- u32 dump_length; +- u8 *eeprom_data; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_EEPROM_GET, false)) +@@ -479,12 +267,6 @@ int nl_getmodule(struct cmd_context *ctx) + return -EOPNOTSUPP; + } + +- request.i2c_address = ETH_I2C_ADDRESS_LOW; +- request.length = 128; +- ret = page_fetch(nlctx, &request); +- if (ret) +- goto cleanup; +- + #ifdef ETHTOOL_ENABLE_PRETTY_DUMP + if (getmodule_cmd_params.page || getmodule_cmd_params.bank || + getmodule_cmd_params.offset || getmodule_cmd_params.length) +@@ -501,33 +283,22 @@ int nl_getmodule(struct cmd_context *ctx) + request.offset = 128; + + if (getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) { +- ret = page_fetch(nlctx, &request); ++ ret = nl_get_eeprom_page(ctx, &request); + if (ret < 0) + goto cleanup; +- reply_page = cache_get(request.page, request.bank, request.i2c_address); +- if (!reply_page) { +- ret = -EINVAL; +- goto cleanup; +- } + +- eeprom_data = reply_page->data + (request.offset - reply_page->offset); +- dump_length = reply_page->length < request.length ? reply_page->length +- : request.length; + if (getmodule_cmd_params.dump_raw) +- fwrite(eeprom_data, 1, request.length, stdout); ++ fwrite(request.data, 1, request.length, stdout); + else +- dump_hex(stdout, eeprom_data, dump_length, request.offset); ++ dump_hex(stdout, request.data, request.length, ++ request.offset); + } else { +-#ifdef ETHTOOL_ENABLE_PRETTY_DUMP +- ret = decoder_prefetch(nlctx); +- if (ret) ++ ret = eeprom_parse(ctx); ++ if (ret < 0) + goto cleanup; +- decoder_print(ctx); +-#endif + } + + cleanup: + eeprom_page_list_flush(); +- cache_free(); + return ret; + } +-- +2.35.1 + diff --git a/SOURCES/0027-sff-8636-Use-an-SFF-8636-specific-define-for-maximum.patch b/SOURCES/0027-sff-8636-Use-an-SFF-8636-specific-define-for-maximum.patch new file mode 100644 index 0000000..1dd08a9 --- /dev/null +++ b/SOURCES/0027-sff-8636-Use-an-SFF-8636-specific-define-for-maximum.patch @@ -0,0 +1,84 @@ +From 37093971b0f645542c4bff603f41f807e8023bd3 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 23 Nov 2021 19:40:55 +0200 +Subject: [PATCH 27/35] sff-8636: Use an SFF-8636 specific define for maximum + number of channels + +'MAX_CHANNEL_NUM' is defined in the common SFF code as 4 and used to set +the size of the per-channel diagnostics array in the common 'sff_diags' +structure. + +The CMIS parsing code is also going to use the structure, but it can +have up to 32 channels, unlike SFF-8636 that only has 4. + +Therefore, set 'MAX_CHANNEL_NUM' to 32 and change the SFF-8636 code to +use an SFF-8636 specific define instead of the common 'MAX_CHANNEL_NUM'. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 9 +++++---- + sff-common.h | 2 +- + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/qsfp.c b/qsfp.c +index e7c2f51cd9c6..58c4c4775e9b 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -71,6 +71,7 @@ struct sff8636_memory_map { + + #define SFF8636_PAGE_SIZE 0x80 + #define SFF8636_I2C_ADDRESS 0x50 ++#define SFF8636_MAX_CHANNEL_NUM 4 + + #define MAX_DESC_SIZE 42 + +@@ -761,7 +762,7 @@ static void sff8636_dom_parse(const struct sff8636_memory_map *map, + + out: + /* Channel Specific Data */ +- for (i = 0; i < MAX_CHANNEL_NUM; i++) { ++ for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) { + u8 rx_power_offset, tx_bias_offset; + u8 tx_power_offset; + +@@ -832,13 +833,13 @@ static void sff8636_show_dom(const struct sff8636_memory_map *map) + printf("\t%-41s : %s\n", "Alarm/warning flags implemented", + (sd.supports_alarms ? "Yes" : "No")); + +- for (i = 0; i < MAX_CHANNEL_NUM; i++) { ++ for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) { + snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)", + "Laser tx bias current", i+1); + PRINT_BIAS(power_string, sd.scd[i].bias_cur); + } + +- for (i = 0; i < MAX_CHANNEL_NUM; i++) { ++ for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) { + snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)", + "Transmit avg optical power", i+1); + PRINT_xX_PWR(power_string, sd.scd[i].tx_power); +@@ -849,7 +850,7 @@ static void sff8636_show_dom(const struct sff8636_memory_map *map) + else + rx_power_string = "Rcvr signal avg optical power"; + +- for (i = 0; i < MAX_CHANNEL_NUM; i++) { ++ for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) { + snprintf(power_string, MAX_DESC_SIZE, "%s(Channel %d)", + rx_power_string, i+1); + PRINT_xX_PWR(power_string, sd.scd[i].rx_power); +diff --git a/sff-common.h b/sff-common.h +index 2183f41ff9c9..aab306e0b74f 100644 +--- a/sff-common.h ++++ b/sff-common.h +@@ -160,7 +160,7 @@ struct sff_channel_diags { + /* Module Monitoring Fields */ + struct sff_diags { + +-#define MAX_CHANNEL_NUM 4 ++#define MAX_CHANNEL_NUM 32 + #define LWARN 0 + #define HWARN 1 + #define LALRM 2 +-- +2.35.1 + diff --git a/SOURCES/0028-sff-common-Move-OFFSET_TO_U16_PTR-to-common-header-f.patch b/SOURCES/0028-sff-common-Move-OFFSET_TO_U16_PTR-to-common-header-f.patch new file mode 100644 index 0000000..9149cfc --- /dev/null +++ b/SOURCES/0028-sff-common-Move-OFFSET_TO_U16_PTR-to-common-header-f.patch @@ -0,0 +1,45 @@ +From 2dcf6b9dc1c1874705b9e71e13e00dde9f7f576c Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 23 Nov 2021 19:40:56 +0200 +Subject: [PATCH 28/35] sff-common: Move OFFSET_TO_U16_PTR() to common header + file + +The define is also useful for CMIS, so move it from SFF-8636 to the +common header file. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 1 - + sff-common.h | 4 ++-- + 2 files changed, 2 insertions(+), 3 deletions(-) + +diff --git a/qsfp.c b/qsfp.c +index 58c4c4775e9b..b3c9e1516af9 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -700,7 +700,6 @@ sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map *ma + * Second byte are 1/256th of degree, which are added to the dec part. + */ + #define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset)) +-#define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1]) + + static void sff8636_dom_parse(const struct sff8636_memory_map *map, + struct sff_diags *sd) +diff --git a/sff-common.h b/sff-common.h +index aab306e0b74f..9e323008ba19 100644 +--- a/sff-common.h ++++ b/sff-common.h +@@ -126,8 +126,8 @@ + #define SFF8024_ENCODING_PAM4 0x08 + + /* Most common case: 16-bit unsigned integer in a certain unit */ +-#define OFFSET_TO_U16(offset) \ +- (id[offset] << 8 | id[(offset) + 1]) ++#define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1]) ++#define OFFSET_TO_U16(offset) OFFSET_TO_U16_PTR(id, offset) + + # define PRINT_xX_PWR(string, var) \ + printf("\t%-41s : %.4f mW / %.2f dBm\n", (string), \ +-- +2.35.1 + diff --git a/SOURCES/0029-cmis-Initialize-Page-02h-in-memory-map.patch b/SOURCES/0029-cmis-Initialize-Page-02h-in-memory-map.patch new file mode 100644 index 0000000..dd5c145 --- /dev/null +++ b/SOURCES/0029-cmis-Initialize-Page-02h-in-memory-map.patch @@ -0,0 +1,59 @@ +From 86853162d53b47cd0f6bcb926810aa0fd68b8898 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 23 Nov 2021 19:40:57 +0200 +Subject: [PATCH 29/35] cmis: Initialize Page 02h in memory map + +Page 02h stores module and lane thresholds that are going to be parsed +and displayed in subsequent patches. + +Request it via the 'MODULE_EEPROM_GET' netlink message and initialize it +in the memory map. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/cmis.c b/cmis.c +index 4798fd4c7d68..55b9d1b959cd 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -17,9 +17,10 @@ + + struct cmis_memory_map { + const __u8 *lower_memory; +- const __u8 *upper_memory[1][2]; /* Bank, Page */ ++ const __u8 *upper_memory[1][3]; /* Bank, Page */ + #define page_00h upper_memory[0x0][0x0] + #define page_01h upper_memory[0x0][0x1] ++#define page_02h upper_memory[0x0][0x2] + }; + + #define CMIS_PAGE_SIZE 0x80 +@@ -423,8 +424,8 @@ cmis_memory_map_init_pages(struct cmd_context *ctx, + return ret; + map->page_00h = request.data - CMIS_PAGE_SIZE; + +- /* Page 01h is only present when the module memory model is paged and +- * not flat. ++ /* Pages 01h and 02h are only present when the module memory model is ++ * paged and not flat. + */ + if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & + CMIS_MEMORY_MODEL_MASK) +@@ -436,6 +437,12 @@ cmis_memory_map_init_pages(struct cmd_context *ctx, + return ret; + map->page_01h = request.data - CMIS_PAGE_SIZE; + ++ cmis_request_init(&request, 0, 0x2, CMIS_PAGE_SIZE); ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ map->page_02h = request.data - CMIS_PAGE_SIZE; ++ + return 0; + } + +-- +2.35.1 + diff --git a/SOURCES/0030-cmis-Initialize-Banked-Page-11h-in-memory-map.patch b/SOURCES/0030-cmis-Initialize-Banked-Page-11h-in-memory-map.patch new file mode 100644 index 0000000..54090bf --- /dev/null +++ b/SOURCES/0030-cmis-Initialize-Banked-Page-11h-in-memory-map.patch @@ -0,0 +1,122 @@ +From 21367ae2b8ebbe5173cbed22dfa51680a3fe48d2 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 23 Nov 2021 19:40:58 +0200 +Subject: [PATCH 30/35] cmis: Initialize Banked Page 11h in memory map + +Banked Page 11h stores, among other things, lane-specific flags and +monitors that are going to be parsed and displayed in subsequent +patches. + +Request it via the 'MODULE_EEPROM_GET' netlink message and initialize it +in the memory map. + +Only initialize it in supported Banks. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- + cmis.h | 7 +++++++ + 2 files changed, 54 insertions(+), 2 deletions(-) + +diff --git a/cmis.c b/cmis.c +index 55b9d1b959cd..83ced4d253ae 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -15,9 +15,17 @@ + #include "cmis.h" + #include "netlink/extapi.h" + ++/* The maximum number of supported Banks. Relevant documents: ++ * [1] CMIS Rev. 5, page. 128, section 8.4.4, Table 8-40 ++ */ ++#define CMIS_MAX_BANKS 4 ++ ++/* We are not parsing further than Page 11h. */ ++#define CMIS_MAX_PAGES 18 ++ + struct cmis_memory_map { + const __u8 *lower_memory; +- const __u8 *upper_memory[1][3]; /* Bank, Page */ ++ const __u8 *upper_memory[CMIS_MAX_BANKS][CMIS_MAX_PAGES]; + #define page_00h upper_memory[0x0][0x0] + #define page_01h upper_memory[0x0][0x1] + #define page_02h upper_memory[0x0][0x2] +@@ -399,12 +407,33 @@ static void cmis_request_init(struct ethtool_module_eeprom *request, u8 bank, + request->data = NULL; + } + ++static int cmis_num_banks_get(const struct cmis_memory_map *map, ++ int *p_num_banks) ++{ ++ switch (map->page_01h[CMIS_PAGES_ADVER_OFFSET] & ++ CMIS_BANKS_SUPPORTED_MASK) { ++ case CMIS_BANK_0_SUPPORTED: ++ *p_num_banks = 1; ++ break; ++ case CMIS_BANK_0_1_SUPPORTED: ++ *p_num_banks = 2; ++ break; ++ case CMIS_BANK_0_3_SUPPORTED: ++ *p_num_banks = 4; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + static int + cmis_memory_map_init_pages(struct cmd_context *ctx, + struct cmis_memory_map *map) + { + struct ethtool_module_eeprom request; +- int ret; ++ int num_banks, i, ret; + + /* Lower Memory and Page 00h are always present. + * +@@ -443,6 +472,22 @@ cmis_memory_map_init_pages(struct cmd_context *ctx, + return ret; + map->page_02h = request.data - CMIS_PAGE_SIZE; + ++ /* Bank 0 of Page 11h provides lane-specific registers for the first 8 ++ * lanes, and each additional Banks provides support for an additional ++ * 8 lanes. Only initialize supported Banks. ++ */ ++ ret = cmis_num_banks_get(map, &num_banks); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < num_banks; i++) { ++ cmis_request_init(&request, i, 0x11, CMIS_PAGE_SIZE); ++ ret = nl_get_eeprom_page(ctx, &request); ++ if (ret < 0) ++ return ret; ++ map->upper_memory[i][0x11] = request.data - CMIS_PAGE_SIZE; ++ } ++ + return 0; + } + +diff --git a/cmis.h b/cmis.h +index 911491dc5c8f..8d90a04756ad 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -114,6 +114,13 @@ + #define CMIS_WAVELENGTH_TOL_MSB 0x8C + #define CMIS_WAVELENGTH_TOL_LSB 0x8D + ++/* Supported Pages Advertising (Page 1) */ ++#define CMIS_PAGES_ADVER_OFFSET 0x8E ++#define CMIS_BANKS_SUPPORTED_MASK 0x03 ++#define CMIS_BANK_0_SUPPORTED 0x00 ++#define CMIS_BANK_0_1_SUPPORTED 0x01 ++#define CMIS_BANK_0_3_SUPPORTED 0x02 ++ + /* Signal integrity controls */ + #define CMIS_SIG_INTEG_TX_OFFSET 0xA1 + #define CMIS_SIG_INTEG_RX_OFFSET 0xA2 +-- +2.35.1 + diff --git a/SOURCES/0031-cmis-Parse-and-print-diagnostic-information.patch b/SOURCES/0031-cmis-Parse-and-print-diagnostic-information.patch new file mode 100644 index 0000000..c9d6293 --- /dev/null +++ b/SOURCES/0031-cmis-Parse-and-print-diagnostic-information.patch @@ -0,0 +1,631 @@ +From 086662c0b884c2b2e44ec472566d56c68a4330e0 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 23 Nov 2021 19:40:59 +0200 +Subject: [PATCH 31/35] cmis: Parse and print diagnostic information + +Like SFF-8636, CMIS has module-level monitors such as temperature and +voltage and channel-level monitors such as Tx optical power. + +These monitors have thresholds and flags that are set when the monitors +cross the thresholds. + +Print and parse these values in a similar fashion to SFF-8636. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 466 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- + cmis.h | 79 ++++++++++ + 2 files changed, 518 insertions(+), 27 deletions(-) + +diff --git a/cmis.c b/cmis.c +index 83ced4d253ae..d7b7097139b3 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -19,6 +19,8 @@ + * [1] CMIS Rev. 5, page. 128, section 8.4.4, Table 8-40 + */ + #define CMIS_MAX_BANKS 4 ++#define CMIS_CHANNELS_PER_BANK 8 ++#define CMIS_MAX_CHANNEL_NUM (CMIS_MAX_BANKS * CMIS_CHANNELS_PER_BANK) + + /* We are not parsing further than Page 11h. */ + #define CMIS_MAX_PAGES 18 +@@ -34,6 +36,80 @@ struct cmis_memory_map { + #define CMIS_PAGE_SIZE 0x80 + #define CMIS_I2C_ADDRESS 0x50 + ++static struct { ++ const char *str; ++ int offset; ++ __u8 value; /* Alarm is on if (offset & value) != 0. */ ++} cmis_aw_mod_flags[] = { ++ { "Module temperature high alarm", ++ CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HALARM_STATUS }, ++ { "Module temperature low alarm", ++ CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LALARM_STATUS }, ++ { "Module temperature high warning", ++ CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HWARN_STATUS }, ++ { "Module temperature low warning", ++ CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LWARN_STATUS }, ++ ++ { "Module voltage high alarm", ++ CMIS_VCC_AW_OFFSET, CMIS_VCC_HALARM_STATUS }, ++ { "Module voltage low alarm", ++ CMIS_VCC_AW_OFFSET, CMIS_VCC_LALARM_STATUS }, ++ { "Module voltage high warning", ++ CMIS_VCC_AW_OFFSET, CMIS_VCC_HWARN_STATUS }, ++ { "Module voltage low warning", ++ CMIS_VCC_AW_OFFSET, CMIS_VCC_LWARN_STATUS }, ++ ++ { NULL, 0, 0 }, ++}; ++ ++static struct { ++ const char *fmt_str; ++ int offset; ++ int adver_offset; /* In Page 01h. */ ++ __u8 adver_value; /* Supported if (offset & value) != 0. */ ++} cmis_aw_chan_flags[] = { ++ { "Laser bias current high alarm (Chan %d)", ++ CMIS_TX_BIAS_AW_HALARM_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, ++ { "Laser bias current low alarm (Chan %d)", ++ CMIS_TX_BIAS_AW_LALARM_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, ++ { "Laser bias current high warning (Chan %d)", ++ CMIS_TX_BIAS_AW_HWARN_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, ++ { "Laser bias current low warning (Chan %d)", ++ CMIS_TX_BIAS_AW_LWARN_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, ++ ++ { "Laser tx power high alarm (Channel %d)", ++ CMIS_TX_PWR_AW_HALARM_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, ++ { "Laser tx power low alarm (Channel %d)", ++ CMIS_TX_PWR_AW_LALARM_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, ++ { "Laser tx power high warning (Channel %d)", ++ CMIS_TX_PWR_AW_HWARN_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, ++ { "Laser tx power low warning (Channel %d)", ++ CMIS_TX_PWR_AW_LWARN_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, ++ ++ { "Laser rx power high alarm (Channel %d)", ++ CMIS_RX_PWR_AW_HALARM_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, ++ { "Laser rx power low alarm (Channel %d)", ++ CMIS_RX_PWR_AW_LALARM_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, ++ { "Laser rx power high warning (Channel %d)", ++ CMIS_RX_PWR_AW_HWARN_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, ++ { "Laser rx power low warning (Channel %d)", ++ CMIS_RX_PWR_AW_LWARN_OFFSET, ++ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, ++ ++ { NULL, 0, 0, 0 }, ++}; ++ + static void cmis_show_identifier(const struct cmis_memory_map *map) + { + sff8024_show_identifier(map->lower_memory, CMIS_ID_OFFSET); +@@ -277,32 +353,6 @@ static void cmis_show_mit_compliance(const struct cmis_memory_map *map) + } + } + +-/* +- * 2-byte internal temperature conversions: +- * First byte is a signed 8-bit integer, which is the temp decimal part +- * Second byte is a multiple of 1/256th of a degree, which is added to +- * the dec part. +- */ +-#define OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset)) +- +-/** +- * Print relevant module level monitoring values. Relevant documents: +- * [1] CMIS Rev. 3: +- * --> pag. 50, section 1.7.2.4, Table 22 +- * +- * [2] CMIS Rev. 4: +- * --> pag. 84, section 8.2.4, Table 8-6 +- */ +-static void cmis_show_mod_lvl_monitors(const struct cmis_memory_map *map) +-{ +- const __u8 *id = map->lower_memory; +- +- PRINT_TEMP("Module temperature", +- OFFSET_TO_TEMP(CMIS_CURR_TEMP_OFFSET)); +- PRINT_VCC("Module voltage", +- OFFSET_TO_U16(CMIS_CURR_VCC_OFFSET)); +-} +- + /** + * Print relevant info about the maximum supported fiber media length + * for each type of fiber media at the maximum module-supported bit rate. +@@ -352,6 +402,368 @@ static void cmis_show_vendor_info(const struct cmis_memory_map *map) + CMIS_CLEI_END_OFFSET, "CLEI code"); + } + ++static void cmis_parse_dom_power_type(const struct cmis_memory_map *map, ++ struct sff_diags *sd) ++{ ++ sd->rx_power_type = map->page_01h[CMIS_DIAG_TYPE_OFFSET] & ++ CMIS_RX_PWR_TYPE_MASK; ++ sd->tx_power_type = map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & ++ CMIS_TX_PWR_MON_MASK; ++} ++ ++static void cmis_parse_dom_mod_lvl_monitors(const struct cmis_memory_map *map, ++ struct sff_diags *sd) ++{ ++ sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(map->lower_memory, ++ CMIS_CURR_VCC_OFFSET); ++ sd->sfp_temp[MCURR] = (__s16)OFFSET_TO_U16_PTR(map->lower_memory, ++ CMIS_CURR_TEMP_OFFSET); ++} ++ ++static void cmis_parse_dom_mod_lvl_thresh(const struct cmis_memory_map *map, ++ struct sff_diags *sd) ++{ ++ /* Page is not present in IOCTL path. */ ++ if (!map->page_02h) ++ return; ++ sd->supports_alarms = 1; ++ ++ sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_VCC_HALRM_OFFSET); ++ sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_VCC_LALRM_OFFSET); ++ sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_VCC_HWARN_OFFSET); ++ sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_VCC_LWARN_OFFSET); ++ ++ sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TEMP_HALRM_OFFSET); ++ sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TEMP_LALRM_OFFSET); ++ sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TEMP_HWARN_OFFSET); ++ sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TEMP_LWARN_OFFSET); ++} ++ ++static __u8 cmis_tx_bias_mul(const struct cmis_memory_map *map) ++{ ++ switch (map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & ++ CMIS_TX_BIAS_MUL_MASK) { ++ case CMIS_TX_BIAS_MUL_1: ++ return 0; ++ case CMIS_TX_BIAS_MUL_2: ++ return 1; ++ case CMIS_TX_BIAS_MUL_4: ++ return 2; ++ } ++ ++ return 0; ++} ++ ++static void ++cmis_parse_dom_chan_lvl_monitors_bank(const struct cmis_memory_map *map, ++ struct sff_diags *sd, int bank) ++{ ++ const __u8 *page_11h = map->upper_memory[bank][0x11]; ++ int i; ++ ++ if (!page_11h) ++ return; ++ ++ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { ++ __u8 tx_bias_offset, rx_power_offset, tx_power_offset; ++ int chan = bank * CMIS_CHANNELS_PER_BANK + i; ++ __u8 bias_mul = cmis_tx_bias_mul(map); ++ ++ tx_bias_offset = CMIS_TX_BIAS_OFFSET + i * sizeof(__u16); ++ rx_power_offset = CMIS_RX_PWR_OFFSET + i * sizeof(__u16); ++ tx_power_offset = CMIS_TX_PWR_OFFSET + i * sizeof(__u16); ++ ++ sd->scd[chan].bias_cur = OFFSET_TO_U16_PTR(page_11h, ++ tx_bias_offset); ++ sd->scd[chan].bias_cur >>= bias_mul; ++ sd->scd[chan].rx_power = OFFSET_TO_U16_PTR(page_11h, ++ rx_power_offset); ++ sd->scd[chan].tx_power = OFFSET_TO_U16_PTR(page_11h, ++ tx_power_offset); ++ } ++} ++ ++static void cmis_parse_dom_chan_lvl_monitors(const struct cmis_memory_map *map, ++ struct sff_diags *sd) ++{ ++ int i; ++ ++ for (i = 0; i < CMIS_MAX_BANKS; i++) ++ cmis_parse_dom_chan_lvl_monitors_bank(map, sd, i); ++} ++ ++static void cmis_parse_dom_chan_lvl_thresh(const struct cmis_memory_map *map, ++ struct sff_diags *sd) ++{ ++ __u8 bias_mul = cmis_tx_bias_mul(map); ++ ++ if (!map->page_02h) ++ return; ++ ++ sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TX_BIAS_HALRM_OFFSET); ++ sd->bias_cur[HALRM] >>= bias_mul; ++ sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TX_BIAS_LALRM_OFFSET); ++ sd->bias_cur[LALRM] >>= bias_mul; ++ sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TX_BIAS_HWARN_OFFSET); ++ sd->bias_cur[HWARN] >>= bias_mul; ++ sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TX_BIAS_LWARN_OFFSET); ++ sd->bias_cur[LWARN] >>= bias_mul; ++ ++ sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TX_PWR_HALRM_OFFSET); ++ sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TX_PWR_LALRM_OFFSET); ++ sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TX_PWR_HWARN_OFFSET); ++ sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_TX_PWR_LWARN_OFFSET); ++ ++ sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_RX_PWR_HALRM_OFFSET); ++ sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_RX_PWR_LALRM_OFFSET); ++ sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_RX_PWR_HWARN_OFFSET); ++ sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, ++ CMIS_RX_PWR_LWARN_OFFSET); ++} ++ ++static void cmis_parse_dom(const struct cmis_memory_map *map, ++ struct sff_diags *sd) ++{ ++ cmis_parse_dom_power_type(map, sd); ++ cmis_parse_dom_mod_lvl_monitors(map, sd); ++ cmis_parse_dom_mod_lvl_thresh(map, sd); ++ cmis_parse_dom_chan_lvl_monitors(map, sd); ++ cmis_parse_dom_chan_lvl_thresh(map, sd); ++} ++ ++/* Print module-level monitoring values. Relevant documents: ++ * [1] CMIS Rev. 5, page 110, section 8.2.5, Table 8-9 ++ */ ++static void cmis_show_dom_mod_lvl_monitors(const struct sff_diags *sd) ++{ ++ PRINT_TEMP("Module temperature", sd->sfp_temp[MCURR]); ++ PRINT_VCC("Module voltage", sd->sfp_voltage[MCURR]); ++} ++ ++/* Print channel Tx laser bias current. Relevant documents: ++ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 ++ */ ++static void ++cmis_show_dom_chan_lvl_tx_bias_bank(const struct cmis_memory_map *map, ++ const struct sff_diags *sd, int bank) ++{ ++ const __u8 *page_11h = map->upper_memory[bank][0x11]; ++ int i; ++ ++ if (!page_11h) ++ return; ++ ++ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { ++ int chan = bank * CMIS_CHANNELS_PER_BANK + i; ++ char fmt_str[80]; ++ ++ snprintf(fmt_str, 80, "%s (Channel %d)", ++ "Laser tx bias current", chan + 1); ++ PRINT_BIAS(fmt_str, sd->scd[chan].bias_cur); ++ } ++} ++ ++static void cmis_show_dom_chan_lvl_tx_bias(const struct cmis_memory_map *map, ++ const struct sff_diags *sd) ++{ ++ int i; ++ ++ if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & ++ CMIS_TX_BIAS_MON_MASK)) ++ return; ++ ++ for (i = 0; i < CMIS_MAX_BANKS; i++) ++ cmis_show_dom_chan_lvl_tx_bias_bank(map, sd, i); ++} ++ ++/* Print channel Tx average optical power. Relevant documents: ++ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 ++ */ ++static void ++cmis_show_dom_chan_lvl_tx_power_bank(const struct cmis_memory_map *map, ++ const struct sff_diags *sd, int bank) ++{ ++ const __u8 *page_11h = map->upper_memory[bank][0x11]; ++ int i; ++ ++ if (!page_11h) ++ return; ++ ++ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { ++ int chan = bank * CMIS_CHANNELS_PER_BANK + i; ++ char fmt_str[80]; ++ ++ snprintf(fmt_str, 80, "%s (Channel %d)", ++ "Transmit avg optical power", chan + 1); ++ PRINT_xX_PWR(fmt_str, sd->scd[chan].tx_power); ++ } ++} ++ ++static void cmis_show_dom_chan_lvl_tx_power(const struct cmis_memory_map *map, ++ const struct sff_diags *sd) ++{ ++ int i; ++ ++ if (!sd->tx_power_type) ++ return; ++ ++ for (i = 0; i < CMIS_MAX_BANKS; i++) ++ cmis_show_dom_chan_lvl_tx_power_bank(map, sd, i); ++} ++ ++/* Print channel Rx input optical power. Relevant documents: ++ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 ++ */ ++static void ++cmis_show_dom_chan_lvl_rx_power_bank(const struct cmis_memory_map *map, ++ const struct sff_diags *sd, int bank) ++{ ++ const __u8 *page_11h = map->upper_memory[bank][0x11]; ++ int i; ++ ++ if (!page_11h) ++ return; ++ ++ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { ++ int chan = bank * CMIS_CHANNELS_PER_BANK + i; ++ char *rx_power_str; ++ char fmt_str[80]; ++ ++ if (!sd->rx_power_type) ++ rx_power_str = "Receiver signal OMA"; ++ else ++ rx_power_str = "Rcvr signal avg optical power"; ++ ++ snprintf(fmt_str, 80, "%s (Channel %d)", rx_power_str, ++ chan + 1); ++ PRINT_xX_PWR(fmt_str, sd->scd[chan].rx_power); ++ } ++} ++ ++static void cmis_show_dom_chan_lvl_rx_power(const struct cmis_memory_map *map, ++ const struct sff_diags *sd) ++{ ++ int i; ++ ++ if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & CMIS_RX_PWR_MON_MASK)) ++ return; ++ ++ for (i = 0; i < CMIS_MAX_BANKS; i++) ++ cmis_show_dom_chan_lvl_rx_power_bank(map, sd, i); ++} ++ ++static void cmis_show_dom_chan_lvl_monitors(const struct cmis_memory_map *map, ++ const struct sff_diags *sd) ++{ ++ cmis_show_dom_chan_lvl_tx_bias(map, sd); ++ cmis_show_dom_chan_lvl_tx_power(map, sd); ++ cmis_show_dom_chan_lvl_rx_power(map, sd); ++} ++ ++/* Print module-level flags. Relevant documents: ++ * [1] CMIS Rev. 5, page 109, section 8.2.4, Table 8-8 ++ */ ++static void cmis_show_dom_mod_lvl_flags(const struct cmis_memory_map *map) ++{ ++ int i; ++ ++ for (i = 0; cmis_aw_mod_flags[i].str; i++) { ++ printf("\t%-41s : %s\n", cmis_aw_mod_flags[i].str, ++ map->lower_memory[cmis_aw_mod_flags[i].offset] & ++ cmis_aw_mod_flags[i].value ? "On" : "Off"); ++ } ++} ++ ++/* Print channel-level flags. Relevant documents: ++ * [1] CMIS Rev. 5, page 162, section 8.9.3, Table 8-77 ++ * [1] CMIS Rev. 5, page 164, section 8.9.3, Table 8-78 ++ */ ++static void cmis_show_dom_chan_lvl_flags_chan(const struct cmis_memory_map *map, ++ int bank, int chan) ++{ ++ const __u8 *page_11h = map->upper_memory[bank][0x11]; ++ int i; ++ ++ for (i = 0; cmis_aw_chan_flags[i].fmt_str; i++) { ++ char str[80]; ++ ++ if (!(map->page_01h[cmis_aw_chan_flags[i].adver_offset] & ++ cmis_aw_chan_flags[i].adver_value)) ++ continue; ++ ++ snprintf(str, 80, cmis_aw_chan_flags[i].fmt_str, chan + 1); ++ printf("\t%-41s : %s\n", str, ++ page_11h[cmis_aw_chan_flags[i].offset] & chan ? ++ "On" : "Off"); ++ } ++} ++ ++static void ++cmis_show_dom_chan_lvl_flags_bank(const struct cmis_memory_map *map, ++ int bank) ++{ ++ const __u8 *page_11h = map->upper_memory[bank][0x11]; ++ int i; ++ ++ if (!page_11h) ++ return; ++ ++ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { ++ int chan = bank * CMIS_CHANNELS_PER_BANK + i; ++ ++ cmis_show_dom_chan_lvl_flags_chan(map, bank, chan); ++ } ++} ++ ++static void cmis_show_dom_chan_lvl_flags(const struct cmis_memory_map *map) ++{ ++ int i; ++ ++ for (i = 0; i < CMIS_MAX_BANKS; i++) ++ cmis_show_dom_chan_lvl_flags_bank(map, i); ++} ++ ++ ++static void cmis_show_dom(const struct cmis_memory_map *map) ++{ ++ struct sff_diags sd = {}; ++ ++ /* Diagnostic information is only relevant when the module memory ++ * model is paged and not flat. ++ */ ++ if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & ++ CMIS_MEMORY_MODEL_MASK) ++ return; ++ ++ cmis_parse_dom(map, &sd); ++ ++ cmis_show_dom_mod_lvl_monitors(&sd); ++ cmis_show_dom_chan_lvl_monitors(map, &sd); ++ cmis_show_dom_mod_lvl_flags(map); ++ cmis_show_dom_chan_lvl_flags(map); ++ if (sd.supports_alarms) ++ sff_show_thresholds(sd); ++} ++ + static void cmis_show_all_common(const struct cmis_memory_map *map) + { + cmis_show_identifier(map); +@@ -360,10 +772,10 @@ static void cmis_show_all_common(const struct cmis_memory_map *map) + cmis_show_cbl_asm_len(map); + cmis_show_sig_integrity(map); + cmis_show_mit_compliance(map); +- cmis_show_mod_lvl_monitors(map); + cmis_show_link_len(map); + cmis_show_vendor_info(map); + cmis_show_rev_compliance(map); ++ cmis_show_dom(map); + } + + static void cmis_memory_map_init_buf(struct cmis_memory_map *map, +diff --git a/cmis.h b/cmis.h +index 8d90a04756ad..310697b0ef32 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -7,6 +7,18 @@ + #define CMIS_MEMORY_MODEL_OFFSET 0x02 + #define CMIS_MEMORY_MODEL_MASK 0x80 + ++/* Module Flags (Page 0) */ ++#define CMIS_VCC_AW_OFFSET 0x09 ++#define CMIS_VCC_LWARN_STATUS 0x80 ++#define CMIS_VCC_HWARN_STATUS 0x40 ++#define CMIS_VCC_LALARM_STATUS 0x20 ++#define CMIS_VCC_HALARM_STATUS 0x10 ++#define CMIS_TEMP_AW_OFFSET 0x09 ++#define CMIS_TEMP_LWARN_STATUS 0x08 ++#define CMIS_TEMP_HWARN_STATUS 0x04 ++#define CMIS_TEMP_LALARM_STATUS 0x02 ++#define CMIS_TEMP_HALARM_STATUS 0x01 ++ + #define CMIS_MODULE_TYPE_OFFSET 0x55 + #define CMIS_MT_MMF 0x01 + #define CMIS_MT_SMF 0x02 +@@ -121,10 +133,77 @@ + #define CMIS_BANK_0_1_SUPPORTED 0x01 + #define CMIS_BANK_0_3_SUPPORTED 0x02 + ++/* Module Characteristics Advertising (Page 1) */ ++#define CMIS_DIAG_TYPE_OFFSET 0x97 ++#define CMIS_RX_PWR_TYPE_MASK 0x10 ++ ++/* Supported Monitors Advertisement (Page 1) */ ++#define CMIS_DIAG_CHAN_ADVER_OFFSET 0xA0 ++#define CMIS_TX_BIAS_MON_MASK 0x01 ++#define CMIS_TX_PWR_MON_MASK 0x02 ++#define CMIS_RX_PWR_MON_MASK 0x04 ++#define CMIS_TX_BIAS_MUL_MASK 0x18 ++#define CMIS_TX_BIAS_MUL_1 0x00 ++#define CMIS_TX_BIAS_MUL_2 0x08 ++#define CMIS_TX_BIAS_MUL_4 0x10 ++ + /* Signal integrity controls */ + #define CMIS_SIG_INTEG_TX_OFFSET 0xA1 + #define CMIS_SIG_INTEG_RX_OFFSET 0xA2 + ++/*----------------------------------------------------------------------- ++ * Upper Memory Page 0x02: Optional Page that informs about module-defined ++ * thresholds for module-level and lane-specific threshold crossing monitors. ++ */ ++ ++/* Module-Level Monitor Thresholds (Page 2) */ ++#define CMIS_TEMP_HALRM_OFFSET 0x80 ++#define CMIS_TEMP_LALRM_OFFSET 0x82 ++#define CMIS_TEMP_HWARN_OFFSET 0x84 ++#define CMIS_TEMP_LWARN_OFFSET 0x86 ++#define CMIS_VCC_HALRM_OFFSET 0x88 ++#define CMIS_VCC_LALRM_OFFSET 0x8A ++#define CMIS_VCC_HWARN_OFFSET 0x8C ++#define CMIS_VCC_LWARN_OFFSET 0x8E ++ ++/* Lane-Related Monitor Thresholds (Page 2) */ ++#define CMIS_TX_PWR_HALRM_OFFSET 0xB0 ++#define CMIS_TX_PWR_LALRM_OFFSET 0xB2 ++#define CMIS_TX_PWR_HWARN_OFFSET 0xB4 ++#define CMIS_TX_PWR_LWARN_OFFSET 0xB6 ++#define CMIS_TX_BIAS_HALRM_OFFSET 0xB8 ++#define CMIS_TX_BIAS_LALRM_OFFSET 0xBA ++#define CMIS_TX_BIAS_HWARN_OFFSET 0xBC ++#define CMIS_TX_BIAS_LWARN_OFFSET 0xBE ++#define CMIS_RX_PWR_HALRM_OFFSET 0xC0 ++#define CMIS_RX_PWR_LALRM_OFFSET 0xC2 ++#define CMIS_RX_PWR_HWARN_OFFSET 0xC4 ++#define CMIS_RX_PWR_LWARN_OFFSET 0xC6 ++ ++/*----------------------------------------------------------------------- ++ * Upper Memory Page 0x11: Optional Page that contains lane dynamic status ++ * bytes. ++ */ ++ ++/* Media Lane-Specific Flags (Page 0x11) */ ++#define CMIS_TX_PWR_AW_HALARM_OFFSET 0x8B ++#define CMIS_TX_PWR_AW_LALARM_OFFSET 0x8C ++#define CMIS_TX_PWR_AW_HWARN_OFFSET 0x8D ++#define CMIS_TX_PWR_AW_LWARN_OFFSET 0x8E ++#define CMIS_TX_BIAS_AW_HALARM_OFFSET 0x8F ++#define CMIS_TX_BIAS_AW_LALARM_OFFSET 0x90 ++#define CMIS_TX_BIAS_AW_HWARN_OFFSET 0x91 ++#define CMIS_TX_BIAS_AW_LWARN_OFFSET 0x92 ++#define CMIS_RX_PWR_AW_HALARM_OFFSET 0x95 ++#define CMIS_RX_PWR_AW_LALARM_OFFSET 0x96 ++#define CMIS_RX_PWR_AW_HWARN_OFFSET 0x97 ++#define CMIS_RX_PWR_AW_LWARN_OFFSET 0x98 ++ ++/* Media Lane-Specific Monitors (Page 0x11) */ ++#define CMIS_TX_PWR_OFFSET 0x9A ++#define CMIS_TX_BIAS_OFFSET 0xAA ++#define CMIS_RX_PWR_OFFSET 0xBA ++ + #define YESNO(x) (((x) != 0) ? "Yes" : "No") + #define ONOFF(x) (((x) != 0) ? "On" : "Off") + +-- +2.35.1 + diff --git a/SOURCES/0032-cmis-Print-Module-State-and-Fault-Cause.patch b/SOURCES/0032-cmis-Print-Module-State-and-Fault-Cause.patch new file mode 100644 index 0000000..87b279d --- /dev/null +++ b/SOURCES/0032-cmis-Print-Module-State-and-Fault-Cause.patch @@ -0,0 +1,155 @@ +From 4a86034f138e1a96e54047b8036b0a4425e99944 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 23 Nov 2021 19:41:00 +0200 +Subject: [PATCH 32/35] cmis: Print Module State and Fault Cause + +Print the CMIS Module State when dumping EEPROM contents via the '-m' +option. It can be used, for example, to test module power mode settings. + +Example output: + + # ethtool -m swp11 + Identifier : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628)) + ... + Module State : 0x03 (ModuleReady) + + # ethtool --set-module swp11 power-mode-policy auto + + # ethtool -m swp11 + Identifier : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628)) + ... + Module State : 0x01 (ModuleLowPwr) + +In case the module is in fault state, print the CMIS Module Fault Cause. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + cmis.h | 16 ++++++++++++++ + 2 files changed, 86 insertions(+) + +diff --git a/cmis.c b/cmis.c +index d7b7097139b3..a32cc9f8b1f6 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -402,6 +402,74 @@ static void cmis_show_vendor_info(const struct cmis_memory_map *map) + CMIS_CLEI_END_OFFSET, "CLEI code"); + } + ++/* Print the current Module State. Relevant documents: ++ * [1] CMIS Rev. 5, pag. 57, section 6.3.2.2, Figure 6-3 ++ * [2] CMIS Rev. 5, pag. 60, section 6.3.2.3, Figure 6-4 ++ * [3] CMIS Rev. 5, pag. 107, section 8.2.2, Table 8-6 ++ */ ++static void cmis_show_mod_state(const struct cmis_memory_map *map) ++{ ++ __u8 mod_state; ++ ++ mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] & ++ CMIS_MODULE_STATE_MASK) >> 1; ++ printf("\t%-41s : 0x%02x", "Module State", mod_state); ++ switch (mod_state) { ++ case CMIS_MODULE_STATE_MODULE_LOW_PWR: ++ printf(" (ModuleLowPwr)\n"); ++ break; ++ case CMIS_MODULE_STATE_MODULE_PWR_UP: ++ printf(" (ModulePwrUp)\n"); ++ break; ++ case CMIS_MODULE_STATE_MODULE_READY: ++ printf(" (ModuleReady)\n"); ++ break; ++ case CMIS_MODULE_STATE_MODULE_PWR_DN: ++ printf(" (ModulePwrDn)\n"); ++ break; ++ case CMIS_MODULE_STATE_MODULE_FAULT: ++ printf(" (ModuleFault)\n"); ++ break; ++ default: ++ printf(" (reserved or unknown)\n"); ++ break; ++ } ++} ++ ++/* Print the Module Fault Information. Relevant documents: ++ * [1] CMIS Rev. 5, pag. 64, section 6.3.2.12 ++ * [2] CMIS Rev. 5, pag. 115, section 8.2.10, Table 8-15 ++ */ ++static void cmis_show_mod_fault_cause(const struct cmis_memory_map *map) ++{ ++ __u8 mod_state, fault_cause; ++ ++ mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] & ++ CMIS_MODULE_STATE_MASK) >> 1; ++ if (mod_state != CMIS_MODULE_STATE_MODULE_FAULT) ++ return; ++ ++ fault_cause = map->lower_memory[CMIS_MODULE_FAULT_OFFSET]; ++ printf("\t%-41s : 0x%02x", "Module Fault Cause", fault_cause); ++ switch (fault_cause) { ++ case CMIS_MODULE_FAULT_NO_FAULT: ++ printf(" (No fault detected / not supported)\n"); ++ break; ++ case CMIS_MODULE_FAULT_TEC_RUNAWAY: ++ printf(" (TEC runaway)\n"); ++ break; ++ case CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED: ++ printf(" (Data memory corrupted)\n"); ++ break; ++ case CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED: ++ printf(" (Program memory corrupted)\n"); ++ break; ++ default: ++ printf(" (reserved or unknown)\n"); ++ break; ++ } ++} ++ + static void cmis_parse_dom_power_type(const struct cmis_memory_map *map, + struct sff_diags *sd) + { +@@ -775,6 +843,8 @@ static void cmis_show_all_common(const struct cmis_memory_map *map) + cmis_show_link_len(map); + cmis_show_vendor_info(map); + cmis_show_rev_compliance(map); ++ cmis_show_mod_state(map); ++ cmis_show_mod_fault_cause(map); + cmis_show_dom(map); + } + +diff --git a/cmis.h b/cmis.h +index 310697b0ef32..2c67ad5640ab 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -7,6 +7,15 @@ + #define CMIS_MEMORY_MODEL_OFFSET 0x02 + #define CMIS_MEMORY_MODEL_MASK 0x80 + ++/* Global Status Information (Page 0) */ ++#define CMIS_MODULE_STATE_OFFSET 0x03 ++#define CMIS_MODULE_STATE_MASK 0x0E ++#define CMIS_MODULE_STATE_MODULE_LOW_PWR 0x01 ++#define CMIS_MODULE_STATE_MODULE_PWR_UP 0x02 ++#define CMIS_MODULE_STATE_MODULE_READY 0x03 ++#define CMIS_MODULE_STATE_MODULE_PWR_DN 0x04 ++#define CMIS_MODULE_STATE_MODULE_FAULT 0x05 ++ + /* Module Flags (Page 0) */ + #define CMIS_VCC_AW_OFFSET 0x09 + #define CMIS_VCC_LWARN_STATUS 0x80 +@@ -27,6 +36,13 @@ + #define CMIS_CURR_TEMP_OFFSET 0x0E + #define CMIS_CURR_VCC_OFFSET 0x10 + ++/* Module Fault Information (Page 0) */ ++#define CMIS_MODULE_FAULT_OFFSET 0x29 ++#define CMIS_MODULE_FAULT_NO_FAULT 0x00 ++#define CMIS_MODULE_FAULT_TEC_RUNAWAY 0x01 ++#define CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED 0x02 ++#define CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED 0x03 ++ + #define CMIS_CTOR_OFFSET 0xCB + + /* Vendor related information (Page 0) */ +-- +2.35.1 + diff --git a/SOURCES/0033-cmis-Print-Module-Level-Controls.patch b/SOURCES/0033-cmis-Print-Module-Level-Controls.patch new file mode 100644 index 0000000..f41a573 --- /dev/null +++ b/SOURCES/0033-cmis-Print-Module-Level-Controls.patch @@ -0,0 +1,196 @@ +From 0b45f392c6c92e9823d6332622d7a45e7a36365e Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 23 Nov 2021 19:41:01 +0200 +Subject: [PATCH 33/35] cmis: Print Module-Level Controls + +Print the CMIS Module-Level Controls when dumping EEPROM contents via +the '-m' option. It can be used to understand low power mode enforcement +by the host. + +Example output: + + # ethtool -m swp11 + Identifier : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628)) + ... + Module State : 0x03 (ModuleReady) + LowPwrAllowRequestHW : Off + LowPwrRequestSW : Off + ... + Transmit avg optical power (Channel 1) : 1.3222 mW / 1.21 dBm + Transmit avg optical power (Channel 2) : 1.2666 mW / 1.03 dBm + Transmit avg optical power (Channel 3) : 1.2860 mW / 1.09 dBm + Transmit avg optical power (Channel 4) : 1.2988 mW / 1.14 dBm + Transmit avg optical power (Channel 5) : 1.2828 mW / 1.08 dBm + Transmit avg optical power (Channel 6) : 1.2913 mW / 1.11 dBm + Transmit avg optical power (Channel 7) : 1.2636 mW / 1.02 dBm + Transmit avg optical power (Channel 8) : 1.3408 mW / 1.27 dBm + Transmit avg optical power (Channel 9) : 1.3222 mW / 1.21 dBm + Transmit avg optical power (Channel 10) : 1.2666 mW / 1.03 dBm + Transmit avg optical power (Channel 11) : 1.2860 mW / 1.09 dBm + Transmit avg optical power (Channel 12) : 1.2988 mW / 1.14 dBm + Transmit avg optical power (Channel 13) : 1.2828 mW / 1.08 dBm + Transmit avg optical power (Channel 14) : 1.2913 mW / 1.11 dBm + Transmit avg optical power (Channel 15) : 1.2636 mW / 1.02 dBm + Transmit avg optical power (Channel 16) : 1.3408 mW / 1.27 dBm + Rcvr signal avg optical power (Channel 1) : 1.1351 mW / 0.55 dBm + Rcvr signal avg optical power (Channel 2) : 1.1603 mW / 0.65 dBm + Rcvr signal avg optical power (Channel 3) : 1.1529 mW / 0.62 dBm + Rcvr signal avg optical power (Channel 4) : 1.1670 mW / 0.67 dBm + Rcvr signal avg optical power (Channel 5) : 1.1759 mW / 0.70 dBm + Rcvr signal avg optical power (Channel 6) : 1.1744 mW / 0.70 dBm + Rcvr signal avg optical power (Channel 7) : 1.1188 mW / 0.49 dBm + Rcvr signal avg optical power (Channel 8) : 1.1640 mW / 0.66 dBm + Rcvr signal avg optical power (Channel 9) : 1.1351 mW / 0.55 dBm + Rcvr signal avg optical power (Channel 10) : 1.1603 mW / 0.65 dBm + Rcvr signal avg optical power (Channel 11) : 1.1529 mW / 0.62 dBm + Rcvr signal avg optical power (Channel 12) : 1.1670 mW / 0.67 dBm + Rcvr signal avg optical power (Channel 13) : 1.1759 mW / 0.70 dBm + Rcvr signal avg optical power (Channel 14) : 1.1744 mW / 0.70 dBm + Rcvr signal avg optical power (Channel 15) : 1.1188 mW / 0.49 dBm + Rcvr signal avg optical power (Channel 16) : 1.1640 mW / 0.66 dBm + + # ethtool --set-module swp11 power-mode-policy auto + + # ethtool -m swp11 + Identifier : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628)) + ... + Module State : 0x01 (ModuleLowPwr) + LowPwrAllowRequestHW : Off + LowPwrRequestSW : On + ... + Transmit avg optical power (Channel 1) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 2) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 3) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 4) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 5) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 6) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 7) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 8) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 9) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 10) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 11) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 12) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 13) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 14) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 15) : 0.0001 mW / -40.00 dBm + Transmit avg optical power (Channel 16) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 1) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 2) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 3) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 4) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 5) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 6) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 7) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 8) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 9) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 10) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 11) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 12) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 13) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 14) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 15) : 0.0001 mW / -40.00 dBm + Rcvr signal avg optical power (Channel 16) : 0.0001 mW / -40.00 dBm + + # ethtool --set-module swp11 power-mode-policy high + + # ethtool -m swp11 + Identifier : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628)) + ... + Module State : 0x03 (ModuleReady) + LowPwrAllowRequestHW : Off + LowPwrRequestSW : Off + ... + Transmit avg optical power (Channel 1) : 1.3690 mW / 1.36 dBm + Transmit avg optical power (Channel 2) : 1.3036 mW / 1.15 dBm + Transmit avg optical power (Channel 3) : 1.3358 mW / 1.26 dBm + Transmit avg optical power (Channel 4) : 1.3509 mW / 1.31 dBm + Transmit avg optical power (Channel 5) : 1.3193 mW / 1.20 dBm + Transmit avg optical power (Channel 6) : 1.3314 mW / 1.24 dBm + Transmit avg optical power (Channel 7) : 1.3042 mW / 1.15 dBm + Transmit avg optical power (Channel 8) : 1.3919 mW / 1.44 dBm + Transmit avg optical power (Channel 9) : 1.3690 mW / 1.36 dBm + Transmit avg optical power (Channel 10) : 1.3036 mW / 1.15 dBm + Transmit avg optical power (Channel 11) : 1.3358 mW / 1.26 dBm + Transmit avg optical power (Channel 12) : 1.3509 mW / 1.31 dBm + Transmit avg optical power (Channel 13) : 1.3193 mW / 1.20 dBm + Transmit avg optical power (Channel 14) : 1.3314 mW / 1.24 dBm + Transmit avg optical power (Channel 15) : 1.3042 mW / 1.15 dBm + Transmit avg optical power (Channel 16) : 1.3919 mW / 1.44 dBm + Rcvr signal avg optical power (Channel 1) : 1.1299 mW / 0.53 dBm + Rcvr signal avg optical power (Channel 2) : 1.1566 mW / 0.63 dBm + Rcvr signal avg optical power (Channel 3) : 1.1484 mW / 0.60 dBm + Rcvr signal avg optical power (Channel 4) : 1.1655 mW / 0.67 dBm + Rcvr signal avg optical power (Channel 5) : 1.1751 mW / 0.70 dBm + Rcvr signal avg optical power (Channel 6) : 1.1595 mW / 0.64 dBm + Rcvr signal avg optical power (Channel 7) : 1.1158 mW / 0.48 dBm + Rcvr signal avg optical power (Channel 8) : 1.1595 mW / 0.64 dBm + Rcvr signal avg optical power (Channel 9) : 1.1299 mW / 0.53 dBm + Rcvr signal avg optical power (Channel 10) : 1.1566 mW / 0.63 dBm + Rcvr signal avg optical power (Channel 11) : 1.1484 mW / 0.60 dBm + Rcvr signal avg optical power (Channel 12) : 1.1655 mW / 0.67 dBm + Rcvr signal avg optical power (Channel 13) : 1.1751 mW / 0.70 dBm + Rcvr signal avg optical power (Channel 14) : 1.1595 mW / 0.64 dBm + Rcvr signal avg optical power (Channel 15) : 1.1158 mW / 0.48 dBm + Rcvr signal avg optical power (Channel 16) : 1.1595 mW / 0.64 dBm + +In the above example, the LowPwrRequestHW signal is ignored and low +power mode is controlled via software only. + +Signed-off-by: Ido Schimmel +--- + cmis.c | 15 +++++++++++++++ + cmis.h | 5 +++++ + 2 files changed, 20 insertions(+) + +diff --git a/cmis.c b/cmis.c +index a32cc9f8b1f6..d0b62728e998 100644 +--- a/cmis.c ++++ b/cmis.c +@@ -470,6 +470,20 @@ static void cmis_show_mod_fault_cause(const struct cmis_memory_map *map) + } + } + ++/* Print the current Module-Level Controls. Relevant documents: ++ * [1] CMIS Rev. 5, pag. 58, section 6.3.2.2, Table 6-12 ++ * [2] CMIS Rev. 5, pag. 111, section 8.2.6, Table 8-10 ++ */ ++static void cmis_show_mod_lvl_controls(const struct cmis_memory_map *map) ++{ ++ printf("\t%-41s : ", "LowPwrAllowRequestHW"); ++ printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] & ++ CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK)); ++ printf("\t%-41s : ", "LowPwrRequestSW"); ++ printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] & ++ CMIS_LOW_PWR_REQUEST_SW_MASK)); ++} ++ + static void cmis_parse_dom_power_type(const struct cmis_memory_map *map, + struct sff_diags *sd) + { +@@ -845,6 +859,7 @@ static void cmis_show_all_common(const struct cmis_memory_map *map) + cmis_show_rev_compliance(map); + cmis_show_mod_state(map); + cmis_show_mod_fault_cause(map); ++ cmis_show_mod_lvl_controls(map); + cmis_show_dom(map); + } + +diff --git a/cmis.h b/cmis.h +index 2c67ad5640ab..46797081f13c 100644 +--- a/cmis.h ++++ b/cmis.h +@@ -36,6 +36,11 @@ + #define CMIS_CURR_TEMP_OFFSET 0x0E + #define CMIS_CURR_VCC_OFFSET 0x10 + ++/* Module Global Controls (Page 0) */ ++#define CMIS_MODULE_CONTROL_OFFSET 0x1A ++#define CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK 0x40 ++#define CMIS_LOW_PWR_REQUEST_SW_MASK 0x10 ++ + /* Module Fault Information (Page 0) */ + #define CMIS_MODULE_FAULT_OFFSET 0x29 + #define CMIS_MODULE_FAULT_NO_FAULT 0x00 +-- +2.35.1 + diff --git a/SOURCES/0034-sff-8636-Print-Power-set-and-Power-override-bits.patch b/SOURCES/0034-sff-8636-Print-Power-set-and-Power-override-bits.patch new file mode 100644 index 0000000..38b7f4c --- /dev/null +++ b/SOURCES/0034-sff-8636-Print-Power-set-and-Power-override-bits.patch @@ -0,0 +1,108 @@ +From 48391fa16592b47d37ef63466111c751a10c3e56 Mon Sep 17 00:00:00 2001 +From: Ido Schimmel +Date: Tue, 23 Nov 2021 19:41:02 +0200 +Subject: [PATCH 34/35] sff-8636: Print Power set and Power override bits + +Print the SFF-8636 Power set and Power override bits when dumping EEPROM +contents via the '-m' option. They can be used to understand low power +mode enforcement by the host. + +The 'SFF8636_LOW_PWR_MODE' define is renamed to 'SFF8636_LOW_PWR_SET' to +reflect its naming in the standard for QSFP+/QSFP28. + +Example output: + + # ethtool -m swp13 + Identifier : 0x11 (QSFP28) + ... + Extended identifier description : 5.0W max. Power consumption, High Power Class (> 3.5 W) enabled + Power set : Off + Power override : On + ... + Transmit avg optical power (Channel 1) : 0.7633 mW / -1.17 dBm + Transmit avg optical power (Channel 2) : 0.7649 mW / -1.16 dBm + Transmit avg optical power (Channel 3) : 0.7696 mW / -1.14 dBm + Transmit avg optical power (Channel 4) : 0.7739 mW / -1.11 dBm + Rcvr signal avg optical power(Channel 1) : 0.9240 mW / -0.34 dBm + Rcvr signal avg optical power(Channel 2) : 0.9129 mW / -0.40 dBm + Rcvr signal avg optical power(Channel 3) : 0.9194 mW / -0.36 dBm + Rcvr signal avg optical power(Channel 4) : 0.8708 mW / -0.60 dBm + + # ethtool --set-module swp13 power-mode-policy auto + + # ethtool -m swp13 + Identifier : 0x11 (QSFP28) + ... + Extended identifier description : 5.0W max. Power consumption, High Power Class (> 3.5 W) not enabled + Power set : On + Power override : On + ... + Transmit avg optical power (Channel 1) : 0.0000 mW / -inf dBm + Transmit avg optical power (Channel 2) : 0.0000 mW / -inf dBm + Transmit avg optical power (Channel 3) : 0.0000 mW / -inf dBm + Transmit avg optical power (Channel 4) : 0.0000 mW / -inf dBm + Rcvr signal avg optical power(Channel 1) : 0.0000 mW / -inf dBm + Rcvr signal avg optical power(Channel 2) : 0.0000 mW / -inf dBm + Rcvr signal avg optical power(Channel 3) : 0.0000 mW / -inf dBm + Rcvr signal avg optical power(Channel 4) : 0.0000 mW / -inf dBm + + # ethtool --set-module swp13 power-mode-policy high + + # ethtool -m swp13 + Identifier : 0x11 (QSFP28) + ... + Extended identifier description : 5.0W max. Power consumption, High Power Class (> 3.5 W) enabled + Power set : Off + Power override : On + ... + Transmit avg optical power (Channel 1) : 0.7733 mW / -1.12 dBm + Transmit avg optical power (Channel 2) : 0.7754 mW / -1.10 dBm + Transmit avg optical power (Channel 3) : 0.7885 mW / -1.03 dBm + Transmit avg optical power (Channel 4) : 0.7886 mW / -1.03 dBm + Rcvr signal avg optical power(Channel 1) : 0.9248 mW / -0.34 dBm + Rcvr signal avg optical power(Channel 2) : 0.9129 mW / -0.40 dBm + Rcvr signal avg optical power(Channel 3) : 0.9187 mW / -0.37 dBm + Rcvr signal avg optical power(Channel 4) : 0.8785 mW / -0.56 dBm + +In the above example, the LPMode signal is ignored (Power override is +always on) and low power mode is controlled via software only. + +Signed-off-by: Ido Schimmel +--- + qsfp.c | 6 ++++++ + qsfp.h | 2 +- + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/qsfp.c b/qsfp.c +index b3c9e1516af9..57aac86bd5f6 100644 +--- a/qsfp.c ++++ b/qsfp.c +@@ -268,6 +268,12 @@ static void sff8636_show_ext_identifier(const struct sff8636_memory_map *map) + printf(" High Power Class (> 3.5 W) enabled\n"); + else + printf(" High Power Class (> 3.5 W) not enabled\n"); ++ printf("\t%-41s : ", "Power set"); ++ printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] & ++ SFF8636_LOW_PWR_SET)); ++ printf("\t%-41s : ", "Power override"); ++ printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] & ++ SFF8636_PWR_OVERRIDE)); + } + + static void sff8636_show_connector(const struct sff8636_memory_map *map) +diff --git a/qsfp.h b/qsfp.h +index 1d8f24b5cbc2..aabf09fdc623 100644 +--- a/qsfp.h ++++ b/qsfp.h +@@ -180,7 +180,7 @@ + + #define SFF8636_PWR_MODE_OFFSET 0x5D + #define SFF8636_HIGH_PWR_ENABLE (1 << 2) +-#define SFF8636_LOW_PWR_MODE (1 << 1) ++#define SFF8636_LOW_PWR_SET (1 << 1) + #define SFF8636_PWR_OVERRIDE (1 << 0) + + #define SFF8636_TX_APP_SELECT_4_OFFSET 0x5E +-- +2.35.1 + diff --git a/SOURCES/0035-sff-8079-8472-Fix-missing-sff-8472-output-in-netlink.patch b/SOURCES/0035-sff-8079-8472-Fix-missing-sff-8472-output-in-netlink.patch new file mode 100644 index 0000000..667a5d4 --- /dev/null +++ b/SOURCES/0035-sff-8079-8472-Fix-missing-sff-8472-output-in-netlink.patch @@ -0,0 +1,107 @@ +From 206cd00caf9b71ae20f897075b4bd261e923e563 Mon Sep 17 00:00:00 2001 +From: Ivan Vecera +Date: Tue, 31 May 2022 20:55:03 +0200 +Subject: [PATCH 35/35] sff-8079/8472: Fix missing sff-8472 output in netlink + path + +Commit 25b64c66f58d ("ethtool: Add netlink handler for +getmodule (-m)") provided a netlink variant for getmodule +but also introduced a regression as netlink output is different +from ioctl output that provides information from A2h page +via sff8472_show_all(). + +To fix this the netlink path should check a presence of A2h page +by value of bit 6 in byte 92 of page A0h and if it is set then +get A2h page and call sff8472_show_all(). + +Fixes: 25b64c66f58d ("ethtool: Add netlink handler for getmodule (-m)") +Tested-by: Daniel Juarez +Tested-by: Ido Schimmel +Reviewed-by: Ido Schimmel +Co-authored-by: Ido Schimmel +Signed-off-by: Ivan Vecera +--- + sfpid.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 46 insertions(+), 8 deletions(-) + +diff --git a/sfpid.c b/sfpid.c +index 621d1e86c278..1bc45c183770 100644 +--- a/sfpid.c ++++ b/sfpid.c +@@ -13,8 +13,9 @@ + #include "sff-common.h" + #include "netlink/extapi.h" + +-#define SFF8079_PAGE_SIZE 0x80 +-#define SFF8079_I2C_ADDRESS_LOW 0x50 ++#define SFF8079_PAGE_SIZE 0x80 ++#define SFF8079_I2C_ADDRESS_LOW 0x50 ++#define SFF8079_I2C_ADDRESS_HIGH 0x51 + + static void sff8079_show_identifier(const __u8 *id) + { +@@ -450,18 +451,55 @@ void sff8079_show_all_ioctl(const __u8 *id) + sff8079_show_all_common(id); + } + +-int sff8079_show_all_nl(struct cmd_context *ctx) ++static int sff8079_get_eeprom_page(struct cmd_context *ctx, u8 i2c_address, ++ __u8 *buf) + { + struct ethtool_module_eeprom request = { + .length = SFF8079_PAGE_SIZE, +- .i2c_address = SFF8079_I2C_ADDRESS_LOW, ++ .i2c_address = i2c_address, + }; + int ret; + + ret = nl_get_eeprom_page(ctx, &request); +- if (ret < 0) +- return ret; +- sff8079_show_all_common(request.data); ++ if (!ret) ++ memcpy(buf, request.data, SFF8079_PAGE_SIZE); ++ ++ return ret; ++} ++ ++int sff8079_show_all_nl(struct cmd_context *ctx) ++{ ++ u8 *buf; ++ int ret; ++ ++ /* The SFF-8472 parser expects a single buffer that contains the ++ * concatenation of the first 256 bytes from addresses A0h and A2h, ++ * respectively. ++ */ ++ buf = calloc(1, ETH_MODULE_SFF_8472_LEN); ++ if (!buf) ++ return -ENOMEM; ++ ++ /* Read A0h page */ ++ ret = sff8079_get_eeprom_page(ctx, SFF8079_I2C_ADDRESS_LOW, buf); ++ if (ret) ++ goto out; ++ ++ sff8079_show_all_common(buf); ++ ++ /* Finish if A2h page is not present */ ++ if (!(buf[92] & (1 << 6))) ++ goto out; ++ ++ /* Read A2h page */ ++ ret = sff8079_get_eeprom_page(ctx, SFF8079_I2C_ADDRESS_HIGH, ++ buf + ETH_MODULE_SFF_8079_LEN); ++ if (ret) ++ goto out; ++ ++ sff8472_show_all(buf); ++out: ++ free(buf); + +- return 0; ++ return ret; + } +-- +2.35.1 + diff --git a/SPECS/ethtool.spec b/SPECS/ethtool.spec index aa6e62c..3174f49 100644 --- a/SPECS/ethtool.spec +++ b/SPECS/ethtool.spec @@ -1,7 +1,7 @@ Name: ethtool Epoch: 2 Version: 5.13 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Settings tool for Ethernet NICs License: GPLv2 Group: Applications/System @@ -10,6 +10,42 @@ Source0: https://www.kernel.org/pub/software/network/%{name}/%{name}-%{version}. BuildRequires: libmnl-devel Conflicts: filesystem < 3 +Patch1: 0001-sff-8636-Fix-parsing-of-Page-03h-in-IOCTL-path.patch +Patch2: 0002-cmis-Fix-invalid-memory-access-in-IOCTL-path.patch +Patch3: 0003-netlink-eeprom-Fallback-to-IOCTL-when-a-complete-hex.patch +Patch4: 0004-ethtool-Fix-compilation-warning-when-pretty-dump-is-.patch +Patch5: 0005-netlink-eeprom-Fix-compilation-when-pretty-dump-is-d.patch +Patch6: 0006-cmis-Fix-CLEI-code-parsing.patch +Patch7: 0007-cmis-Fix-wrong-define-name.patch +Patch8: 0008-cmis-Correct-comment.patch +Patch9: 0009-sff-8636-Remove-incorrect-comment.patch +Patch10: 0010-sff-8636-Fix-incorrect-function-name.patch +Patch11: 0011-sff-8636-Convert-if-statement-to-switch-case.patch +Patch12: 0012-sff-8636-Remove-extra-blank-lines.patch +Patch13: 0013-cmis-Rename-CMIS-parsing-functions.patch +Patch14: 0014-cmis-Initialize-CMIS-memory-map.patch +Patch15: 0015-cmis-Use-memory-map-during-parsing.patch +Patch16: 0016-cmis-Consolidate-code-between-IOCTL-and-netlink-path.patch +Patch17: 0017-sff-8636-Rename-SFF-8636-parsing-functions.patch +Patch18: 0018-sff-8636-Initialize-SFF-8636-memory-map.patch +Patch19: 0019-sff-8636-Use-memory-map-during-parsing.patch +Patch20: 0020-sff-8636-Consolidate-code-between-IOCTL-and-netlink-.patch +Patch21: 0021-sff-8079-Split-SFF-8079-parsing-function.patch +Patch22: 0022-netlink-eeprom-Export-a-function-to-request-an-EEPRO.patch +Patch23: 0023-cmis-Request-specific-pages-for-parsing-in-netlink-p.patch +Patch24: 0024-sff-8636-Request-specific-pages-for-parsing-in-netli.patch +Patch25: 0025-sff-8079-Request-specific-pages-for-parsing-in-netli.patch +Patch26: 0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch +Patch27: 0027-sff-8636-Use-an-SFF-8636-specific-define-for-maximum.patch +Patch28: 0028-sff-common-Move-OFFSET_TO_U16_PTR-to-common-header-f.patch +Patch29: 0029-cmis-Initialize-Page-02h-in-memory-map.patch +Patch30: 0030-cmis-Initialize-Banked-Page-11h-in-memory-map.patch +Patch31: 0031-cmis-Parse-and-print-diagnostic-information.patch +Patch32: 0032-cmis-Print-Module-State-and-Fault-Cause.patch +Patch33: 0033-cmis-Print-Module-Level-Controls.patch +Patch34: 0034-sff-8636-Print-Power-set-and-Power-override-bits.patch +Patch35: 0035-sff-8079-8472-Fix-missing-sff-8472-output-in-netlink.patch + %description This utility allows querying and changing settings such as speed, port, auto-negotiation, PCI locations and checksum offload on many @@ -17,6 +53,41 @@ network devices, especially of Ethernet devices. %prep %setup -q +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 %build %configure @@ -33,6 +104,9 @@ make DESTDIR=%{buildroot} INSTALL='install -p' install %{_datadir}/bash-completion/completions/ethtool %changelog +* Thu Jun 02 2022 Ivan Vecera - 2:5.13-2 +- Module (SFP/QSFP/CMIS) related bugfixes + * Thu Nov 18 2021 Ivan Vecera - 2:5.13-1 - Updated to upstream v5.13