1f2507
From b6005ecf2ce2aaeb86995fa50df97e7384f46f99 Mon Sep 17 00:00:00 2001
1f2507
From: Ido Schimmel <idosch@nvidia.com>
1f2507
Date: Tue, 12 Oct 2021 16:25:25 +0300
1f2507
Subject: [PATCH 26/35] netlink: eeprom: Defer page requests to individual
1f2507
 parsers
1f2507
1f2507
The individual EEPROM parsers (e.g., CMIS, SFF-8636) now request the
1f2507
EEPROM pages they intend to parse and populate their memory maps before
1f2507
parsing them.
1f2507
1f2507
Therefore, there is no need for the common netlink code to request
1f2507
potentially invalid pages and pass them as blobs to these parsers.
1f2507
1f2507
Instead, only query the SFF-8024 Identifier Value which is located at
1f2507
I2C address 0x50, byte 0 and dispatch to the relevant EEPROM parser.
1f2507
1f2507
Tested by making sure that the output of 'ethtool -m' does not change
1f2507
for SFF-8079, SFF-8636 and CMIS before and after the patch.
1f2507
1f2507
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
1f2507
---
1f2507
 netlink/module-eeprom.c | 347 +++++++---------------------------------
1f2507
 1 file changed, 59 insertions(+), 288 deletions(-)
1f2507
1f2507
diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
1f2507
index 6d76b8a96461..f359aeec4ddf 100644
1f2507
--- a/netlink/module-eeprom.c
1f2507
+++ b/netlink/module-eeprom.c
1f2507
@@ -19,7 +19,6 @@
1f2507
 #include "parser.h"
1f2507
 
1f2507
 #define ETH_I2C_ADDRESS_LOW	0x50
1f2507
-#define ETH_I2C_ADDRESS_HIGH	0x51
1f2507
 #define ETH_I2C_MAX_ADDRESS	0x7F
1f2507
 
