teamd: lacp: set port to disabled state during removal

Currently, the disabled state is set only after port is removed from
team master in kernel. Team driver puts the port netdevice down right
away. In some cases, there is nice to send LACPDU to the partner with
flags set accordingly for the disabled port.

Introduce "port_removing" op and call it right before kernel
is asked to remove the port. Implement the op in LACP runner
to disable the port which leads to proper LACPDU send.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
diff --git a/teamd/teamd.h b/teamd/teamd.h
index f94c918..541d2a7 100644
--- a/teamd/teamd.h
+++ b/teamd/teamd.h
@@ -180,6 +180,8 @@
 	int (*admin_state_changed)(struct teamd_context *ctx, void *priv);
 	int (*port_added)(struct teamd_context *ctx,
 			  struct teamd_port *tdport, void *priv);
+	void (*port_removing)(struct teamd_context *ctx,
+			      struct teamd_port *tdport, void *priv);
 	void (*port_removed)(struct teamd_context *ctx,
 			     struct teamd_port *tdport, void *priv);
 	int (*port_changed)(struct teamd_context *ctx,
@@ -200,6 +202,8 @@
 
 int teamd_event_port_added(struct teamd_context *ctx,
 			   struct teamd_port *tdport);
+void teamd_event_port_removing(struct teamd_context *ctx,
+			       struct teamd_port *tdport);
 void teamd_event_port_removed(struct teamd_context *ctx,
 			      struct teamd_port *tdport);
 int teamd_event_port_changed(struct teamd_context *ctx,
diff --git a/teamd/teamd_events.c b/teamd/teamd_events.c
index 65aa46a..eac1759 100644
--- a/teamd/teamd_events.c
+++ b/teamd/teamd_events.c
@@ -50,6 +50,18 @@
 	return 0;
 }
 
+void teamd_event_port_removing(struct teamd_context *ctx,
+			       struct teamd_port *tdport)
+{
+	struct event_watch_item *watch;
+
+	list_for_each_node_entry(watch, &ctx->event_watch_list, list) {
+		if (!watch->ops->port_removing)
+			continue;
+		watch->ops->port_removing(ctx, tdport, watch->priv);
+	}
+}
+
 void teamd_event_port_removed(struct teamd_context *ctx,
 			      struct teamd_port *tdport)
 {
diff --git a/teamd/teamd_per_port.c b/teamd/teamd_per_port.c
index 9689df4..c9c754e 100644
--- a/teamd/teamd_per_port.c
+++ b/teamd/teamd_per_port.c
@@ -351,6 +351,7 @@
 
 	teamd_log_dbg(ctx, "%s: Removing port (found ifindex \"%d\").",
 		      tdport->ifname, tdport->ifindex);
+	teamd_event_port_removing(ctx, tdport);
 	err = team_port_remove(ctx->th, tdport->ifindex);
 	if (err)
 		teamd_log_err("%s: Failed to remove port.", tdport->ifname);
diff --git a/teamd/teamd_runner_lacp.c b/teamd/teamd_runner_lacp.c
index d414bb4..51c7714 100644
--- a/teamd/teamd_runner_lacp.c
+++ b/teamd/teamd_runner_lacp.c
@@ -1420,6 +1420,17 @@
 	return teamd_balancer_port_added(lacp->tb, tdport);
 }
 
+static void lacp_event_watch_port_removing(struct teamd_context *ctx,
+					   struct teamd_port *tdport, void *priv)
+{
+	struct lacp *lacp = priv;
+	struct lacp_port *lacp_port = lacp_port_get(lacp, tdport);
+
+	/* Ensure that no incoming LACPDU is going to be processed. */
+	teamd_loop_callback_disable(ctx, LACP_SOCKET_CB_NAME, lacp_port);
+	lacp_port_set_state(lacp_port, PORT_STATE_DISABLED);
+}
+
 static void lacp_event_watch_port_removed(struct teamd_context *ctx,
 					  struct teamd_port *tdport, void *priv)
 {
@@ -1441,6 +1452,7 @@
 	.hwaddr_changed = lacp_event_watch_hwaddr_changed,
 	.port_hwaddr_changed = lacp_event_watch_port_hwaddr_changed,
 	.port_added = lacp_event_watch_port_added,
+	.port_removing = lacp_event_watch_port_removing,
 	.port_removed = lacp_event_watch_port_removed,
 	.port_changed = lacp_event_watch_port_changed,
 	.admin_state_changed = lacp_event_watch_admin_state_changed,