Thomas Bogendoerfer f8bbdb
From: Jeroen de Borst <jeroendb@google.com>
Thomas Bogendoerfer f8bbdb
Date: Thu, 17 Nov 2022 08:27:00 -0800
Thomas Bogendoerfer f8bbdb
Subject: gve: Adding a new AdminQ command to verify driver
Thomas Bogendoerfer f8bbdb
Patch-mainline: v6.2-rc1
Thomas Bogendoerfer f8bbdb
Git-commit: c2a0c3ed5b64750a41cec052e40cb377b5c4b9bc
Thomas Bogendoerfer f8bbdb
References: bsc#1211519
Thomas Bogendoerfer f8bbdb
Thomas Bogendoerfer f8bbdb
Check whether the driver is compatible with the device
Thomas Bogendoerfer f8bbdb
presented.
Thomas Bogendoerfer f8bbdb
Thomas Bogendoerfer f8bbdb
Signed-off-by: Jeroen de Borst <jeroendb@google.com>
Thomas Bogendoerfer f8bbdb
Reviewed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Thomas Bogendoerfer f8bbdb
Signed-off-by: David S. Miller <davem@davemloft.net>
Thomas Bogendoerfer f8bbdb
Acked-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
Thomas Bogendoerfer f8bbdb
---
Thomas Bogendoerfer f8bbdb
 drivers/net/ethernet/google/gve/gve.h        |    1 
Thomas Bogendoerfer f8bbdb
 drivers/net/ethernet/google/gve/gve_adminq.c |   21 ++++++++++
Thomas Bogendoerfer f8bbdb
 drivers/net/ethernet/google/gve/gve_adminq.h |   49 +++++++++++++++++++++++++
Thomas Bogendoerfer f8bbdb
 drivers/net/ethernet/google/gve/gve_main.c   |   52 +++++++++++++++++++++++++++
Thomas Bogendoerfer f8bbdb
 4 files changed, 122 insertions(+), 1 deletion(-)