1f2507
 struct cmd_params {
1f2507
@@ -78,267 +77,6 @@ static const struct param_parser getmodule_params[] = {
1f2507
 	{}
1f2507
 };
1f2507
 
1f2507
-struct page_entry {
1f2507
-	struct list_head link;
1f2507
-	struct ethtool_module_eeprom *page;
1f2507
-};
1f2507
-
1f2507
-static struct list_head page_list = LIST_HEAD_INIT(page_list);
1f2507
-
1f2507
-static int cache_add(struct ethtool_module_eeprom *page)
1f2507
-{
1f2507
-	struct page_entry *list_element;
1f2507
-
1f2507
-	if (!page)
1f2507
-		return -1;
1f2507
-	list_element = malloc(sizeof(*list_element));
1f2507
-	if (!list_element)
1f2507
-		return -ENOMEM;
1f2507
-	list_element->page = page;
1f2507
-
1f2507
-	list_add(&list_element->link, &page_list);
1f2507
-	return 0;
1f2507
-}
1f2507
-
1f2507
-static void page_free(struct ethtool_module_eeprom *page)
1f2507
-{
1f2507
-	free(page->data);
1f2507
-	free(page);
1f2507
-}
1f2507
-
1f2507
-static void cache_del(struct ethtool_module_eeprom *page)
1f2507
-{
1f2507
-	struct ethtool_module_eeprom *entry;
1f2507
-	struct list_head *head, *next;
1f2507
-
1f2507
-	list_for_each_safe(head, next, &page_list) {
1f2507
-		entry = ((struct page_entry *)head)->page;
1f2507
-		if (entry == page) {
1f2507
-			list_del(head);
1f2507
-			free(head);
1f2507
-			page_free(entry);
1f2507
-			break;
1f2507
-		}
1f2507
-	}
1f2507
-}
1f2507
-
1f2507
-static void cache_free(void)
1f2507
-{
1f2507
-	struct ethtool_module_eeprom *entry;
1f2507
-	struct list_head *head, *next;
1f2507
-
1f2507
-	list_for_each_safe(head, next, &page_list) {
1f2507
-		entry = ((struct page_entry *)head)->page;
1f2507
-		list_del(head);
1f2507
-		free(head);
1f2507
-		page_free(entry);
1f2507
-	}
1f2507
-}
1f2507
-
1f2507
-static struct ethtool_module_eeprom *page_join(struct ethtool_module_eeprom *page_a,
1f2507
-					       struct ethtool_module_eeprom *page_b)
1f2507
-{
1f2507
-	struct ethtool_module_eeprom *joined_page;
1f2507
-	u32 total_length;
1f2507
-
1f2507
-	if (!page_a || !page_b ||
1f2507
-	    page_a->page != page_b->page ||
1f2507
-	    page_a->bank != page_b->bank ||
1f2507
-	    page_a->i2c_address != page_b->i2c_address)
1f2507
-		return NULL;
1f2507
-
1f2507
-	total_length = page_a->length + page_b->length;
1f2507
-	joined_page = calloc(1, sizeof(*joined_page));
1f2507
-	joined_page->data = calloc(1, total_length);
1f2507
-	joined_page->page = page_a->page;
1f2507
-	joined_page->bank = page_a->bank;
1f2507
-	joined_page->length = total_length;
1f2507
-	joined_page->i2c_address = page_a->i2c_address;
1f2507
-
1f2507
-	if (page_a->offset < page_b->offset) {
1f2507
-		memcpy(joined_page->data, page_a->data, page_a->length);
1f2507
-		memcpy(joined_page->data + page_a->length, page_b->data, page_b->length);
1f2507
-		joined_page->offset = page_a->offset;
1f2507
-	} else {
1f2507
-		memcpy(joined_page->data, page_b->data, page_b->length);
1f2507
-		memcpy(joined_page->data + page_b->length, page_a->data, page_a->length);
1f2507
-		joined_page->offset = page_b->offset;
1f2507
-	}
1f2507
-
1f2507
-	return joined_page;
1f2507
-}
1f2507
-
1f2507
-static struct ethtool_module_eeprom *cache_get(u32 page, u32 bank, u8 i2c_address)
1f2507
-{
1f2507
-	struct ethtool_module_eeprom *entry;
1f2507
-	struct list_head *head, *next;
1f2507
-
1f2507
-	list_for_each_safe(head, next, &page_list) {
1f2507
-		entry = ((struct page_entry *)head)->page;
1f2507
-		if (entry->page == page && entry->bank == bank &&
1f2507
-		    entry->i2c_address == i2c_address)
1f2507
-			return entry;
1f2507
-	}
1f2507
-
1f2507
-	return NULL;
1f2507
-}
1f2507
-
1f2507
-static int getmodule_page_fetch_reply_cb(const struct nlmsghdr *nlhdr,
1f2507
-					 void *data)
1f2507
-{
1f2507
-	const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {};
1f2507
-	DECLARE_ATTR_TB_INFO(tb);
1f2507
-	struct ethtool_module_eeprom *lower_page;
1f2507
-	struct ethtool_module_eeprom *response;
1f2507
-	struct ethtool_module_eeprom *request;
1f2507
-	struct ethtool_module_eeprom *joined;
1f2507
-	u8 *eeprom_data;
1f2507
-	int ret;
1f2507
-
1f2507
-	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
1f2507
-	if (ret < 0)
1f2507
-		return ret;
1f2507
-
1f2507
-	if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA]) {
1f2507
-		fprintf(stderr, "Malformed netlink message (getmodule)\n");
1f2507
-		return MNL_CB_ERROR;
1f2507
-	}
1f2507
-
1f2507
-	response = calloc(1, sizeof(*response));
1f2507
-	if (!response)
1f2507
-		return -ENOMEM;
1f2507
-
1f2507
-	request = (struct ethtool_module_eeprom *)data;
1f2507
-	response->offset = request->offset;
1f2507
-	response->page = request->page;
1f2507
-	response->bank = request->bank;
1f2507
-	response->i2c_address = request->i2c_address;
1f2507
-	response->length = mnl_attr_get_payload_len(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
1f2507
-	eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
1f2507
-
1f2507
-	response->data = malloc(response->length);
1f2507
-	if (!response->data) {
1f2507
-		free(response);
1f2507
-		return -ENOMEM;
1f2507
-	}
1f2507
-	memcpy(response->data, eeprom_data, response->length);
1f2507
-
1f2507
-	if (!request->page) {
1f2507
-		lower_page = cache_get(request->page, request->bank, response->i2c_address);
1f2507
-		if (lower_page) {
1f2507
-			joined = page_join(lower_page, response);
1f2507
-			page_free(response);
1f2507
-			cache_del(lower_page);
1f2507
-			return cache_add(joined);
1f2507
-		}
1f2507
-	}
1f2507
-
1f2507
-	return cache_add(response);
1f2507
-}
1f2507
-
1f2507
-static int page_fetch(struct nl_context *nlctx, const struct ethtool_module_eeprom *request)
1f2507
-{
1f2507
-	struct nl_socket *nlsock = nlctx->ethnl_socket;
1f2507
-	struct nl_msg_buff *msg = &nlsock->msgbuff;
1f2507
-	struct ethtool_module_eeprom *page;
1f2507
-	int ret;
1f2507
-
1f2507
-	if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS)
1f2507
-		return -EINVAL;
1f2507
-
1f2507
-	/* Satisfy request right away, if region is already in cache */
1f2507
-	page = cache_get(request->page, request->bank, request->i2c_address);
1f2507
-	if (page && page->offset <= request->offset &&
1f2507
-	    page->offset + page->length >= request->offset + request->length) {
1f2507
-		return 0;
1f2507
-	}
1f2507
-
1f2507
-	ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET,
1f2507
-				      ETHTOOL_A_MODULE_EEPROM_HEADER, 0);
1f2507
-	if (ret < 0)
1f2507
-		return ret;
1f2507
-
1f2507
-	if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH, request->length) ||
1f2507
-	    ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET, request->offset) ||
1f2507
-	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE, request->page) ||
1f2507
-	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK, request->bank) ||
1f2507
-	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, request->i2c_address))
1f2507
-		return -EMSGSIZE;
1f2507
-
1f2507
-	ret = nlsock_sendmsg(nlsock, NULL);
1f2507
-	if (ret < 0)
1f2507
-		return ret;
1f2507
-	ret = nlsock_process_reply(nlsock, getmodule_page_fetch_reply_cb, (void *)request);
1f2507
-	if (ret < 0)
1f2507
-		return ret;
1f2507
-
1f2507
-	return nlsock_process_reply(nlsock, nomsg_reply_cb, NULL);
1f2507
-}
1f2507
-
1f2507
-#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1f2507
-static int decoder_prefetch(struct nl_context *nlctx)
1f2507
-{
1f2507
-	struct ethtool_module_eeprom *page_zero_lower = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
1f2507
-	struct ethtool_module_eeprom request = {0};
1f2507
-	u8 module_id = page_zero_lower->data[0];
1f2507
-	int err = 0;
1f2507
-
1f2507
-	/* Fetch rest of page 00 */
1f2507
-	request.i2c_address = ETH_I2C_ADDRESS_LOW;
1f2507
-	request.offset = 128;
1f2507
-	request.length = 128;
1f2507
-	err = page_fetch(nlctx, &request);
1f2507
-	if (err)
1f2507
-		return err;
1f2507
-
1f2507
-	switch (module_id) {
1f2507
-	case SFF8024_ID_QSFP:
1f2507
-	case SFF8024_ID_QSFP28:
1f2507
-	case SFF8024_ID_QSFP_PLUS:
1f2507
-		memset(&request, 0, sizeof(request));
1f2507
-		request.i2c_address = ETH_I2C_ADDRESS_LOW;
1f2507
-		request.offset = 128;
1f2507
-		request.length = 128;
1f2507
-		request.page = 3;
1f2507
-		break;
1f2507
-	case SFF8024_ID_QSFP_DD:
1f2507
-	case SFF8024_ID_DSFP:
1f2507
-		memset(&request, 0, sizeof(request));
1f2507
-		request.i2c_address = ETH_I2C_ADDRESS_LOW;
1f2507
-		request.offset = 128;
1f2507
-		request.length = 128;
1f2507
-		request.page = 1;
1f2507
-		break;
1f2507
-	}
1f2507
-
1f2507
-	return page_fetch(nlctx, &request);
1f2507
-}
1f2507
-
1f2507
-static void decoder_print(struct cmd_context *ctx)
1f2507
-{
1f2507
-	struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
1f2507
-	u8 module_id = page_zero->data[SFF8636_ID_OFFSET];
1f2507
-
1f2507
-	switch (module_id) {
1f2507
-	case SFF8024_ID_SFP:
1f2507
-		sff8079_show_all_nl(ctx);
1f2507
-		break;
1f2507
-	case SFF8024_ID_QSFP:
1f2507
-	case SFF8024_ID_QSFP28:
1f2507
-	case SFF8024_ID_QSFP_PLUS:
1f2507
-		sff8636_show_all_nl(ctx);
1f2507
-		break;
1f2507
-	case SFF8024_ID_QSFP_DD:
1f2507
-	case SFF8024_ID_DSFP:
1f2507
-		cmis_show_all_nl(ctx);
1f2507
-		break;
1f2507
-	default:
1f2507
-		dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset);
1f2507
-		break;
1f2507
-	}
1f2507
-}
1f2507
-#endif
1f2507
-
1f2507
 static struct list_head eeprom_page_list = LIST_HEAD_INIT(eeprom_page_list);
