lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20230206153603.2801791-3-simon.horman@corigine.com>
Date:   Mon,  6 Feb 2023 16:36:03 +0100
From:   Simon Horman <simon.horman@...igine.com>
To:     David Miller <davem@...emloft.net>,
        Jakub Kicinski <kuba@...nel.org>,
        Paolo Abeni <pabeni@...hat.com>
Cc:     Michael Chan <michael.chan@...adcom.com>,
        Andy Gospodarek <andy@...yhouse.net>,
        Gal Pressman <gal@...dia.com>,
        Saeed Mahameed <saeed@...nel.org>,
        Jesse Brandeburg <jesse.brandeburg@...el.com>,
        Tony Nguyen <anthony.l.nguyen@...el.com>,
        Edward Cree <ecree.xilinx@...il.com>,
        Vladimir Oltean <vladimir.oltean@....com>,
        Andrew Lunn <andrew@...n.ch>, Fei Qin <fei.qin@...igine.com>,
        netdev@...r.kernel.org, oss-drivers@...igine.com
Subject: [PATCH/RFC net-next 2/2] nfp: add support for assigning VFs to different physical ports

From: Fei Qin <fei.qin@...igine.com>

Currently, all the VFs are bridged to port 0 for the device.
Assigning VFs to different ports is required to realize
traffic load balancing.

Add implement for "devlink port function set vf_count <VAL>"
command to support assigning vfs to different physical ports.

e.g.
$ devlink port show pci/0000:82:00.0/0
pci/0000:82:00.0/0: type eth netdev enp130s0np0 flavour physical
port 0 splittable true lanes 4
    function:
       vf_count 0

$ devlink port function set pci/0000:82:00.0/0 vf_count 3

$ devlink port show pci/0000:82:00.0/0
pci/0000:82:00.0/0: type eth netdev enp130s0np0 flavour physical
port 0 splittable true lanes 4
    function:
       vf_count 3

Signed-off-by: Fei Qin <fei.qin@...igine.com>
Signed-off-by: Simon Horman <simon.horman@...igine.com>
---
 .../net/ethernet/netronome/nfp/nfp_devlink.c  |  71 +++++++++
 drivers/net/ethernet/netronome/nfp/nfp_main.c |   5 +
 drivers/net/ethernet/netronome/nfp/nfp_main.h |   4 +
 .../ethernet/netronome/nfp/nfp_net_sriov.c    | 147 +++++++++++++++++-
 .../ethernet/netronome/nfp/nfp_net_sriov.h    |   6 +
 5 files changed, 225 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
index bf6bae557158..9a6b4d6c28ca 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
@@ -9,6 +9,7 @@
 #include "nfp_app.h"
 #include "nfp_main.h"
 #include "nfp_port.h"
