+static void debug_log_add_del_addr(struct rte_ether_addr *addr, bool add)
+{
+ char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+
+ rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, addr);
+ ENICPMD_LOG(DEBUG, " %s address %s\n",
+ add ? "add" : "remove", mac_str);
+}
+
+static int enicpmd_set_mc_addr_list(struct rte_eth_dev *eth_dev,
+ struct rte_ether_addr *mc_addr_set,
+ uint32_t nb_mc_addr)
+{
+ struct enic *enic = pmd_priv(eth_dev);
+ char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+ struct rte_ether_addr *addr;
+ uint32_t i, j;
+ int ret;
+
+ ENICPMD_FUNC_TRACE();
+
+ /* Validate the given addresses first */
+ for (i = 0; i < nb_mc_addr && mc_addr_set != NULL; i++) {
+ addr = &mc_addr_set[i];
+ if (!rte_is_multicast_ether_addr(addr) ||
+ rte_is_broadcast_ether_addr(addr)) {
+ rte_ether_format_addr(mac_str,
+ RTE_ETHER_ADDR_FMT_SIZE, addr);
+ ENICPMD_LOG(ERR, " invalid multicast address %s\n",
+ mac_str);
+ return -EINVAL;
+ }
+ }
+
+ /* Flush all if requested */
+ if (nb_mc_addr == 0 || mc_addr_set == NULL) {
+ ENICPMD_LOG(DEBUG, " flush multicast addresses\n");
+ for (i = 0; i < enic->mc_count; i++) {
+ addr = &enic->mc_addrs[i];
+ debug_log_add_del_addr(addr, false);
+ ret = vnic_dev_del_addr(enic->vdev, addr->addr_bytes);
+ if (ret)
+ return ret;
+ }
+ enic->mc_count = 0;
+ return 0;
+ }
+
+ if (nb_mc_addr > ENIC_MULTICAST_PERFECT_FILTERS) {
+ ENICPMD_LOG(ERR, " too many multicast addresses: max=%d\n",
+ ENIC_MULTICAST_PERFECT_FILTERS);
+ return -ENOSPC;
+ }
+ /*
+ * devcmd is slow, so apply the difference instead of flushing and
+ * adding everything.
+ * 1. Delete addresses on the NIC but not on the host
+ */
+ for (i = 0; i < enic->mc_count; i++) {
+ addr = &enic->mc_addrs[i];
+ for (j = 0; j < nb_mc_addr; j++) {
+ if (rte_is_same_ether_addr(addr, &mc_addr_set[j]))
+ break;
+ }
+ if (j < nb_mc_addr)
+ continue;
+ debug_log_add_del_addr(addr, false);
+ ret = vnic_dev_del_addr(enic->vdev, addr->addr_bytes);
+ if (ret)
+ return ret;
+ }
+ /* 2. Add addresses on the host but not on the NIC */
+ for (i = 0; i < nb_mc_addr; i++) {
+ addr = &mc_addr_set[i];
+ for (j = 0; j < enic->mc_count; j++) {
+ if (rte_is_same_ether_addr(addr, &enic->mc_addrs[j]))
+ break;
+ }
+ if (j < enic->mc_count)
+ continue;
+ debug_log_add_del_addr(addr, true);
+ ret = vnic_dev_add_addr(enic->vdev, addr->addr_bytes);
+ if (ret)
+ return ret;
+ }
+ /* Keep a copy so we can flush/apply later on.. */
+ memcpy(enic->mc_addrs, mc_addr_set,
+ nb_mc_addr * sizeof(struct rte_ether_addr));
+ enic->mc_count = nb_mc_addr;
+ return 0;
+}
+