1f2507
 
1f2507
 struct eeprom_page_entry {
1f2507
@@ -443,14 +181,64 @@ int nl_get_eeprom_page(struct cmd_context *ctx,
1f2507
 				    (void *)request);
1f2507
 }
1f2507
 
1f2507
+static int eeprom_dump_hex(struct cmd_context *ctx)
1f2507
+{
1f2507
+	struct ethtool_module_eeprom request = {
1f2507
+		.length = 128,
1f2507
+		.i2c_address = ETH_I2C_ADDRESS_LOW,
1f2507
+	};
1f2507
+	int ret;
1f2507
+
1f2507
+	ret = nl_get_eeprom_page(ctx, &request);
1f2507
+	if (ret < 0)
1f2507
+		return ret;
1f2507
+
1f2507
+	dump_hex(stdout, request.data, request.length, request.offset);
1f2507
+
1f2507
+	return 0;
1f2507
+}
1f2507
+
1f2507
+static int eeprom_parse(struct cmd_context *ctx)
1f2507
+{
1f2507
+	struct ethtool_module_eeprom request = {
1f2507
+		.length = 1,
1f2507
+		.i2c_address = ETH_I2C_ADDRESS_LOW,
1f2507
+	};
1f2507
+	int ret;
1f2507
+
1f2507
+	/* Fetch the SFF-8024 Identifier Value. For all supported standards, it
1f2507
+	 * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024,
1f2507
+	 * revision 4.9.
1f2507
+	 */
1f2507
+	ret = nl_get_eeprom_page(ctx, &request);
1f2507
+	if (ret < 0)
1f2507
+		return ret;
1f2507
+
1f2507
+	switch (request.data[0]) {
1f2507
+#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1f2507
+	case SFF8024_ID_SFP:
1f2507
+		return sff8079_show_all_nl(ctx);
1f2507
+	case SFF8024_ID_QSFP:
1f2507
+	case SFF8024_ID_QSFP28:
1f2507
+	case SFF8024_ID_QSFP_PLUS:
1f2507
+		return sff8636_show_all_nl(ctx);
1f2507
+	case SFF8024_ID_QSFP_DD:
1f2507
+	case SFF8024_ID_DSFP:
1f2507
+		return cmis_show_all_nl(ctx);
1f2507
+#endif
1f2507
+	default:
1f2507
+		/* If we cannot recognize the memory map, default to dumping
1f2507
+		 * the first 128 bytes in hex.
1f2507
+		 */
1f2507
+		return eeprom_dump_hex(ctx);
1f2507
+	}
1f2507
+}
1f2507
+
1f2507
 int nl_getmodule(struct cmd_context *ctx)
1f2507
 {
1f2507
 	struct cmd_params getmodule_cmd_params = {};
1f2507
 	struct ethtool_module_eeprom request = {0};
1f2507
-	struct ethtool_module_eeprom *reply_page;
1f2507
 	struct nl_context *nlctx = ctx->nlctx;
1f2507
-	u32 dump_length;
1f2507
-	u8 *eeprom_data;
1f2507
 	int ret;
1f2507
 
1f2507
 	if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_EEPROM_GET, false))
1f2507
@@ -479,12 +267,6 @@ int nl_getmodule(struct cmd_context *ctx)
1f2507
 		return -EOPNOTSUPP;
1f2507
 	}
1f2507
 
1f2507
-	request.i2c_address = ETH_I2C_ADDRESS_LOW;
1f2507
-	request.length = 128;
1f2507
-	ret = page_fetch(nlctx, &request);
1f2507
-	if (ret)
1f2507
-		goto cleanup;
1f2507
-
1f2507
 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1f2507
 	if (getmodule_cmd_params.page || getmodule_cmd_params.bank ||
1f2507
 	    getmodule_cmd_params.offset || getmodule_cmd_params.length)
1f2507
@@ -501,33 +283,22 @@ int nl_getmodule(struct cmd_context *ctx)
1f2507
 		request.offset = 128;
