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