Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux.git] / drivers / net / ethernet / intel / iavf / iavf_main.c
index 2e2c153ce46a3c791b0aedc73e61077478cf50c8..7dddf9800e2f1318a7b6ff1353fab2d99efd5b53 100644 (file)
@@ -978,6 +978,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 = ether_addr_equal(macaddr, adapter->hw.mac.addr);
                adapter->aq_required |= IAVF_FLAG_AQ_ADD_MAC_FILTER;
@@ -989,47 +990,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;
 }
 
 /**
@@ -2445,6 +2531,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);
@@ -4159,7 +4247,7 @@ static netdev_features_t iavf_features_check(struct sk_buff *skb,
        }
 
        /* No need to validate L4LEN as TCP is the only protocol with a
-        * flexible value and we support all possible values supported
+        * flexible value and we support all possible values supported
         * by TCP, which is at most 15 dwords
         */
 
@@ -4678,6 +4766,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:
This page took 0.040165 seconds and 4 git commands to generate.