Thomas Bogendoerfer f8bbdb
Thomas Bogendoerfer f8bbdb
--- a/drivers/net/ethernet/google/gve/gve.h
Thomas Bogendoerfer f8bbdb
+++ b/drivers/net/ethernet/google/gve/gve.h
Thomas Bogendoerfer f8bbdb
@@ -563,6 +563,7 @@ struct gve_priv {
Thomas Bogendoerfer f8bbdb
 	u32 adminq_report_stats_cnt;
Thomas Bogendoerfer f8bbdb
 	u32 adminq_report_link_speed_cnt;
Thomas Bogendoerfer f8bbdb
 	u32 adminq_get_ptype_map_cnt;
Thomas Bogendoerfer f8bbdb
+	u32 adminq_verify_driver_compatibility_cnt;
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
 	/* Global stats */
Thomas Bogendoerfer f8bbdb
 	u32 interface_up_cnt; /* count of times interface turned up since last reset */
Thomas Bogendoerfer f8bbdb
--- a/drivers/net/ethernet/google/gve/gve_adminq.c
Thomas Bogendoerfer f8bbdb
+++ b/drivers/net/ethernet/google/gve/gve_adminq.c
Thomas Bogendoerfer f8bbdb
@@ -289,7 +289,7 @@ static int gve_adminq_parse_err(struct g
Thomas Bogendoerfer f8bbdb
 	case GVE_ADMINQ_COMMAND_ERROR_RESOURCE_EXHAUSTED:
Thomas Bogendoerfer f8bbdb
 		return -ENOMEM;
Thomas Bogendoerfer f8bbdb
 	case GVE_ADMINQ_COMMAND_ERROR_UNIMPLEMENTED:
Thomas Bogendoerfer f8bbdb
-		return -ENOTSUPP;
Thomas Bogendoerfer f8bbdb
+		return -EOPNOTSUPP;
Thomas Bogendoerfer f8bbdb
 	default:
Thomas Bogendoerfer f8bbdb
 		dev_err(&priv->pdev->dev, "parse_aq_err: unknown status code %d\n", status);
Thomas Bogendoerfer f8bbdb
 		return -EINVAL;
Thomas Bogendoerfer f8bbdb
@@ -407,6 +407,9 @@ static int gve_adminq_issue_cmd(struct g
Thomas Bogendoerfer f8bbdb
 	case GVE_ADMINQ_GET_PTYPE_MAP:
Thomas Bogendoerfer f8bbdb
 		priv->adminq_get_ptype_map_cnt++;
Thomas Bogendoerfer f8bbdb
 		break;
Thomas Bogendoerfer f8bbdb
+	case GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY:
Thomas Bogendoerfer f8bbdb
+		priv->adminq_verify_driver_compatibility_cnt++;
Thomas Bogendoerfer f8bbdb
+		break;
Thomas Bogendoerfer f8bbdb
 	default:
Thomas Bogendoerfer f8bbdb
 		dev_err(&priv->pdev->dev, "unknown AQ command opcode %d\n", opcode);
Thomas Bogendoerfer f8bbdb
 	}
Thomas Bogendoerfer f8bbdb
@@ -876,6 +879,22 @@ int gve_adminq_report_stats(struct gve_p
Thomas Bogendoerfer f8bbdb
 	};
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
 	return gve_adminq_execute_cmd(priv, &cmd);
Thomas Bogendoerfer f8bbdb
+}
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+int gve_adminq_verify_driver_compatibility(struct gve_priv *priv,
Thomas Bogendoerfer f8bbdb
+					   u64 driver_info_len,
Thomas Bogendoerfer f8bbdb
+					   dma_addr_t driver_info_addr)
Thomas Bogendoerfer f8bbdb
+{
Thomas Bogendoerfer f8bbdb
+	union gve_adminq_command cmd;
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+	memset(&cmd, 0, sizeof(cmd));
Thomas Bogendoerfer f8bbdb
+	cmd.opcode = cpu_to_be32(GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY);
Thomas Bogendoerfer f8bbdb
+	cmd.verify_driver_compatibility = (struct gve_adminq_verify_driver_compatibility) {
Thomas Bogendoerfer f8bbdb
+		.driver_info_len = cpu_to_be64(driver_info_len),
Thomas Bogendoerfer f8bbdb
+		.driver_info_addr = cpu_to_be64(driver_info_addr),
Thomas Bogendoerfer f8bbdb
+	};
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+	return gve_adminq_execute_cmd(priv, &cmd);
Thomas Bogendoerfer f8bbdb
 }
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
 int gve_adminq_report_link_speed(struct gve_priv *priv)
Thomas Bogendoerfer f8bbdb
--- a/drivers/net/ethernet/google/gve/gve_adminq.h
Thomas Bogendoerfer f8bbdb
+++ b/drivers/net/ethernet/google/gve/gve_adminq.h
Thomas Bogendoerfer f8bbdb
@@ -24,6 +24,7 @@ enum gve_adminq_opcodes {
Thomas Bogendoerfer f8bbdb
 	GVE_ADMINQ_REPORT_STATS			= 0xC,
Thomas Bogendoerfer f8bbdb
 	GVE_ADMINQ_REPORT_LINK_SPEED		= 0xD,
Thomas Bogendoerfer f8bbdb
 	GVE_ADMINQ_GET_PTYPE_MAP		= 0xE,
Thomas Bogendoerfer f8bbdb
+	GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY	= 0xF,
Thomas Bogendoerfer f8bbdb
 };
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
 /* Admin queue status codes */
Thomas Bogendoerfer f8bbdb
@@ -146,6 +147,49 @@ enum gve_sup_feature_mask {
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
 #define GVE_DEV_OPT_LEN_GQI_RAW_ADDRESSING 0x0
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
+#define GVE_VERSION_STR_LEN 128
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+enum gve_driver_capbility {
Thomas Bogendoerfer f8bbdb
+	gve_driver_capability_gqi_qpl = 0,
Thomas Bogendoerfer f8bbdb
+	gve_driver_capability_gqi_rda = 1,
Thomas Bogendoerfer f8bbdb
+	gve_driver_capability_dqo_qpl = 2, /* reserved for future use */
Thomas Bogendoerfer f8bbdb
+	gve_driver_capability_dqo_rda = 3,
Thomas Bogendoerfer f8bbdb
+};
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+#define GVE_CAP1(a) BIT((int)a)
Thomas Bogendoerfer f8bbdb
+#define GVE_CAP2(a) BIT(((int)a) - 64)
Thomas Bogendoerfer f8bbdb
+#define GVE_CAP3(a) BIT(((int)a) - 128)
Thomas Bogendoerfer f8bbdb
+#define GVE_CAP4(a) BIT(((int)a) - 192)
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+#define GVE_DRIVER_CAPABILITY_FLAGS1 \
Thomas Bogendoerfer f8bbdb
+	(GVE_CAP1(gve_driver_capability_gqi_qpl) | \
Thomas Bogendoerfer f8bbdb
+	 GVE_CAP1(gve_driver_capability_gqi_rda) | \
Thomas Bogendoerfer f8bbdb
+	 GVE_CAP1(gve_driver_capability_dqo_rda))
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+#define GVE_DRIVER_CAPABILITY_FLAGS2 0x0
Thomas Bogendoerfer f8bbdb
+#define GVE_DRIVER_CAPABILITY_FLAGS3 0x0
Thomas Bogendoerfer f8bbdb
+#define GVE_DRIVER_CAPABILITY_FLAGS4 0x0
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+struct gve_driver_info {
Thomas Bogendoerfer f8bbdb
+	u8 os_type;	/* 0x01 = Linux */
Thomas Bogendoerfer f8bbdb
+	u8 driver_major;
Thomas Bogendoerfer f8bbdb
+	u8 driver_minor;
Thomas Bogendoerfer f8bbdb
+	u8 driver_sub;
Thomas Bogendoerfer f8bbdb
+	__be32 os_version_major;
Thomas Bogendoerfer f8bbdb
+	__be32 os_version_minor;
Thomas Bogendoerfer f8bbdb
+	__be32 os_version_sub;
Thomas Bogendoerfer f8bbdb
+	__be64 driver_capability_flags[4];
Thomas Bogendoerfer f8bbdb
+	u8 os_version_str1[GVE_VERSION_STR_LEN];
Thomas Bogendoerfer f8bbdb
+	u8 os_version_str2[GVE_VERSION_STR_LEN];
Thomas Bogendoerfer f8bbdb
+};
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+struct gve_adminq_verify_driver_compatibility {
Thomas Bogendoerfer f8bbdb
+	__be64 driver_info_len;
Thomas Bogendoerfer f8bbdb
+	__be64 driver_info_addr;
Thomas Bogendoerfer f8bbdb
+};
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+static_assert(sizeof(struct gve_adminq_verify_driver_compatibility) == 16);
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
 struct gve_adminq_configure_device_resources {
Thomas Bogendoerfer f8bbdb
 	__be64 counter_array;
Thomas Bogendoerfer f8bbdb
 	__be64 irq_db_addr;
Thomas Bogendoerfer f8bbdb
@@ -345,6 +389,8 @@ union gve_adminq_command {
Thomas Bogendoerfer f8bbdb
 			struct gve_adminq_report_stats report_stats;
Thomas Bogendoerfer f8bbdb
 			struct gve_adminq_report_link_speed report_link_speed;
Thomas Bogendoerfer f8bbdb
 			struct gve_adminq_get_ptype_map get_ptype_map;
Thomas Bogendoerfer f8bbdb
+			struct gve_adminq_verify_driver_compatibility
Thomas Bogendoerfer f8bbdb
+						verify_driver_compatibility;
Thomas Bogendoerfer f8bbdb
 		};
Thomas Bogendoerfer f8bbdb
 	};
Thomas Bogendoerfer f8bbdb
 	u8 reserved[64];
Thomas Bogendoerfer f8bbdb
@@ -372,6 +418,9 @@ int gve_adminq_unregister_page_list(stru
Thomas Bogendoerfer f8bbdb
 int gve_adminq_set_mtu(struct gve_priv *priv, u64 mtu);
Thomas Bogendoerfer f8bbdb
 int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
Thomas Bogendoerfer f8bbdb
 			    dma_addr_t stats_report_addr, u64 interval);
Thomas Bogendoerfer f8bbdb
+int gve_adminq_verify_driver_compatibility(struct gve_priv *priv,
Thomas Bogendoerfer f8bbdb
+					   u64 driver_info_len,
Thomas Bogendoerfer f8bbdb
+					   dma_addr_t driver_info_addr);
Thomas Bogendoerfer f8bbdb
 int gve_adminq_report_link_speed(struct gve_priv *priv);
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
 struct gve_ptype_lut;
Thomas Bogendoerfer f8bbdb
--- a/drivers/net/ethernet/google/gve/gve_main.c
Thomas Bogendoerfer f8bbdb
+++ b/drivers/net/ethernet/google/gve/gve_main.c
Thomas Bogendoerfer f8bbdb
@@ -12,6 +12,8 @@
Thomas Bogendoerfer f8bbdb
 #include <linux/sched.h>
Thomas Bogendoerfer f8bbdb
 #include <linux/timer.h>
Thomas Bogendoerfer f8bbdb
 #include <linux/workqueue.h>
Thomas Bogendoerfer f8bbdb
+#include <linux/utsname.h>
Thomas Bogendoerfer f8bbdb
+#include <linux/version.h>
Thomas Bogendoerfer f8bbdb
 #include <net/sch_generic.h>
Thomas Bogendoerfer f8bbdb
 #include "gve.h"
Thomas Bogendoerfer f8bbdb
 #include "gve_dqo.h"
Thomas Bogendoerfer f8bbdb
@@ -30,6 +32,49 @@
Thomas Bogendoerfer f8bbdb
 const char gve_version_str[] = GVE_VERSION;
Thomas Bogendoerfer f8bbdb
 static const char gve_version_prefix[] = GVE_VERSION_PREFIX;
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
+static int gve_verify_driver_compatibility(struct gve_priv *priv)
Thomas Bogendoerfer f8bbdb
+{
Thomas Bogendoerfer f8bbdb
+	int err;
Thomas Bogendoerfer f8bbdb
+	struct gve_driver_info *driver_info;
Thomas Bogendoerfer f8bbdb
+	dma_addr_t driver_info_bus;
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+	driver_info = dma_alloc_coherent(&priv->pdev->dev,
Thomas Bogendoerfer f8bbdb
+					 sizeof(struct gve_driver_info),
Thomas Bogendoerfer f8bbdb
+					 &driver_info_bus, GFP_KERNEL);
Thomas Bogendoerfer f8bbdb
+	if (!driver_info)
Thomas Bogendoerfer f8bbdb
+		return -ENOMEM;
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+	*driver_info = (struct gve_driver_info) {
Thomas Bogendoerfer f8bbdb
+		.os_type = 1, /* Linux */
Thomas Bogendoerfer f8bbdb
+		.os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR),
Thomas Bogendoerfer f8bbdb
+		.os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL),
Thomas Bogendoerfer f8bbdb
+		.os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL),
Thomas Bogendoerfer f8bbdb
+		.driver_capability_flags = {
Thomas Bogendoerfer f8bbdb
+			cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1),
Thomas Bogendoerfer f8bbdb
+			cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2),
Thomas Bogendoerfer f8bbdb
+			cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3),
Thomas Bogendoerfer f8bbdb
+			cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4),
Thomas Bogendoerfer f8bbdb
+		},
Thomas Bogendoerfer f8bbdb
+	};
Thomas Bogendoerfer f8bbdb
+	strscpy(driver_info->os_version_str1, utsname()->release,
Thomas Bogendoerfer f8bbdb
+		sizeof(driver_info->os_version_str1));
Thomas Bogendoerfer f8bbdb
+	strscpy(driver_info->os_version_str2, utsname()->version,
Thomas Bogendoerfer f8bbdb
+		sizeof(driver_info->os_version_str2));
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+	err = gve_adminq_verify_driver_compatibility(priv,
Thomas Bogendoerfer f8bbdb
+						     sizeof(struct gve_driver_info),
Thomas Bogendoerfer f8bbdb
+						     driver_info_bus);
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+	/* It's ok if the device doesn't support this */
Thomas Bogendoerfer f8bbdb
+	if (err == -EOPNOTSUPP)
Thomas Bogendoerfer f8bbdb
+		err = 0;
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
+	dma_free_coherent(&priv->pdev->dev,
Thomas Bogendoerfer f8bbdb
+			  sizeof(struct gve_driver_info),
Thomas Bogendoerfer f8bbdb
+			  driver_info, driver_info_bus);
Thomas Bogendoerfer f8bbdb
+	return err;
Thomas Bogendoerfer f8bbdb
+}
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
 static netdev_tx_t gve_start_xmit(struct sk_buff *skb, struct net_device *dev)
Thomas Bogendoerfer f8bbdb
 {
Thomas Bogendoerfer f8bbdb
 	struct gve_priv *priv = netdev_priv(dev);
Thomas Bogendoerfer f8bbdb
@@ -1369,6 +1414,13 @@ static int gve_init_priv(struct gve_priv
Thomas Bogendoerfer f8bbdb
 		return err;
Thomas Bogendoerfer f8bbdb
 	}
Thomas Bogendoerfer f8bbdb
 
Thomas Bogendoerfer f8bbdb
+	err = gve_verify_driver_compatibility(priv);
Thomas Bogendoerfer f8bbdb
+	if (err) {
Thomas Bogendoerfer f8bbdb
+		dev_err(&priv->pdev->dev,
Thomas Bogendoerfer f8bbdb
+			"Could not verify driver compatibility: err=%d\n", err);
Thomas Bogendoerfer f8bbdb
+		goto err;
Thomas Bogendoerfer f8bbdb
+	}
Thomas Bogendoerfer f8bbdb
+
Thomas Bogendoerfer f8bbdb
 	if (skip_describe_device)
Thomas Bogendoerfer f8bbdb
 		goto setup_device;
Thomas Bogendoerfer f8bbdb