[PATCH iwl-net 5/5] ice: Implement VF LLDP TX support for VF

From: Larysa Zaremba
Date: Wed Feb 28 2024 - 11:05:38 EST


From: Mateusz Pacuszka <mateuszx.pacuszka@xxxxxxxxx>

Add option to enable transmit LLDP on single trusted VF via a sysfs entry,
for example:

echo '1' > /sys/class/net/<PF_IFNAME>/device/virtfn0/transmit_lldp

Signed-off-by: Mateusz Pacuszka <mateuszx.pacuszka@xxxxxxxxx>
Co-developed-by: Jakub Buchocki <jakubx.buchocki@xxxxxxxxx>
Signed-off-by: Jakub Buchocki <jakubx.buchocki@xxxxxxxxx>
Signed-off-by: Larysa Zaremba <larysa.zaremba@xxxxxxxxx>
---
drivers/net/ethernet/intel/ice/ice_lib.c | 3 +
drivers/net/ethernet/intel/ice/ice_sriov.c | 4 +
drivers/net/ethernet/intel/ice/ice_vf_lib.c | 163 ++++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_vf_lib.h | 14 ++
4 files changed, 184 insertions(+)

diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 19f08f2e0139..32b1ed74bfa4 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -2062,6 +2062,9 @@ void ice_dis_sw_lldp(struct ice_pf *pf)

if (vsi && vsi->rx_lldp_ena)
ice_cfg_sw_lldp(vsi, false, false);
+
+ if (vf->transmit_lldp)
+ ice_handle_vf_tx_lldp(vf, false);
}
}

diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c
index a94a1c48c3de..0fe07330cc1a 100644
--- a/drivers/net/ethernet/intel/ice/ice_sriov.c
+++ b/drivers/net/ethernet/intel/ice/ice_sriov.c
@@ -832,6 +832,10 @@ static int ice_create_vf_entries(struct ice_pf *pf, u16 num_vfs)
vf->vfdev = vfdev;
vf->vf_sw_id = pf->first_sw;

+ err = ice_init_vf_sysfs(vf);
+ if (err)
+ goto err_free_entries;
+
pci_dev_get(vfdev);

/* set default number of MSI-X */
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c
index 2de6ef3661cf..244d0ac7c9c4 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c
@@ -577,6 +577,165 @@ void ice_ena_all_vfs_rx_lldp(struct ice_pf *pf)
}
}

