@@ -344,6 +344,8 @@ struct ice_vsi {
u32 tx_busy;
u32 rx_buf_failed;
u32 rx_page_failed;
+ u8 lldp_macs; /* counter for LLDP MACs added to VSI */
+ u8 rx_lldp_ena : 1; /* Rx LLDP filter enabled */
u16 num_q_vectors;
/* tell if only dynamic irq allocation is allowed */
bool irq_dyn_alloc;
@@ -1480,6 +1480,8 @@ static int ice_set_priv_flags(struct net_device *netdev, u32 flags)
if (status)
dev_warn(dev, "Fail to init DCB\n");
+ ice_ena_all_vfs_rx_lldp(pf);
+
pf->dcbx_cap &= ~DCB_CAP_DCBX_LLD_MANAGED;
pf->dcbx_cap |= DCB_CAP_DCBX_HOST;
} else {
@@ -1493,10 +1495,10 @@ static int ice_set_priv_flags(struct net_device *netdev, u32 flags)
goto ethtool_exit;
}
- /* Remove rule to direct LLDP packets to default VSI.
+ /* Remove rules to direct LLDP packets to PF/VF VSIs.
* The FW LLDP engine will now be consuming them.
*/
- ice_cfg_sw_lldp(vsi, false, false);
+ ice_dis_sw_lldp(pf);
/* AQ command to start FW LLDP agent will return an
* error if the agent is already started
@@ -2039,6 +2039,50 @@ static void ice_vsi_set_tc_cfg(struct ice_vsi *vsi)
ice_vsi_set_dcb_tc_cfg(vsi);
}
+/**
+ * ice_dis_sw_lldp - Disable SW LLDP on PFs and VFs
+ * @pf: pointer to PF structure
+ *
+ * Disable SW LLDP settings on each entity that can have it
+ */
+void ice_dis_sw_lldp(struct ice_pf *pf)
+{
+ struct ice_vsi *vsi;
+ struct ice_vf *vf;
+ unsigned int bkt;
+
+ vsi = ice_get_main_vsi(pf);
+ ice_cfg_sw_lldp(vsi, false, false);
+
+ if (!test_bit(ICE_FLAG_SRIOV_ENA, pf->flags))
+ return;
+
+ ice_for_each_vf(pf, bkt, vf) {
+ vsi = ice_get_vf_vsi(vf);
+
+ if (vsi && vsi->rx_lldp_ena)
+ ice_cfg_sw_lldp(vsi, false, false);
+ }
+}
+
+/**
+ * ice_is_mc_lldp_eth_addr - Check if given MAC is an LLDP multicast address
+ * @mac: MAC address
+ *
+ * Check if given MAC is one of three possible LLDP multicast addresses.
+ */
+bool ice_is_mc_lldp_eth_addr(const u8 *mac)
+{
+ u8 lldp_mac_base[] = {0x01, 0x80, 0xc2, 0x00, 0x00};
+
+ /* Compare the first 5 octets of given and multicast LLDP address */
+ if (memcmp(mac, lldp_mac_base, (ETH_ALEN - 1) * sizeof(*mac)))
+ return false;
+
+ /* Compare the possible last octets of LLDP address and the given one */
+ return (mac[5] == 0x0e || mac[5] == 0x03 || mac[5] == 0x00);
+}
+
/**
* ice_lldp_fltr_remove_from_port - Remove a LLDP Rx filter from the port
* @hw: port
@@ -2086,6 +2130,9 @@ void ice_cfg_sw_lldp(struct ice_vsi *vsi, bool tx, bool create)
status = eth_fltr(vsi, ETH_P_LLDP, ICE_FLTR_RX,
ICE_FWD_TO_VSI);
+
+ if (!status)
+ vsi->rx_lldp_ena = create;
}
if (status)
@@ -68,6 +68,10 @@ int ice_vsi_stop_xdp_tx_rings(struct ice_vsi *vsi);
void ice_cfg_sw_lldp(struct ice_vsi *vsi, bool tx, bool create);
+void ice_dis_sw_lldp(struct ice_pf *pf);
+
+bool ice_is_mc_lldp_eth_addr(const u8 *mac);
+
int ice_set_link(struct ice_vsi *vsi, bool ena);
void ice_vsi_delete(struct ice_vsi *vsi);
@@ -4704,9 +4704,60 @@ void ice_deinit_dev(struct ice_pf *pf)
ice_clear_interrupt_scheme(pf);
}
+static ssize_t rx_lldp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ice_pf *pf = dev_get_drvdata(dev);
+ struct ice_vsi *vsi = ice_get_main_vsi(pf);
+
+ return sysfs_emit(buf, "%u\n", vsi->rx_lldp_ena);
+}
+
+static ssize_t rx_lldp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ice_pf *pf = dev_get_drvdata(dev);
+ struct ice_vsi *vsi;
+ bool ena;
+ int err;
+
+ if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) {
+ dev_err(dev, "Toggling Rx LLDP for PF is only allowed when FW LLDP Agent is disabled");
+ return -EPERM;
+ }
+
+ err = kstrtobool(buf, &ena);
+ if (err)
+ return -EINVAL;
+
+ vsi = ice_get_main_vsi(pf);
+
+ if (ena == vsi->rx_lldp_ena) {
+ dev_dbg(dev, "Rx LLDP already %sabled", ena ? "en" : "dis");
+ return count;
+ }
+
+ ice_cfg_sw_lldp(vsi, false, ena);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(rx_lldp);
+
+static int ice_init_rx_lldp_sysfs(struct ice_pf *pf)
+{
+ return device_create_file(ice_pf_to_dev(pf), &dev_attr_rx_lldp);
+}
+
+static void ice_deinit_rx_lldp_sysfs(struct ice_pf *pf)
+{
+ device_remove_file(ice_pf_to_dev(pf), &dev_attr_rx_lldp);
+}
+
static void ice_init_features(struct ice_pf *pf)
{
struct device *dev = ice_pf_to_dev(pf);
+ int err;
if (ice_is_safe_mode(pf))
return;
@@ -4734,6 +4785,11 @@ static void ice_init_features(struct ice_pf *pf)
ice_cfg_lldp_mib_change(&pf->hw, true);
}
+ err = ice_init_rx_lldp_sysfs(pf);
+ if (err)
+ dev_err(dev, "could not init rx_lldp sysfs entry, err: %d",
+ err);
+
if (ice_init_lag(pf))
dev_warn(dev, "Failed to init link aggregation support\n");
@@ -4757,6 +4813,8 @@ static void ice_deinit_features(struct ice_pf *pf)
ice_dpll_deinit(pf);
if (pf->eswitch_mode == DEVLINK_ESWITCH_MODE_SWITCHDEV)
xa_destroy(&pf->eswitch.reprs);
+
+ ice_deinit_rx_lldp_sysfs(pf);
}
static void ice_init_wakeup(struct ice_pf *pf)
@@ -518,6 +518,65 @@ static void ice_vf_rebuild_aggregator_node_cfg(struct ice_vsi *vsi)
vsi->agg_node->num_vsis++;
}
+/**
+ * ice_ena_vf_rx_lldp
+ * @vf: VF to configure Rx LLDP for
+ *
+ * Configure Rx filters for VF to receive LLDP
+ */
+int ice_ena_vf_rx_lldp(struct ice_vf *vf)
+{
+ struct ice_pf *pf = vf->pf;
+ struct ice_vsi *vsi;
+
+ if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) {
+ dev_err(ice_pf_to_dev(pf), "FW LLDP agent is enabled, cannot enable Rx LLDP on VF %d\n",
+ vf->vf_id);
+ return -EPERM;
+ }
+
+ if (!vf->trusted) {
+ dev_dbg(ice_pf_to_dev(pf), "VF %d is not trusted - cannot configure Rx LLDP filter.\n",
+ vf->vf_id);
+ return -EPERM;
+ }
+
+ vsi = ice_get_vf_vsi(vf);
+ if (!vsi)
+ return -ENOENT;
+
+ if (vsi->rx_lldp_ena)
+ return 0;
+
+ ice_cfg_sw_lldp(vsi, false, true);
+
+ return 0;
+}
+
+/**
+ * ice_ena_all_vfs_rx_lldp - Re-add RX LLDP filter on applicable VFs.
+ * @pf: ptr to PF
+ *
+ * That is in case when fw-lldp-agent is toggled and LLDP multicast addresses
+ * are added to the interface.
+ * RX LLDP filter will be added for trusted VFs only.
+ */
+void ice_ena_all_vfs_rx_lldp(struct ice_pf *pf)
+{
+ struct ice_vf *vf;
+ unsigned int bkt;
+
+ if (!test_bit(ICE_FLAG_SRIOV_ENA, pf->flags))
+ return;
+
+ ice_for_each_vf(pf, bkt, vf) {
+ struct ice_vsi *vsi = ice_get_vf_vsi(vf);
+
+ if (vsi && vsi->lldp_macs > 0 && !vsi->rx_lldp_ena)
+ ice_ena_vf_rx_lldp(vf);
+ }
+}
+
/**
* ice_vf_rebuild_host_cfg - host admin configuration is persistent across reset
* @vf: VF to rebuild host configuration on
@@ -232,6 +232,8 @@ ice_vf_clear_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m);
int ice_reset_vf(struct ice_vf *vf, u32 flags);
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);
#else /* CONFIG_PCI_IOV */
static inline struct ice_vf *ice_get_vf_by_id(struct ice_pf *pf, u16 vf_id)
{
@@ -307,6 +309,10 @@ ice_get_vf_ctrl_vsi(struct ice_pf *pf, struct ice_vsi *vsi)
{
return NULL;
}
+
+static inline void ice_ena_all_vfs_rx_lldp(struct ice_pf *pf)
+{
+}
#endif /* !CONFIG_PCI_IOV */
#endif /* _ICE_VF_LIB_H_ */
@@ -1958,6 +1958,11 @@ ice_vc_add_mac_addr(struct ice_vf *vf, struct ice_vsi *vsi,
return ret;
} else {
vf->num_mac++;
+
+ if (ice_is_mc_lldp_eth_addr(mac_addr)) {
+ vsi->lldp_macs++;
+ ice_ena_vf_rx_lldp(vf);
+ }
}
ice_vfhw_mac_add(vf, vc_ether_addr);
@@ -2049,6 +2054,12 @@ ice_vc_del_mac_addr(struct ice_vf *vf, struct ice_vsi *vsi,
return -EIO;
}
+ if (ice_is_mc_lldp_eth_addr(mac_addr))
+ vsi->lldp_macs--;
+
+ if (vsi->rx_lldp_ena && !vsi->lldp_macs)
+ ice_cfg_sw_lldp(vsi, false, false);
+
ice_vfhw_mac_del(vf, vc_ether_addr);
vf->num_mac--;