@@ -407,6 +407,7 @@ void sfc_port_link_mode_to_info(efx_link_mode_t link_mode,
int sfc_port_update_mac_stats(struct sfc_adapter *sa);
int sfc_port_reset_mac_stats(struct sfc_adapter *sa);
int sfc_set_rx_mode(struct sfc_adapter *sa);
+int sfc_set_rx_mode_unchecked(struct sfc_adapter *sa);
#ifdef __cplusplus
@@ -393,8 +393,16 @@ sfc_dev_filter_set(struct rte_eth_dev *dev, enum sfc_dev_filter_mode mode,
} else if ((sa->state == SFC_ADAPTER_STARTED) &&
((rc = sfc_set_rx_mode(sa)) != 0)) {
*toggle = !(enabled);
- sfc_warn(sa, "Failed to %s %s mode",
- ((enabled) ? "enable" : "disable"), desc);
+ sfc_warn(sa, "Failed to %s %s mode, rc = %d",
+ ((enabled) ? "enable" : "disable"), desc, rc);
+
+ /*
+ * For promiscuous and all-multicast filters a
+ * permission failure should be reported as an
+ * unsupported filter.
+ */
+ if (rc == EPERM)
+ rc = ENOTSUP;
}
}
@@ -1060,12 +1068,12 @@ sfc_mac_addr_set(struct rte_eth_dev *dev, struct rte_ether_addr *mac_addr)
* has no effect on received traffic, therefore
* we also need to update unicast filters
*/
- rc = sfc_set_rx_mode(sa);
+ rc = sfc_set_rx_mode_unchecked(sa);
if (rc != 0) {
sfc_err(sa, "cannot set filter (rc = %u)", rc);
/* Rollback the old address */
(void)efx_mac_addr_set(sa->nic, old_addr->addr_bytes);
- (void)sfc_set_rx_mode(sa);
+ (void)sfc_set_rx_mode_unchecked(sa);
}
} else {
sfc_warn(sa, "cannot set MAC address with filters installed");
@@ -239,7 +239,7 @@ sfc_port_start(struct sfc_adapter *sa)
B_TRUE : B_FALSE;
port->allmulti = (sa->eth_dev->data->all_multicast != 0) ?
B_TRUE : B_FALSE;
- rc = sfc_set_rx_mode(sa);
+ rc = sfc_set_rx_mode_unchecked(sa);
if (rc != 0)
goto fail_mac_filter_set;
@@ -485,16 +485,70 @@ sfc_port_detach(struct sfc_adapter *sa)
sfc_log_init(sa, "done");
}
+static boolean_t
+sfc_get_requested_all_ucast(struct sfc_port *port)
+{
+ return port->promisc;
+}
+
+static boolean_t
+sfc_get_requested_all_mcast(struct sfc_port *port)
+{
+ return port->promisc || port->allmulti;
+}
+
+int
+sfc_set_rx_mode_unchecked(struct sfc_adapter *sa)
+{
+ struct sfc_port *port = &sa->port;
+ boolean_t requested_all_ucast = sfc_get_requested_all_ucast(port);
+ boolean_t requested_all_mcast = sfc_get_requested_all_mcast(port);
+ int rc;
+
+ rc = efx_mac_filter_set(sa->nic, requested_all_ucast, B_TRUE,
+ requested_all_mcast, B_TRUE);
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
int
sfc_set_rx_mode(struct sfc_adapter *sa)
{
struct sfc_port *port = &sa->port;
+ boolean_t old_all_ucast;
+ boolean_t old_all_mcast;
+ boolean_t requested_all_ucast = sfc_get_requested_all_ucast(port);
+ boolean_t requested_all_mcast = sfc_get_requested_all_mcast(port);
+ boolean_t actual_all_ucast;
+ boolean_t actual_all_mcast;
int rc;
- rc = efx_mac_filter_set(sa->nic, port->promisc, B_TRUE,
- port->promisc || port->allmulti, B_TRUE);
+ efx_mac_filter_get_all_ucast_mcast(sa->nic, &old_all_ucast,
+ &old_all_mcast);
- return rc;
+ rc = sfc_set_rx_mode_unchecked(sa);
+ if (rc != 0)
+ return rc;
+
+ efx_mac_filter_get_all_ucast_mcast(sa->nic, &actual_all_ucast,
+ &actual_all_mcast);
+
+ if (actual_all_ucast != requested_all_ucast ||
+ actual_all_mcast != requested_all_mcast) {
+ /*
+ * MAC filter set succeeded but not all requested modes
+ * were applied. The rollback is necessary to bring back the
+ * consistent old state.
+ */
+ (void)efx_mac_filter_set(sa->nic, old_all_ucast, B_TRUE,
+ old_all_mcast, B_TRUE);
+
+ return EPERM;
+ }
+
+ return 0;
}
void
@@ -720,7 +720,7 @@ sfc_rx_default_rxq_set_filter(struct sfc_adapter *sa, struct sfc_rxq *rxq)
port->promisc = B_FALSE;
sa->eth_dev->data->promiscuous = 0;
- rc = sfc_set_rx_mode(sa);
+ rc = sfc_set_rx_mode_unchecked(sa);
if (rc != 0)
return rc;
@@ -734,7 +734,7 @@ sfc_rx_default_rxq_set_filter(struct sfc_adapter *sa, struct sfc_rxq *rxq)
port->allmulti = B_FALSE;
sa->eth_dev->data->all_multicast = 0;
- rc = sfc_set_rx_mode(sa);
+ rc = sfc_set_rx_mode_unchecked(sa);
if (rc != 0)
return rc;