+static bool ice_is_transmit_lldp_enabled(struct ice_pf *pf)
+{
+ struct ice_vf *vf;
+ unsigned int bkt;
+
+ ice_for_each_vf(pf, bkt, vf) {
+ if (vf->transmit_lldp)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ice_handle_vf_tx_lldp - enable/disable LLDP TX on VF
+ * @vf: VF to configure Tx LLDP for
+ * @ena: Enable or disable Tx LLDP switch rule
+ *
+ * Configure Tx filters for VF to transmit LLDP
+ */
+int ice_handle_vf_tx_lldp(struct ice_vf *vf, bool ena)
+{
+ void (*allow_override)(struct ice_vsi_ctx *ctx);
+ struct ice_vsi *vsi, *main_vsi;
+ struct ice_pf *pf = vf->pf;
+ struct device *dev;
+ bool prev_ena;
+
+ dev = ice_pf_to_dev(pf);
+ vsi = ice_get_vf_vsi(vf);
+ main_vsi = ice_get_main_vsi(pf);
+ if (!vsi || !main_vsi)
+ return -ENOENT;
+
+ if (ena && test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) {
+ dev_err(dev, "Transmit LLDP VF is only allowed when FW LLDP Agent is disabled");
+ return -EPERM;
+ }
+
+ if (ena && ice_is_transmit_lldp_enabled(pf)) {
+ dev_err(dev, "Only a single VF per port is allowed to transmit LLDP packets, ignoring the settings");
+ return -EPERM;
+ }
+
+ allow_override = ena ? ice_vsi_ctx_set_allow_override
+ : ice_vsi_ctx_clear_allow_override;
+ prev_ena = vsi->info.sec_flags & ICE_AQ_VSI_SEC_FLAG_ALLOW_DEST_OVRD;
+
+ if (ice_vsi_update_security(vsi, allow_override))
+ return -ENOENT;
+
+ /* If VF can transmit LLDP, then PF cannot and vice versa */
+ allow_override = ena ? ice_vsi_ctx_clear_allow_override
+ : ice_vsi_ctx_set_allow_override;
+
+ if (ice_vsi_update_security(main_vsi, allow_override)) {
+ allow_override = prev_ena ? ice_vsi_ctx_set_allow_override
+ : ice_vsi_ctx_clear_allow_override;
+ ice_vsi_update_security(vsi, allow_override);
+ return -ENOENT;
+ }
+
+ vf->transmit_lldp = ena;
+ return 0;
+}
+
+static ssize_t ice_transmit_lldp_vf_attr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ice_vf *vf = ice_get_vf_by_dev(dev);
+
+ if (!vf)
+ return -ENOENT;
+
+ return sysfs_emit(buf, "%u\n", vf->transmit_lldp);
+}
+
+static ssize_t ice_transmit_lldp_vf_attr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *vfdev = container_of(dev, struct pci_dev, dev);
+ struct ice_vf *vf = ice_get_vf_by_dev(dev);
+ struct pci_dev *pdev = vfdev->physfn;
+ struct ice_pf *pf;
+ bool ena;
+ int err;
+
+ if (!vf)
+ return -ENOENT;
+
+ pf = pci_get_drvdata(pdev);
+ if (!pf)
+ return -ENOENT;
+
+ err = kstrtobool(buf, &ena);
+ if (err)
+ return -EINVAL;
+
+ if (ena == vf->transmit_lldp) {
+ dev_dbg(dev, "Transmit LLDP value already set for VF %d",
+ vf->vf_id);
+ return count;
+ }
+
+ err = ice_handle_vf_tx_lldp(vf, ena);
+ if (err)
+ return err;
+
+ return count;
+}
+
+static int ice_init_vf_transmit_lldp_sysfs(struct ice_vf *vf)
+{
+ struct device_attribute tmp = __ATTR(transmit_lldp, 0644,
+ ice_transmit_lldp_vf_attr_show,
+ ice_transmit_lldp_vf_attr_store);
+
+ vf->transmit_lldp_attr = tmp;
+
+ return device_create_file(&vf->vfdev->dev, &vf->transmit_lldp_attr);
+}
+
+/**
+ * ice_init_vf_sysfs - Initialize sysfs entries for a VF
+ * @vf: VF to init sysfs for
+ *
+ * Initialize sysfs entries (accessible from the host) for a VF
+ */
+int ice_init_vf_sysfs(struct ice_vf *vf)
+{
+ struct device *dev = ice_pf_to_dev(vf->pf);
+ int err = 0;
+
+ if (!vf->vfdev) {
+ dev_err(dev, "%s: no vfdev", __func__);
+ return -ENOENT;
+ }
+
+ err = ice_init_vf_transmit_lldp_sysfs(vf);
+ if (err)
+ dev_err(dev, "could not init transmit_lldp sysfs entry, err: %d",
+ err);
+
+ return err;
+}
+
+static int ice_vf_apply_tx_lldp(struct ice_vf *vf)
+{
+ if (!vf->transmit_lldp)
+ return 0;
+
+ /* Disable it so it can be applied again. */
+ vf->transmit_lldp = false;
+
+ return ice_handle_vf_tx_lldp(vf, true);
+}
+
/**
* ice_vf_rebuild_host_cfg - host admin configuration is persistent across reset
* @vf: VF to rebuild host configuration on
@@ -607,6 +766,10 @@ static void ice_vf_rebuild_host_cfg(struct ice_vf *vf)
dev_err(dev, "failed to rebuild spoofchk configuration for VF %d\n",
vf->vf_id);

+ if (ice_vf_apply_tx_lldp(vf))
+ dev_err(dev, "failed to rebuild transmit LLDP configuration for VF %d\n",
+ vf->vf_id);
+
/* rebuild aggregator node config for main VF VSI */
ice_vf_rebuild_aggregator_node_cfg(vsi);
}
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
index 81f734f2ae41..63e53591541e 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
@@ -104,9 +104,11 @@ struct ice_vf {
struct ice_vlan port_vlan_info; /* Port VLAN ID, QoS, and TPID */
struct virtchnl_vlan_caps vlan_v2_caps;
struct ice_mbx_vf_info mbx_info;
+ struct device_attribute transmit_lldp_attr;
u8 pf_set_mac:1; /* VF MAC address set by VMM admin */
u8 trusted:1;
u8 spoofchk:1;
+ u8 transmit_lldp:1;
u8 link_forced:1;
u8 link_up:1; /* only valid if VF link is forced */
/* VSI indices - actual VSI pointers are maintained in the PF structure
@@ -234,6 +236,8 @@ void ice_reset_all_vfs(struct ice_pf *pf);
struct ice_vsi *ice_get_vf_ctrl_vsi(struct ice_pf *pf, struct ice_vsi *vsi);
void ice_ena_all_vfs_rx_lldp(struct ice_pf *pf);
int ice_ena_vf_rx_lldp(struct ice_vf *vf);
+int ice_init_vf_sysfs(struct ice_vf *vf);
+int ice_handle_vf_tx_lldp(struct ice_vf *vf, bool ena);
#else /* CONFIG_PCI_IOV */
static inline struct ice_vf *ice_get_vf_by_id(struct ice_pf *pf, u16 vf_id)
{
@@ -313,6 +317,16 @@ ice_get_vf_ctrl_vsi(struct ice_pf *pf, struct ice_vsi *vsi)
static inline void ice_ena_all_vfs_rx_lldp(struct ice_pf *pf)
{
}
+
+static inline int ice_handle_vf_tx_lldp(struct ice_vf *vf, bool ena)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int ice_init_vf_sysfs(struct ice_vf *vf)
+{
+ return 0;
+}
#endif /* !CONFIG_PCI_IOV */

#endif /* _ICE_VF_LIB_H_ */
--
2.43.0