1f2507
 
1f2507
 	if (getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) {
1f2507
-		ret = page_fetch(nlctx, &request);
1f2507
+		ret = nl_get_eeprom_page(ctx, &request);
1f2507
 		if (ret < 0)
1f2507
 			goto cleanup;
1f2507
-		reply_page = cache_get(request.page, request.bank, request.i2c_address);
1f2507
-		if (!reply_page) {
1f2507
-			ret = -EINVAL;
1f2507
-			goto cleanup;
1f2507
-		}
1f2507
 
1f2507
-		eeprom_data = reply_page->data + (request.offset - reply_page->offset);
1f2507
-		dump_length = reply_page->length < request.length ? reply_page->length
1f2507
-								  : request.length;
1f2507
 		if (getmodule_cmd_params.dump_raw)
1f2507
-			fwrite(eeprom_data, 1, request.length, stdout);
1f2507
+			fwrite(request.data, 1, request.length, stdout);
1f2507
 		else
1f2507
-			dump_hex(stdout, eeprom_data, dump_length, request.offset);
1f2507
+			dump_hex(stdout, request.data, request.length,
1f2507
+				 request.offset);
1f2507
 	} else {
1f2507
-#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1f2507
-		ret = decoder_prefetch(nlctx);
1f2507
-		if (ret)
1f2507
+		ret = eeprom_parse(ctx);
1f2507
+		if (ret < 0)
1f2507
 			goto cleanup;
1f2507
-		decoder_print(ctx);
1f2507
-#endif
1f2507
 	}
1f2507
 
1f2507
 cleanup:
1f2507
 	eeprom_page_list_flush();
1f2507
-	cache_free();
1f2507
 	return ret;
1f2507
 }
1f2507
-- 
1f2507
2.35.1
1f2507