+#include "nfp_net_sriov.h"
 
 static int
 nfp_devlink_fill_eth_port(struct nfp_port *port,
@@ -310,6 +311,74 @@ nfp_devlink_flash_update(struct devlink *devlink,
 	return nfp_flash_update_common(devlink_priv(devlink), params->fw, extack);
 }
 
+static int
+nfp_devlink_vf_count_get_port_id(struct devlink_port *dl_port)
+{
+	struct nfp_pf *pf = devlink_priv(dl_port->devlink);
+	struct nfp_eth_table_port *eth_port;
+	struct nfp_port *port;
+	int err;
+
+	err = nfp_net_sriov_check(pf->app, 0, NFP_NET_VF_CFG_MB_CAP_VF_ASSIGNMENT,
+				  "vf_assignment", false);
+	if (err)
+		return err;
+
+	port = container_of(dl_port, struct nfp_port, dl_port);
+	eth_port = __nfp_port_get_eth_port(port);
+	if (!eth_port)
+		return -EOPNOTSUPP;
+
+	return eth_port - pf->eth_tbl->ports;
+}
+
+static int
+nfp_devlink_vf_count_get(struct devlink_port *dl_port, u16 *vf_count,
+			 struct netlink_ext_ack *extack)
+{
+	int port_id = nfp_devlink_vf_count_get_port_id(dl_port);
+	struct nfp_pf *pf = devlink_priv(dl_port->devlink);
+
+	if (port_id < 0 || port_id >= NFP_VF_ASSIGNMENT_PORT_COUNT)
+		return -EOPNOTSUPP;
+
+	*vf_count = pf->num_assigned_vfs[port_id];
+
+	return 0;
+}
+
+static int
+nfp_devlink_vf_count_set(struct devlink_port *dl_port, u16 vf_count,
+			 struct netlink_ext_ack *extack)
+{
+	int port_id = nfp_devlink_vf_count_get_port_id(dl_port);
+	struct nfp_pf *pf = devlink_priv(dl_port->devlink);
+	int total_num_ports = pf->eth_tbl->count;
+	int total_num_vfs = 0;
+	unsigned int i;
+
+	if (port_id < 0 || port_id >= NFP_VF_ASSIGNMENT_PORT_COUNT)
+		return -EOPNOTSUPP;
+
+	for (i = 0; i < total_num_ports; i++) {
+		if (i != port_id)
+			total_num_vfs += pf->num_assigned_vfs[i];
+		else
+			total_num_vfs += vf_count;
+
+		if (total_num_vfs > pf->limit_vfs)
+			goto exit_out_of_range;
+	}
+
+	pf->num_assigned_vfs[port_id] = vf_count;
+
+	return 0;
+
+exit_out_of_range:
+	NL_SET_ERR_MSG_MOD(extack, "Parameter out of range");
+	return -EINVAL;
+}
+
 const struct devlink_ops nfp_devlink_ops = {
 	.port_split		= nfp_devlink_port_split,
 	.port_unsplit		= nfp_devlink_port_unsplit,
@@ -319,6 +388,8 @@ const struct devlink_ops nfp_devlink_ops = {
 	.eswitch_mode_set	= nfp_devlink_eswitch_mode_set,
 	.info_get		= nfp_devlink_info_get,
 	.flash_update		= nfp_devlink_flash_update,
+	.port_fn_vf_count_get	= nfp_devlink_vf_count_get,
+	.port_fn_vf_count_set	= nfp_devlink_vf_count_set,
 };
 
 int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c
index 71301dbd8fb5..29663d2562aa 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_main.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c
@@ -29,6 +29,7 @@
 #include "nfp_app.h"
 #include "nfp_main.h"
 #include "nfp_net.h"
+#include "nfp_net_sriov.h"
 
 static const char nfp_driver_name[] = "nfp";
 
@@ -252,6 +253,10 @@ static int nfp_pcie_sriov_enable(struct pci_dev *pdev, int num_vfs)
 		return -EINVAL;
 	}
 
+	err = nfp_configure_assign_vf(pdev, num_vfs);
+	if (err)
+		return err;
+
 	err = pci_enable_sriov(pdev, num_vfs);
 	if (err) {
 		dev_warn(&pdev->dev, "Failed to enable PCI SR-IOV: %d\n", err);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h
index 14a751bfe1fe..67692fcf1201 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_main.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h
@@ -16,6 +16,8 @@
 #include <linux/workqueue.h>
 #include <net/devlink.h>
 
+#define NFP_VF_ASSIGNMENT_PORT_COUNT 8
+
 struct dentry;
 struct device;
 struct pci_dev;
@@ -63,6 +65,7 @@ struct nfp_dumpspec {
  * @irq_entries:	Array of MSI-X entries for all vNICs
  * @limit_vfs:		Number of VFs supported by firmware (~0 for PCI limit)
  * @num_vfs:		Number of SR-IOV VFs enabled
+ * @num_assigned_vfs:	Number of VFs assigned to different physical ports
  * @fw_loaded:		Is the firmware loaded?
  * @unload_fw_on_remove:Do we need to unload firmware on driver removal?
  * @ctrl_vnic:		Pointer to the control vNIC if available
@@ -111,6 +114,7 @@ struct nfp_pf {
 
 	unsigned int limit_vfs;
 	unsigned int num_vfs;
+	u8 num_assigned_vfs[NFP_VF_ASSIGNMENT_PORT_COUNT];
 
 	bool fw_loaded;
 	bool unload_fw_on_remove;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c
index 6eeeb0fda91f..873c6d707c0e 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c
@@ -13,9 +13,13 @@
 #include "nfp_net_ctrl.h"
 #include "nfp_net.h"
 #include "nfp_net_sriov.h"
+#include "nfp_port.h"
+#include "nfpcore/nfp_nsp.h"
 
-static int
-nfp_net_sriov_check(struct nfp_app *app, int vf, u16 cap, const char *msg, bool warn)
+/* Capability of VF pre-configuration */
+#define NFP_NET_VF_PRE_CONFIG			NFP_NET_VF_CFG_MB_CAP_VF_ASSIGNMENT
+
+int nfp_net_sriov_check(struct nfp_app *app, int vf, u16 cap, const char *msg, bool warn)
 {
 	u16 cap_vf;
 
@@ -29,6 +33,9 @@ nfp_net_sriov_check(struct nfp_app *app, int vf, u16 cap, const char *msg, bool
 		return -EOPNOTSUPP;
 	}
 
+	if (cap & NFP_NET_VF_PRE_CONFIG)
+		return 0;
+
 	if (vf < 0 || vf >= app->pf->num_vfs) {
 		if (warn)
 			nfp_warn(app->pf->cpp, "invalid VF id %d\n", vf);
@@ -38,17 +45,125 @@ nfp_net_sriov_check(struct nfp_app *app, int vf, u16 cap, const char *msg, bool
 	return 0;
 }
 
+/* VFs can be shown and configured through each physical port even with VF
+ * assignment enabled. FW requires configurations to be sent through the port
+ * that the VF assigned to. Driver may need to get the port id and judge if the
+ * current netdev is the one that the VF assigned to.
+ */
+static int nfp_vf_assignment_get_port_id(struct nfp_app *app, int vf)
+{
+	struct nfp_pf *pf = app->pf;
+	unsigned int i, start_vf;
+
+	for (start_vf = 0, i = 0; i < ARRAY_SIZE(pf->num_assigned_vfs); i++) {
+		if (vf >= start_vf && vf < (start_vf + pf->num_assigned_vfs[i]))
+			return i;
+		start_vf += pf->num_assigned_vfs[i];
+	}
+
+	/* If VF assignment is disabled, all the VFs are assigned to port 0 */
+	return 0;
+}
+
+static bool nfp_vf_assignment_assigned_to_cur_port(struct net_device *netdev, int vf)
+{
+	struct nfp_app *app = nfp_app_from_netdev(netdev);
+	struct nfp_eth_table_port *eth_port;
+	struct nfp_pf *pf = app->pf;
+	int assigned_port_id, err;
+	struct nfp_port *port;
+
+	/* If firmware doesn't support vf_assignment, each VF should be shown under
+	 * each physical port or PF normally.
+	 */
+	err = nfp_net_sriov_check(pf->app, 0, NFP_NET_VF_CFG_MB_CAP_VF_ASSIGNMENT,
+				  "vf_assignment", false);
+	if (err < 0)
+		return true;
+
+	port = nfp_port_from_netdev(netdev);
+	eth_port = nfp_port_get_eth_port(port);
+	if (!eth_port)
+		return true;
+
+	assigned_port_id = nfp_vf_assignment_get_port_id(app, vf);
+	return eth_port == &pf->eth_tbl->ports[assigned_port_id];
+}
+
+int nfp_configure_assign_vf(struct pci_dev *pdev, int num_vfs)
+{
+	struct nfp_pf *pf = pci_get_drvdata(pdev);
+	int err, total_num_vfs = 0, idx = 0;
+	struct nfp_net *nn;
+	unsigned int i;
+
+	err = nfp_net_sriov_check(pf->app, 0, NFP_NET_VF_CFG_MB_CAP_VF_ASSIGNMENT,
+				  "vf_assignment", false);
+	if (err)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(pf->num_assigned_vfs); i++)
+		total_num_vfs += pf->num_assigned_vfs[i];
+
+	/* When the total_num_vfs is nonzero, the VF assignment is enabled.
+	 * The total number of created VFs is required to be consistent with
+	 * the one set in VF assignment.
+	 */
+	if (total_num_vfs && num_vfs != total_num_vfs) {
+		dev_err(&pdev->dev,
+			"Trying to create %d VFs not satisfy the configuration of VF assignment\n",
+			num_vfs);
+		return -EINVAL;
+	}
+
+	/* When VF assignment is disabled, all the VFs are allocated to port 0 */
+	list_for_each_entry(nn, &pf->vnics, vnic_list) {
+		u8 num_assigned_vfs = ((idx == 0) && !total_num_vfs) ?
+				  pf->limit_vfs : pf->num_assigned_vfs[idx];
+		idx++;
+
+		writeb(num_assigned_vfs, pf->vfcfg_tbl2 + NFP_NET_VF_CFG_VF_ASSIGNMENT);
+		writew(NFP_NET_VF_CFG_MB_UPD_VF_ASSIGNMENT, pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_UPD);
+
+		err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VF);
+		if (err)
+			return err;
+
+		err = readw(pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_RET);
+		if (err) {
+			dev_info(&pdev->dev,
+				 "FW refused VF assignment update with errno: %d\n", err);
+			return -err;
+		}
+	}
+
+	return 0;
+}
+
 static int
 nfp_net_sriov_update(struct nfp_app *app, int vf, u16 update, const char *msg)
 {
+	int ret, assigned_port_id, cnt = 0;
+	struct nfp_net *nn_iter;
 	struct nfp_net *nn;
-	int ret;
 
 	/* Write update info to mailbox in VF config symbol */
 	writeb(vf, app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_VF_NUM);
 	writew(update, app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_UPD);
 
 	nn = list_first_entry(&app->pf->vnics, struct nfp_net, vnic_list);
+
+	/* If VF assignment is enabled, reconfig of VF should be set through "nn"
+	 * of corresponding physical port
+	 */
+	assigned_port_id = nfp_vf_assignment_get_port_id(app, vf);
+	list_for_each_entry(nn_iter, &app->pf->vnics, vnic_list) {
+		if (cnt++ == assigned_port_id) {
+			nn = nn_iter;
+			break;
+		}
+	}
+
 	/* Signal VF reconfiguration */
 	ret = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VF);
 	if (ret)
@@ -257,11 +372,18 @@ int nfp_app_set_vf_link_state(struct net_device *netdev, int vf,
 				    "link state");
 }
 
+/* VFs can be shown under each physical port. When the VF is
+ * not assigned to the physical port, hardcode its mac to
+ * ff:ff:ff:ff:ff:ff to distinguish. The changes of VFs'
+ * configurations can be only seen under the corresponding
+ * physical ports.
+ */
 int nfp_app_get_vf_config(struct net_device *netdev, int vf,
 			  struct ifla_vf_info *ivi)
 {
 	struct nfp_app *app = nfp_app_from_netdev(netdev);
 	u32 vf_offset, mac_hi, rate;
+	bool is_assigned = true;
 	u32 vlan_tag;
 	u16 mac_lo;
 	u8 flags;
@@ -271,13 +393,19 @@ int nfp_app_get_vf_config(struct net_device *netdev, int vf,
 	if (err)
 		return err;
 
-	vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ;
+	is_assigned = nfp_vf_assignment_assigned_to_cur_port(netdev, vf);
 
-	mac_hi = readl(app->pf->vfcfg_tbl2 + vf_offset);
-	mac_lo = readw(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_MAC_LO);
+	vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ;
 
-	flags = readb(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_CTRL);
-	vlan_tag = readl(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_VLAN);
+	if (is_assigned) {
+		mac_hi = readl(app->pf->vfcfg_tbl2 + vf_offset);
+		mac_lo = readw(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_MAC_LO);
+		flags = readb(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_CTRL);
+		vlan_tag = readl(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_VLAN);
+	} else {
+		mac_hi = 0xffffffff;
+		mac_lo = 0xffff;
+	}
 
 	memset(ivi, 0, sizeof(*ivi));
 	ivi->vf = vf;
@@ -285,6 +413,9 @@ int nfp_app_get_vf_config(struct net_device *netdev, int vf,
 	put_unaligned_be32(mac_hi, &ivi->mac[0]);
 	put_unaligned_be16(mac_lo, &ivi->mac[4]);
 
+	if (!is_assigned)
+		return 0;
+
 	ivi->vlan = FIELD_GET(NFP_NET_VF_CFG_VLAN_VID, vlan_tag);
 	ivi->qos = FIELD_GET(NFP_NET_VF_CFG_VLAN_QOS, vlan_tag);
 	if (!nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_VLAN_PROTO, "vlan_proto", false))
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h
index 2d445fa199dc..fdf429f60d34 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h
@@ -21,6 +21,7 @@
 #define   NFP_NET_VF_CFG_MB_CAP_TRUST			  (0x1 << 4)
 #define   NFP_NET_VF_CFG_MB_CAP_VLAN_PROTO		  (0x1 << 5)
 #define   NFP_NET_VF_CFG_MB_CAP_RATE			  (0x1 << 6)
+#define   NFP_NET_VF_CFG_MB_CAP_VF_ASSIGNMENT		  (0x1 << 8)
 #define NFP_NET_VF_CFG_MB_RET				0x2
 #define NFP_NET_VF_CFG_MB_UPD				0x4
 #define   NFP_NET_VF_CFG_MB_UPD_MAC			  (0x1 << 0)
@@ -30,6 +31,8 @@
 #define   NFP_NET_VF_CFG_MB_UPD_TRUST			  (0x1 << 4)
 #define   NFP_NET_VF_CFG_MB_UPD_VLAN_PROTO		  (0x1 << 5)
 #define   NFP_NET_VF_CFG_MB_UPD_RATE			  (0x1 << 6)
+#define   NFP_NET_VF_CFG_MB_UPD_VF_ASSIGNMENT		  (0x1 << 8)
+#define NFP_NET_VF_CFG_VF_ASSIGNMENT			0x6
 #define NFP_NET_VF_CFG_MB_VF_NUM			0x7
 
 /* VF config entry
@@ -67,5 +70,8 @@ int nfp_app_set_vf_link_state(struct net_device *netdev, int vf,
 			      int link_state);
 int nfp_app_get_vf_config(struct net_device *netdev, int vf,
 			  struct ifla_vf_info *ivi);
+int nfp_configure_assign_vf(struct pci_dev *pdev, int num_vfs);
+int nfp_net_sriov_check(struct nfp_app *app, int vf, u16 cap, const char *msg,
+			bool warn);
 
 #endif /* _NFP_NET_SRIOV_H_ */
-- 
2.30.2

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