Commit 35a2443d authored by Mateusz Palczewski's avatar Mateusz Palczewski Committed by Tony Nguyen
Browse files

iavf: Add waiting for response from PF in set mac



Make iavf_set_mac synchronous by waiting for a response
from a PF. Without this iavf_set_mac is always returning
success even though set_mac can be rejected by a PF.
This ensures that when set_mac exits netdev MAC is updated.
This is needed for sending ARPs with correct MAC after
changing VF's MAC. This is also needed by bonding module.

Signed-off-by: default avatarSylwester Dziedziuch <sylwesterx.dziedziuch@intel.com>
Signed-off-by: default avatarMateusz Palczewski <mateusz.palczewski@intel.com>
Tested-by: default avatarKonrad Jankowski <konrad0.jankowski@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent c87c938f
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -146,7 +146,8 @@ struct iavf_mac_filter {
		u8 remove:1;        /* filter needs to be removed */
		u8 add:1;           /* filter needs to be added */
		u8 is_primary:1;    /* filter is a default VF MAC */
		u8 padding:4;
		u8 add_handled:1;   /* received response for filter add */
		u8 padding:3;
	};
};

@@ -248,6 +249,7 @@ struct iavf_adapter {
	struct work_struct adminq_task;
	struct delayed_work client_task;
	wait_queue_head_t down_waitqueue;
	wait_queue_head_t vc_waitqueue;
	struct iavf_q_vector *q_vectors;
	struct list_head vlan_filter_list;
	struct list_head mac_filter_list;
@@ -292,6 +294,7 @@ struct iavf_adapter {
#define IAVF_FLAG_QUEUES_DISABLED		BIT(17)
#define IAVF_FLAG_SETUP_NETDEV_FEATURES		BIT(18)
#define IAVF_FLAG_REINIT_MSIX_NEEDED		BIT(20)
#define IAVF_FLAG_INITIAL_MAC_SET		BIT(23)
/* duplicates for common code */
#define IAVF_FLAG_DCB_ENABLED			0
	/* flags for admin queue service task */
@@ -559,6 +562,8 @@ void iavf_enable_vlan_stripping_v2(struct iavf_adapter *adapter, u16 tpid);
void iavf_disable_vlan_stripping_v2(struct iavf_adapter *adapter, u16 tpid);
void iavf_enable_vlan_insertion_v2(struct iavf_adapter *adapter, u16 tpid);
void iavf_disable_vlan_insertion_v2(struct iavf_adapter *adapter, u16 tpid);
int iavf_replace_primary_mac(struct iavf_adapter *adapter,
			     const u8 *new_mac);
void
iavf_set_vlan_offload_features(struct iavf_adapter *adapter,
			       netdev_features_t prev_features,
+109 −18
Original line number Diff line number Diff line
@@ -983,6 +983,7 @@ struct iavf_mac_filter *iavf_add_filter(struct iavf_adapter *adapter,

		list_add_tail(&f->list, &adapter->mac_filter_list);
		f->add = true;
		f->add_handled = false;
		f->is_new_mac = true;
		f->is_primary = false;
		adapter->aq_required |= IAVF_FLAG_AQ_ADD_MAC_FILTER;
@@ -994,47 +995,132 @@ struct iavf_mac_filter *iavf_add_filter(struct iavf_adapter *adapter,
}

/**
 * iavf_set_mac - NDO callback to set port mac address
 * @netdev: network interface device structure
 * @p: pointer to an address structure
 * iavf_replace_primary_mac - Replace current primary address
 * @adapter: board private structure
 * @new_mac: new MAC address to be applied
 *
 * Returns 0 on success, negative on failure
 * Replace current dev_addr and send request to PF for removal of previous
 * primary MAC address filter and addition of new primary MAC filter.
 * Return 0 for success, -ENOMEM for failure.
 *
 * Do not call this with mac_vlan_list_lock!
 **/
static int iavf_set_mac(struct net_device *netdev, void *p)
int iavf_replace_primary_mac(struct iavf_adapter *adapter,
			     const u8 *new_mac)
{
	struct iavf_adapter *adapter = netdev_priv(netdev);
	struct iavf_hw *hw = &adapter->hw;
	struct iavf_mac_filter *f;
	struct sockaddr *addr = p;

	if (!is_valid_ether_addr(addr->sa_data))
		return -EADDRNOTAVAIL;

	if (ether_addr_equal(netdev->dev_addr, addr->sa_data))
		return 0;

	spin_lock_bh(&adapter->mac_vlan_list_lock);

	list_for_each_entry(f, &adapter->mac_filter_list, list) {
		f->is_primary = false;
	}

	f = iavf_find_filter(adapter, hw->mac.addr);
	if (f) {
		f->remove = true;
		f->is_primary = true;
		adapter->aq_required |= IAVF_FLAG_AQ_DEL_MAC_FILTER;
	}

	f = iavf_add_filter(adapter, addr->sa_data);
	f = iavf_add_filter(adapter, new_mac);

	if (f) {
		/* Always send the request to add if changing primary MAC
		 * even if filter is already present on the list
		 */
		f->is_primary = true;
		ether_addr_copy(hw->mac.addr, addr->sa_data);
		f->add = true;
		adapter->aq_required |= IAVF_FLAG_AQ_ADD_MAC_FILTER;
		ether_addr_copy(hw->mac.addr, new_mac);
	}

	spin_unlock_bh(&adapter->mac_vlan_list_lock);

	/* schedule the watchdog task to immediately process the request */
	if (f)
	if (f) {
		queue_work(iavf_wq, &adapter->watchdog_task.work);
		return 0;
	}
	return -ENOMEM;
}

/**
 * iavf_is_mac_set_handled - wait for a response to set MAC from PF
 * @netdev: network interface device structure
 * @macaddr: MAC address to set
 *
 * Returns true on success, false on failure
 */
static bool iavf_is_mac_set_handled(struct net_device *netdev,
				    const u8 *macaddr)
{
	struct iavf_adapter *adapter = netdev_priv(netdev);
	struct iavf_mac_filter *f;
	bool ret = false;

	spin_lock_bh(&adapter->mac_vlan_list_lock);

	f = iavf_find_filter(adapter, macaddr);

	return (f == NULL) ? -ENOMEM : 0;
	if (!f || (!f->add && f->add_handled))
		ret = true;

	spin_unlock_bh(&adapter->mac_vlan_list_lock);

	return ret;
}

/**
 * iavf_set_mac - NDO callback to set port MAC address
 * @netdev: network interface device structure
 * @p: pointer to an address structure
 *
 * Returns 0 on success, negative on failure
 */
static int iavf_set_mac(struct net_device *netdev, void *p)
{
	struct iavf_adapter *adapter = netdev_priv(netdev);
	struct sockaddr *addr = p;
	bool handle_mac = iavf_is_mac_set_handled(netdev, addr->sa_data);
	int ret;

	if (!is_valid_ether_addr(addr->sa_data))
		return -EADDRNOTAVAIL;

	ret = iavf_replace_primary_mac(adapter, addr->sa_data);

	if (ret)
		return ret;

	/* If this is an initial set MAC during VF spawn do not wait */
	if (adapter->flags & IAVF_FLAG_INITIAL_MAC_SET) {
		adapter->flags &= ~IAVF_FLAG_INITIAL_MAC_SET;
		return 0;
	}

	if (handle_mac)
		goto done;

	ret = wait_event_interruptible_timeout(adapter->vc_waitqueue, false, msecs_to_jiffies(2500));

	/* If ret < 0 then it means wait was interrupted.
	 * If ret == 0 then it means we got a timeout.
	 * else it means we got response for set MAC from PF,
	 * check if netdev MAC was updated to requested MAC,
	 * if yes then set MAC succeeded otherwise it failed return -EACCES
	 */
	if (ret < 0)
		return ret;

	if (!ret)
		return -EAGAIN;

done:
	if (!ether_addr_equal(netdev->dev_addr, addr->sa_data))
		return -EACCES;

	return 0;
}

/**
@@ -2451,6 +2537,8 @@ static void iavf_init_config_adapter(struct iavf_adapter *adapter)
		ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr);
	}

	adapter->flags |= IAVF_FLAG_INITIAL_MAC_SET;

	adapter->tx_desc_count = IAVF_DEFAULT_TXD;
	adapter->rx_desc_count = IAVF_DEFAULT_RXD;
	err = iavf_init_interrupt_scheme(adapter);
@@ -4681,6 +4769,9 @@ static int iavf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
	/* Setup the wait queue for indicating transition to down status */
	init_waitqueue_head(&adapter->down_waitqueue);

	/* Setup the wait queue for indicating virtchannel events */
	init_waitqueue_head(&adapter->vc_waitqueue);

	return 0;

err_ioremap:
+59 −2
Original line number Diff line number Diff line
@@ -598,6 +598,8 @@ static void iavf_mac_add_ok(struct iavf_adapter *adapter)
	spin_lock_bh(&adapter->mac_vlan_list_lock);
	list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
		f->is_new_mac = false;
		if (!f->add && !f->add_handled)
			f->add_handled = true;
	}
	spin_unlock_bh(&adapter->mac_vlan_list_lock);
}
@@ -618,6 +620,9 @@ static void iavf_mac_add_reject(struct iavf_adapter *adapter)
		if (f->remove && ether_addr_equal(f->macaddr, netdev->dev_addr))
			f->remove = false;

		if (!f->add && !f->add_handled)
			f->add_handled = true;

		if (f->is_new_mac) {
			list_del(&f->list);
			kfree(f);
@@ -1932,6 +1937,7 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
			iavf_mac_add_reject(adapter);
			/* restore administratively set MAC address */
			ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
			wake_up(&adapter->vc_waitqueue);
			break;
		case VIRTCHNL_OP_DEL_VLAN:
			dev_err(&adapter->pdev->dev, "Failed to delete VLAN filter, error %s\n",
@@ -2091,7 +2097,13 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
		if (!v_retval)
			iavf_mac_add_ok(adapter);
		if (!ether_addr_equal(netdev->dev_addr, adapter->hw.mac.addr))
			if (!ether_addr_equal(netdev->dev_addr,
					      adapter->hw.mac.addr)) {
				netif_addr_lock_bh(netdev);
				eth_hw_addr_set(netdev, adapter->hw.mac.addr);
				netif_addr_unlock_bh(netdev);
			}
		wake_up(&adapter->vc_waitqueue);
		break;
	case VIRTCHNL_OP_GET_STATS: {
		struct iavf_eth_stats *stats =
@@ -2121,10 +2133,11 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
			/* restore current mac address */
			ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
		} else {
			netif_addr_lock_bh(netdev);
			/* refresh current mac address if changed */
			eth_hw_addr_set(netdev, adapter->hw.mac.addr);
			ether_addr_copy(netdev->perm_addr,
					adapter->hw.mac.addr);
			netif_addr_unlock_bh(netdev);
		}
		spin_lock_bh(&adapter->mac_vlan_list_lock);
		iavf_add_filter(adapter, adapter->hw.mac.addr);
@@ -2160,6 +2173,10 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
		}
		fallthrough;
	case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS: {
		struct iavf_mac_filter *f;
		bool was_mac_changed;
		u64 aq_required = 0;

		if (v_opcode == VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS)
			memcpy(&adapter->vlan_v2_caps, msg,
			       min_t(u16, msglen,
@@ -2167,6 +2184,46 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,

		iavf_process_config(adapter);
		adapter->flags |= IAVF_FLAG_SETUP_NETDEV_FEATURES;
		was_mac_changed = !ether_addr_equal(netdev->dev_addr,
						    adapter->hw.mac.addr);

		spin_lock_bh(&adapter->mac_vlan_list_lock);

		/* re-add all MAC filters */
		list_for_each_entry(f, &adapter->mac_filter_list, list) {
			if (was_mac_changed &&
			    ether_addr_equal(netdev->dev_addr, f->macaddr))
				ether_addr_copy(f->macaddr,
						adapter->hw.mac.addr);

			f->is_new_mac = true;
			f->add = true;
			f->add_handled = false;
			f->remove = false;
		}

		/* re-add all VLAN filters */
		if (VLAN_FILTERING_ALLOWED(adapter)) {
			struct iavf_vlan_filter *vlf;

			if (!list_empty(&adapter->vlan_filter_list)) {
				list_for_each_entry(vlf,
						    &adapter->vlan_filter_list,
						    list)
					vlf->add = true;

				aq_required |= IAVF_FLAG_AQ_ADD_VLAN_FILTER;
			}
		}

		spin_unlock_bh(&adapter->mac_vlan_list_lock);

		netif_addr_lock_bh(netdev);
		eth_hw_addr_set(netdev, adapter->hw.mac.addr);
		netif_addr_unlock_bh(netdev);

		adapter->aq_required |= IAVF_FLAG_AQ_ADD_MAC_FILTER |
			aq_required;
		}
		break;
	case VIRTCHNL_OP_ENABLE_QUEUES: