Blame SOURCES/0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch

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