diff mbox series

[net-next,8/9] nfp: flower: ensure MAC cleanup on address change

Message ID 20190116030659.27755-9-jakub.kicinski@netronome.com
State Accepted
Delegated to: David Miller
Headers show
Series nfp: flower: improve flower resilience | expand

Commit Message

Jakub Kicinski Jan. 16, 2019, 3:06 a.m. UTC
From: John Hurley <john.hurley@netronome.com>

It is possible to receive a MAC address change notification without the
net device being down (e.g. when an OvS bridge is assigned the same MAC as
a port added to it). This means that an offloaded MAC address may not be
removed if its device gets a new address.

Maintain a record of the offloaded MAC addresses for each repr and netdev
assigned a MAC offload index. Use this to delete the (now expired) MAC if
a change of address event occurs. Only handle change address events if the
device is already up - if not then the netdev up event will handle it.

Signed-off-by: John Hurley <john.hurley@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 .../net/ethernet/netronome/nfp/flower/main.h  |   8 ++
 .../netronome/nfp/flower/tunnel_conf.c        | 106 ++++++++++++++++--
 2 files changed, 104 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h
index 2ce898bfc5be..15b794d0d1e4 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/main.h
+++ b/drivers/net/ethernet/netronome/nfp/flower/main.h
@@ -179,9 +179,13 @@  struct nfp_flower_priv {
 /**
  * struct nfp_flower_repr_priv - Flower APP per-repr priv data
  * @lag_port_flags:	Extended port flags to record lag state of repr
+ * @mac_offloaded:	Flag indicating a MAC address is offloaded for repr
+ * @offloaded_mac_addr:	MAC address that has been offloaded for repr
  */
 struct nfp_flower_repr_priv {
 	unsigned long lag_port_flags;
+	bool mac_offloaded;
+	u8 offloaded_mac_addr[ETH_ALEN];
 };
 
 /**
@@ -189,11 +193,15 @@  struct nfp_flower_repr_priv {
  * @list:		List entry of offloaded reprs
  * @netdev:		Pointer to non-repr net_device
  * @ref_count:		Number of references held for this priv data
+ * @mac_offloaded:	Flag indicating a MAC address is offloaded for device
+ * @offloaded_mac_addr:	MAC address that has been offloaded for dev
  */
 struct nfp_flower_non_repr_priv {
 	struct list_head list;
 	struct net_device *netdev;
 	int ref_count;
+	bool mac_offloaded;
+	u8 offloaded_mac_addr[ETH_ALEN];
 };
 
 struct nfp_fl_key_ls {
diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
index 46d8a222bd55..9d87c88507f3 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
@@ -117,6 +117,7 @@  struct nfp_tun_mac_addr_offload {
 enum nfp_flower_mac_offload_cmd {
 	NFP_TUNNEL_MAC_OFFLOAD_ADD =		0,
 	NFP_TUNNEL_MAC_OFFLOAD_DEL =		1,
+	NFP_TUNNEL_MAC_OFFLOAD_MOD =		2,
 };
 
 #define NFP_MAX_MAC_INDEX       0xff
@@ -568,46 +569,121 @@  static int
 nfp_tunnel_offload_mac(struct nfp_app *app, struct net_device *netdev,
 		       enum nfp_flower_mac_offload_cmd cmd)
 {
-	bool non_repr = false;
+	struct nfp_flower_non_repr_priv *nr_priv = NULL;
+	bool non_repr = false, *mac_offloaded;
+	u8 *off_mac = NULL;
 	int err, port = 0;
 	u16 nfp_mac_idx;
 
 	if (nfp_netdev_is_nfp_repr(netdev)) {
+		struct nfp_flower_repr_priv *repr_priv;
 		struct nfp_repr *repr;
 
 		repr = netdev_priv(netdev);
 		if (repr->app != app)
 			return 0;
 
+		repr_priv = repr->app_priv;
+		mac_offloaded = &repr_priv->mac_offloaded;
+		off_mac = &repr_priv->offloaded_mac_addr[0];
 		port = nfp_repr_get_port_id(netdev);
 	} else if (nfp_fl_is_netdev_to_offload(netdev)) {
+		nr_priv = nfp_flower_non_repr_priv_get(app, netdev);
+		if (!nr_priv)
+			return -ENOMEM;
+
+		mac_offloaded = &nr_priv->mac_offloaded;
+		off_mac = &nr_priv->offloaded_mac_addr[0];
 		non_repr = true;
 	} else {
 		return 0;
 	}
 
-	if (!is_valid_ether_addr(netdev->dev_addr))
-		return -EINVAL;
+	if (!is_valid_ether_addr(netdev->dev_addr)) {
+		err = -EINVAL;
+		goto err_put_non_repr_priv;
+	}
+
+	if (cmd == NFP_TUNNEL_MAC_OFFLOAD_MOD && !*mac_offloaded)
+		cmd = NFP_TUNNEL_MAC_OFFLOAD_ADD;
 
 	switch (cmd) {
 	case NFP_TUNNEL_MAC_OFFLOAD_ADD:
 		err = nfp_tunnel_get_mac_idx_from_port(app, netdev, port,
 						       &nfp_mac_idx);
 		if (err)
-			return err;
+			goto err_put_non_repr_priv;
+
+		err = __nfp_tunnel_offload_mac(app, netdev->dev_addr,
+					       nfp_mac_idx, false);
+		if (err)
+			goto err_free_mac_idx;
 
-		return __nfp_tunnel_offload_mac(app, netdev->dev_addr,
-						nfp_mac_idx, false);
-	case NFP_TUNNEL_MAC_OFFLOAD_DEL:
 		if (non_repr)
+			__nfp_flower_non_repr_priv_get(nr_priv);
+
+		*mac_offloaded = true;
+		ether_addr_copy(off_mac, netdev->dev_addr);
+		break;
+	case NFP_TUNNEL_MAC_OFFLOAD_DEL:
+		/* Only attempt delete if add was successful. */
+		if (!*mac_offloaded)
+			break;
+
+		if (non_repr) {
 			nfp_tun_del_mac_idx(app, netdev->ifindex);
+			__nfp_flower_non_repr_priv_put(nr_priv);
+		}
+
+		*mac_offloaded = false;
+
+		err =  __nfp_tunnel_offload_mac(app, netdev->dev_addr, 0, true);
+		if (err)
+			goto err_put_non_repr_priv;
+
+		break;
+	case NFP_TUNNEL_MAC_OFFLOAD_MOD:
+		/* Ignore if changing to the same address. */
+		if (ether_addr_equal(netdev->dev_addr, off_mac))
+			break;
 
-		return __nfp_tunnel_offload_mac(app, netdev->dev_addr, 0, true);
+		err = nfp_tunnel_get_mac_idx_from_port(app, netdev, port,
+						       &nfp_mac_idx);
+		if (err)
+			goto err_put_non_repr_priv;
+
+		err = __nfp_tunnel_offload_mac(app, netdev->dev_addr,
+					       nfp_mac_idx, false);
+		if (err)
+			goto err_put_non_repr_priv;
+
+		/* Delete the previous MAC address. */
+		err = __nfp_tunnel_offload_mac(app, off_mac, nfp_mac_idx,
+					       true);
+		if (err)
+			nfp_flower_cmsg_warn(app, "Failed to remove offload of replaced MAC addr on %s.\n",
+					     netdev_name(netdev));
+
+		ether_addr_copy(off_mac, netdev->dev_addr);
+		break;
 	default:
-		return -EINVAL;
+		err = -EINVAL;
+		goto err_put_non_repr_priv;
 	}
 
+	if (non_repr)
+		__nfp_flower_non_repr_priv_put(nr_priv);
+
 	return 0;
+
+err_free_mac_idx:
+	if (non_repr)
+		nfp_tun_del_mac_idx(app, netdev->ifindex);
+err_put_non_repr_priv:
+	if (non_repr)
+		__nfp_flower_non_repr_priv_put(nr_priv);
+
+	return err;
 }
 
 int nfp_tunnel_mac_event_handler(struct nfp_app *app,
@@ -622,12 +698,22 @@  int nfp_tunnel_mac_event_handler(struct nfp_app *app,
 		if (err)
 			nfp_flower_cmsg_warn(app, "Failed to delete offload MAC on %s.\n",
 					     netdev_name(netdev));
-	} else if (event == NETDEV_UP || event == NETDEV_CHANGEADDR) {
+	} else if (event == NETDEV_UP) {
 		err = nfp_tunnel_offload_mac(app, netdev,
 					     NFP_TUNNEL_MAC_OFFLOAD_ADD);
 		if (err)
 			nfp_flower_cmsg_warn(app, "Failed to offload MAC on %s.\n",
 					     netdev_name(netdev));
+	} else if (event == NETDEV_CHANGEADDR) {
+		/* Only offload addr change if netdev is already up. */
+		if (!(netdev->flags & IFF_UP))
+			return NOTIFY_OK;
+
+		err = nfp_tunnel_offload_mac(app, netdev,
+					     NFP_TUNNEL_MAC_OFFLOAD_MOD);
+		if (err)
+			nfp_flower_cmsg_warn(app, "Failed to offload MAC change on %s.\n",
+					     netdev_name(netdev));
 	}
 	return NOTIFY_OK;
 }