From af6bd33a72059d7851baf76efe8f5f4e1310cf5b Mon Sep 17 00:00:00 2001 From: Pavel Shirshov Date: Mon, 15 Oct 2018 14:22:46 -0700 Subject: [PATCH 1/3] Add Warm-reboot startup mode for teamd. --- dockers/docker-teamd/teamd.sh | 2 +- .../0005-libteam-Add-warm_reboot-mode.patch | 133 ++++++++++++++++++ src/libteam/Makefile | 1 + 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 src/libteam/0005-libteam-Add-warm_reboot-mode.patch diff --git a/dockers/docker-teamd/teamd.sh b/dockers/docker-teamd/teamd.sh index 2b6d9fb53970..5c87de508764 100755 --- a/dockers/docker-teamd/teamd.sh +++ b/dockers/docker-teamd/teamd.sh @@ -6,7 +6,7 @@ function start_app { rm -f /var/run/teamd/* if [ "$(ls -A $TEAMD_CONF_PATH)" ]; then for f in $TEAMD_CONF_PATH/*; do - teamd -f $f -d + teamd -f $f -d -N -o done fi teamsyncd & diff --git a/src/libteam/0005-libteam-Add-warm_reboot-mode.patch b/src/libteam/0005-libteam-Add-warm_reboot-mode.patch new file mode 100644 index 000000000000..f761c12038a6 --- /dev/null +++ b/src/libteam/0005-libteam-Add-warm_reboot-mode.patch @@ -0,0 +1,133 @@ +diff --git a/libteam/ifinfo.c b/libteam/ifinfo.c +index 72155ae..b281677 100644 +--- a/libteam/ifinfo.c ++++ b/libteam/ifinfo.c +@@ -55,6 +55,7 @@ struct team_ifinfo { + size_t hwaddr_len; + char orig_hwaddr[MAX_ADDR_LEN]; + size_t orig_hwaddr_len; ++ bool is_orig_hwaddr_set; + char ifname[IFNAMSIZ]; + uint32_t master_ifindex; + bool admin_state; +@@ -105,15 +106,17 @@ static void update_hwaddr(struct team_ifinfo *ifinfo, struct rtnl_link *link) + hwaddr_len = nl_addr_get_len(nl_addr); + if (ifinfo->hwaddr_len != hwaddr_len) { + ifinfo->hwaddr_len = hwaddr_len; +- if (!ifinfo->master_ifindex) ++ if (!ifinfo->is_orig_hwaddr_set) + ifinfo->orig_hwaddr_len = hwaddr_len; + set_changed(ifinfo, CHANGED_HWADDR_LEN); + } + hwaddr = nl_addr_get_binary_addr(nl_addr); + if (memcmp(ifinfo->hwaddr, hwaddr, hwaddr_len)) { + memcpy(ifinfo->hwaddr, hwaddr, hwaddr_len); +- if (!ifinfo->master_ifindex) ++ if (!ifinfo->is_orig_hwaddr_set) { + memcpy(ifinfo->orig_hwaddr, hwaddr, hwaddr_len); ++ ifinfo->is_orig_hwaddr_set = true; ++ } + set_changed(ifinfo, CHANGED_HWADDR); + } + } +diff --git a/teamd/teamd.c b/teamd/teamd.c +index c987333..10914b8 100644 +--- a/teamd/teamd.c ++++ b/teamd/teamd.c +@@ -116,7 +116,8 @@ static void print_help(const struct teamd_context *ctx) { + " -D --dbus-enable Enable D-Bus interface\n" + " -Z --zmq-enable=ADDRESS Enable ZeroMQ interface\n" + " -U --usock-enable Enable UNIX domain socket interface\n" +- " -u --usock-disable Disable UNIX domain socket interface\n", ++ " -u --usock-disable Disable UNIX domain socket interface\n" ++ " -w --warm-reboot Warm-reboot startup mode\n", + ctx->argv0); + printf("Available runners: "); + for (i = 0; i < TEAMD_RUNNER_LIST_SIZE; i++) { +@@ -149,10 +150,11 @@ static int parse_command_line(struct teamd_context *ctx, + { "zmq-enable", required_argument, NULL, 'Z' }, + { "usock-enable", no_argument, NULL, 'U' }, + { "usock-disable", no_argument, NULL, 'u' }, ++ { "warm-reboot", no_argument, NULL, 'w' }, + { NULL, 0, NULL, 0 } + }; + +- while ((opt = getopt_long(argc, argv, "hdkevf:c:p:groNt:nDZ:Uu", ++ while ((opt = getopt_long(argc, argv, "hdkevf:c:p:groNt:nDZ:Uuw", + long_options, NULL)) >= 0) { + + switch(opt) { +@@ -230,6 +232,9 @@ static int parse_command_line(struct teamd_context *ctx, + case 'u': + ctx->usock.enabled = false; + break; ++ case 'w': ++ ctx->warm_reboot = true; ++ break; + default: + return -1; + } +diff --git a/teamd/teamd.h b/teamd/teamd.h +index ef0fb1c..36b80de 100644 +--- a/teamd/teamd.h ++++ b/teamd/teamd.h +@@ -125,6 +125,7 @@ struct teamd_context { + char * hwaddr; + uint32_t hwaddr_len; + bool hwaddr_explicit; ++ bool warm_reboot; + struct { + struct list_item callback_list; + int ctrl_pipe_r; +diff --git a/teamd/teamd_runner_lacp.c b/teamd/teamd_runner_lacp.c +index 81324de..56b8740 100644 +--- a/teamd/teamd_runner_lacp.c ++++ b/teamd/teamd_runner_lacp.c +@@ -174,6 +174,7 @@ struct lacp_port { + struct lacp_port *agg_lead; /* leading port of aggregator. + * NULL in case this port is not selected */ + enum lacp_port_state state; ++ bool lacpdu_received; + struct { + uint32_t speed; + uint8_t duplex; +@@ -1080,6 +1081,14 @@ static int lacpdu_send(struct lacp_port *lacp_port) + memcpy(lacpdu.hdr.ether_dhost, ll_slow.sll_addr, ll_slow.sll_halen); + lacpdu.hdr.ether_type = htons(ETH_P_SLOW); + ++ if (lacp_port->ctx->warm_reboot && !lacp_port->lacpdu_received && !(lacp_port->actor.state & INFO_STATE_SYNCHRONIZATION)) { ++ /* don't send lacpdu update in warm-reboot mode unless we receive lacp pdu from the partner */ ++ return 0; ++ } else { ++ /* we have syncronized with the partner. Now we can send lacp pdu any time */ ++ lacp_port->lacpdu_received = true; ++ } ++ + err = teamd_send(lacp_port->sock, &lacpdu, sizeof(lacpdu), 0); + return err; + } +@@ -1144,7 +1153,10 @@ static int lacp_callback_timeout(struct teamd_context *ctx, int events, + err = lacp_port_set_state(lacp_port, PORT_STATE_EXPIRED); + break; + case PORT_STATE_EXPIRED: +- err = lacp_port_set_state(lacp_port, PORT_STATE_DEFAULTED); ++ if (!lacp_port->ctx->warm_reboot) { ++ /* Don't transition into DEFAULT state in warm_reboot mode, which prevents port syncing */ ++ err = lacp_port_set_state(lacp_port, PORT_STATE_DEFAULTED); ++ } + break; + case PORT_STATE_DEFAULTED: + case PORT_STATE_DISABLED: +@@ -1321,7 +1333,11 @@ static void lacp_port_removed(struct teamd_context *ctx, + { + struct lacp_port *lacp_port = priv; + +- lacp_port_set_state(lacp_port, PORT_STATE_DISABLED); ++ if (!lacp_port->ctx->warm_reboot) { ++ /* Don't transition into DISABLED state in warm-reboot mode, ++ teamd will sends EXPIRED LACP PDU update in DISABLED state */ ++ lacp_port_set_state(lacp_port, PORT_STATE_DISABLED); ++ } + teamd_loop_callback_del(ctx, LACP_TIMEOUT_CB_NAME, lacp_port); + teamd_loop_callback_del(ctx, LACP_PERIODIC_CB_NAME, lacp_port); + teamd_loop_callback_del(ctx, LACP_SOCKET_CB_NAME, lacp_port); diff --git a/src/libteam/Makefile b/src/libteam/Makefile index a20f8dbd35b0..50ad002c50c6 100644 --- a/src/libteam/Makefile +++ b/src/libteam/Makefile @@ -19,6 +19,7 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : git apply ../0002-libteam-Temporarily-remove-redundant-debug-mes.patch git apply ../0003-teamd-lacp-runner-will-send-lacp-update-right-after-.patch git apply ../0004-libteam-Add-lacp-fallback-support-for-single-member-.patch + git apply ../0005-libteam-Add-warm_reboot-mode.patch popd # Obtain debian packaging From e8ac907419695ede34d6e15f873a6d6e20f96ce9 Mon Sep 17 00:00:00 2001 From: Pavel Shirshov Date: Mon, 15 Oct 2018 14:24:12 -0700 Subject: [PATCH 2/3] Always start in warm reboot mode --- dockers/docker-teamd/teamd.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockers/docker-teamd/teamd.sh b/dockers/docker-teamd/teamd.sh index 5c87de508764..58b2f54eeec1 100755 --- a/dockers/docker-teamd/teamd.sh +++ b/dockers/docker-teamd/teamd.sh @@ -6,7 +6,7 @@ function start_app { rm -f /var/run/teamd/* if [ "$(ls -A $TEAMD_CONF_PATH)" ]; then for f in $TEAMD_CONF_PATH/*; do - teamd -f $f -d -N -o + teamd -f $f -d -N -o -w done fi teamsyncd & From 04753aa8fd6b3b6ff923e32613346b5a05f08bfc Mon Sep 17 00:00:00 2001 From: Pavel Shirshov Date: Fri, 19 Oct 2018 18:53:31 -0700 Subject: [PATCH 3/3] Separate start and shutdown functions --- .../0005-libteam-Add-warm_reboot-mode.patch | 319 +++++++++++++++--- 1 file changed, 270 insertions(+), 49 deletions(-) diff --git a/src/libteam/0005-libteam-Add-warm_reboot-mode.patch b/src/libteam/0005-libteam-Add-warm_reboot-mode.patch index f761c12038a6..5993c3f1539b 100644 --- a/src/libteam/0005-libteam-Add-warm_reboot-mode.patch +++ b/src/libteam/0005-libteam-Add-warm_reboot-mode.patch @@ -1,21 +1,21 @@ diff --git a/libteam/ifinfo.c b/libteam/ifinfo.c -index 72155ae..b281677 100644 +index 72155ae..266cc21 100644 --- a/libteam/ifinfo.c +++ b/libteam/ifinfo.c -@@ -55,6 +55,7 @@ struct team_ifinfo { - size_t hwaddr_len; - char orig_hwaddr[MAX_ADDR_LEN]; - size_t orig_hwaddr_len; -+ bool is_orig_hwaddr_set; +@@ -58,6 +58,7 @@ struct team_ifinfo { char ifname[IFNAMSIZ]; uint32_t master_ifindex; bool admin_state; ++ bool orig_addr_updated; /* FIXME: Check this. I think we don't need this flag */ + #define MAX_PHYS_PORT_ID_LEN 32 + char phys_port_id[MAX_PHYS_PORT_ID_LEN]; + size_t phys_port_id_len; @@ -105,15 +106,17 @@ static void update_hwaddr(struct team_ifinfo *ifinfo, struct rtnl_link *link) hwaddr_len = nl_addr_get_len(nl_addr); if (ifinfo->hwaddr_len != hwaddr_len) { ifinfo->hwaddr_len = hwaddr_len; - if (!ifinfo->master_ifindex) -+ if (!ifinfo->is_orig_hwaddr_set) ++ if (!ifinfo->orig_addr_updated) ifinfo->orig_hwaddr_len = hwaddr_len; set_changed(ifinfo, CHANGED_HWADDR_LEN); } @@ -23,111 +23,332 @@ index 72155ae..b281677 100644 if (memcmp(ifinfo->hwaddr, hwaddr, hwaddr_len)) { memcpy(ifinfo->hwaddr, hwaddr, hwaddr_len); - if (!ifinfo->master_ifindex) -+ if (!ifinfo->is_orig_hwaddr_set) { ++ if (!ifinfo->orig_addr_updated) { memcpy(ifinfo->orig_hwaddr, hwaddr, hwaddr_len); -+ ifinfo->is_orig_hwaddr_set = true; -+ } ++ ifinfo->orig_addr_updated = true; ++ } set_changed(ifinfo, CHANGED_HWADDR); } } diff --git a/teamd/teamd.c b/teamd/teamd.c -index c987333..10914b8 100644 +index c987333..37c2fb3 100644 --- a/teamd/teamd.c +++ b/teamd/teamd.c -@@ -116,7 +116,8 @@ static void print_help(const struct teamd_context *ctx) { +@@ -116,7 +116,9 @@ static void print_help(const struct teamd_context *ctx) { " -D --dbus-enable Enable D-Bus interface\n" " -Z --zmq-enable=ADDRESS Enable ZeroMQ interface\n" " -U --usock-enable Enable UNIX domain socket interface\n" - " -u --usock-disable Disable UNIX domain socket interface\n", + " -u --usock-disable Disable UNIX domain socket interface\n" -+ " -w --warm-reboot Warm-reboot startup mode\n", ++ " -w --warm-reboot Warm-reboot startup mode\n" ++ " -L --lacp-directory Directory for saving lacp pdu dumps\n", ctx->argv0); printf("Available runners: "); for (i = 0; i < TEAMD_RUNNER_LIST_SIZE; i++) { -@@ -149,10 +150,11 @@ static int parse_command_line(struct teamd_context *ctx, +@@ -149,10 +151,12 @@ static int parse_command_line(struct teamd_context *ctx, { "zmq-enable", required_argument, NULL, 'Z' }, { "usock-enable", no_argument, NULL, 'U' }, { "usock-disable", no_argument, NULL, 'u' }, + { "warm-reboot", no_argument, NULL, 'w' }, ++ { "lacp-directory", required_argument, NULL, 'L' }, { NULL, 0, NULL, 0 } }; - while ((opt = getopt_long(argc, argv, "hdkevf:c:p:groNt:nDZ:Uu", -+ while ((opt = getopt_long(argc, argv, "hdkevf:c:p:groNt:nDZ:Uuw", ++ while ((opt = getopt_long(argc, argv, "hdkevf:c:p:groNt:nDZ:UuwL:", long_options, NULL)) >= 0) { switch(opt) { -@@ -230,6 +232,9 @@ static int parse_command_line(struct teamd_context *ctx, +@@ -230,6 +234,12 @@ static int parse_command_line(struct teamd_context *ctx, case 'u': ctx->usock.enabled = false; break; + case 'w': + ctx->warm_reboot = true; ++ break; ++ case 'L': ++ ctx->lacp_directory = strdup(optarg); + break; default: return -1; } +@@ -384,8 +394,14 @@ static int teamd_run_loop_run(struct teamd_context *ctx) + if (err != -1) { + switch(ctrl_byte) { + case 'q': ++ case 'w': + if (quit_in_progress) + return -EBUSY; ++ if (ctrl_byte == 'w') { ++ ctx->keep_ports = true; ++ ctx->no_quit_destroy = true; ++ teamd_ports_flush_data(ctx); ++ } + teamd_refresh_ports(ctx); + err = teamd_flush_ports(ctx); + if (err) +@@ -428,6 +444,12 @@ void teamd_run_loop_quit(struct teamd_context *ctx, int err) + teamd_run_loop_sent_ctrl_byte(ctx, 'q'); + } + ++static void teamd_run_loop_quit_w_boot(struct teamd_context *ctx, int err) ++{ ++ ctx->run_loop.err = err; ++ teamd_run_loop_sent_ctrl_byte(ctx, 'w'); ++} ++ + void teamd_run_loop_restart(struct teamd_context *ctx) + { + teamd_run_loop_sent_ctrl_byte(ctx, 'r'); +@@ -694,6 +716,10 @@ static int callback_daemon_signal(struct teamd_context *ctx, int events, + teamd_log_warn("Got SIGINT, SIGQUIT or SIGTERM."); + teamd_run_loop_quit(ctx, 0); + break; ++ case SIGUSR1: ++ teamd_log_warn("Got SIGUSR1."); ++ teamd_run_loop_quit_w_boot(ctx, 0); ++ break; + } + return 0; + } +@@ -1507,7 +1533,7 @@ static int teamd_start(struct teamd_context *ctx, enum teamd_exit_code *p_ret) + return -errno; + } + +- if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, 0) < 0) { ++ if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGUSR1, 0) < 0) { + teamd_log_err("Could not register signal handlers."); + daemon_retval_send(errno); + err = -errno; diff --git a/teamd/teamd.h b/teamd/teamd.h -index ef0fb1c..36b80de 100644 +index ef0fb1c..503d9db 100644 --- a/teamd/teamd.h +++ b/teamd/teamd.h -@@ -125,6 +125,7 @@ struct teamd_context { +@@ -125,6 +125,9 @@ struct teamd_context { char * hwaddr; uint32_t hwaddr_len; bool hwaddr_explicit; -+ bool warm_reboot; ++ bool warm_reboot; ++ bool keep_ports; ++ char * lacp_directory; struct { struct list_item callback_list; int ctrl_pipe_r; +@@ -191,12 +194,15 @@ struct teamd_event_watch_ops { + struct teamd_port *tdport, void *priv); + void (*refresh)(struct teamd_context *ctx, + struct teamd_port *tdport, void *priv); ++ void (*port_flush_data)(struct teamd_context *ctx, ++ struct teamd_port *tdport, void *priv); + int (*option_changed)(struct teamd_context *ctx, + struct team_option *option, void *priv); + char *option_changed_match_name; + }; + + void teamd_refresh_ports(struct teamd_context *ctx); ++void teamd_ports_flush_data(struct teamd_context *ctx); + int teamd_event_port_added(struct teamd_context *ctx, + struct teamd_port *tdport); + void teamd_event_port_removed(struct teamd_context *ctx, +diff --git a/teamd/teamd_events.c b/teamd/teamd_events.c +index 5c2ef56..50e5a08 100644 +--- a/teamd/teamd_events.c ++++ b/teamd/teamd_events.c +@@ -47,6 +47,19 @@ void teamd_refresh_ports(struct teamd_context *ctx) + } + } + ++void teamd_ports_flush_data(struct teamd_context *ctx) ++{ ++ struct teamd_port *tdport; ++ struct event_watch_item *watch; ++ ++ teamd_for_each_tdport(tdport, ctx) { ++ list_for_each_node_entry(watch, &ctx->event_watch_list, list) { ++ if (!watch->ops->port_flush_data) continue; ++ watch->ops->port_flush_data(ctx, tdport, watch->priv); ++ } ++ } ++} ++ + int teamd_event_port_added(struct teamd_context *ctx, + struct teamd_port *tdport) + { diff --git a/teamd/teamd_runner_lacp.c b/teamd/teamd_runner_lacp.c -index 81324de..56b8740 100644 +index 81324de..45d667d 100644 --- a/teamd/teamd_runner_lacp.c +++ b/teamd/teamd_runner_lacp.c -@@ -174,6 +174,7 @@ struct lacp_port { +@@ -174,6 +174,9 @@ struct lacp_port { struct lacp_port *agg_lead; /* leading port of aggregator. * NULL in case this port is not selected */ enum lacp_port_state state; + bool lacpdu_received; ++ bool lacpdu_saved; ++ struct lacpdu last_pdu; struct { uint32_t speed; uint8_t duplex; -@@ -1080,6 +1081,14 @@ static int lacpdu_send(struct lacp_port *lacp_port) - memcpy(lacpdu.hdr.ether_dhost, ll_slow.sll_addr, ll_slow.sll_halen); - lacpdu.hdr.ether_type = htons(ETH_P_SLOW); +@@ -1084,26 +1087,23 @@ static int lacpdu_send(struct lacp_port *lacp_port) + return err; + } -+ if (lacp_port->ctx->warm_reboot && !lacp_port->lacpdu_received && !(lacp_port->actor.state & INFO_STATE_SYNCHRONIZATION)) { -+ /* don't send lacpdu update in warm-reboot mode unless we receive lacp pdu from the partner */ -+ return 0; -+ } else { -+ /* we have syncronized with the partner. Now we can send lacp pdu any time */ -+ lacp_port->lacpdu_received = true; -+ } +-static int lacpdu_recv(struct lacp_port *lacp_port) ++static int lacpdu_process(struct lacp_port *lacp_port, struct lacpdu* lacpdu) + { +- struct lacpdu lacpdu; +- struct sockaddr_ll ll_from; + int err; + +- err = teamd_recvfrom(lacp_port->sock, &lacpdu, sizeof(lacpdu), 0, +- (struct sockaddr *) &ll_from, sizeof(ll_from)); +- if (err <= 0) +- return err; +- +- if (!lacpdu_check(&lacpdu)) { ++ if (!lacpdu_check(lacpdu)) { + teamd_log_warn("malformed LACP PDU came."); + return 0; + } + ++ /* save received lacp pdu frame */ ++ (void)memcpy(&lacp_port->last_pdu, lacpdu, sizeof(struct lacpdu)); ++ lacp_port->lacpdu_saved = true; + - err = teamd_send(lacp_port->sock, &lacpdu, sizeof(lacpdu), 0); - return err; + /* Check if we have correct info about the other side */ +- if (memcmp(&lacpdu.actor, &lacp_port->partner, ++ if (memcmp(&lacpdu->actor, &lacp_port->partner, + sizeof(struct lacpdu_info))) { +- lacp_port->partner = lacpdu.actor; ++ lacp_port->partner = lacpdu->actor; + err = lacp_port_partner_update(lacp_port); + if (err) + return err; +@@ -1118,7 +1118,7 @@ static int lacpdu_recv(struct lacp_port *lacp_port) + + /* Check if the other side has correct info about us */ + if (!lacp_port->periodic_on && +- memcmp(&lacpdu.partner, &lacp_port->actor, ++ memcmp(&lacpdu->partner, &lacp_port->actor, + sizeof(struct lacpdu_info))) { + err = lacpdu_send(lacp_port); + if (err) +@@ -1133,6 +1133,47 @@ static int lacpdu_recv(struct lacp_port *lacp_port) + return 0; } -@@ -1144,7 +1153,10 @@ static int lacp_callback_timeout(struct teamd_context *ctx, int events, - err = lacp_port_set_state(lacp_port, PORT_STATE_EXPIRED); - break; - case PORT_STATE_EXPIRED: -- err = lacp_port_set_state(lacp_port, PORT_STATE_DEFAULTED); -+ if (!lacp_port->ctx->warm_reboot) { -+ /* Don't transition into DEFAULT state in warm_reboot mode, which prevents port syncing */ -+ err = lacp_port_set_state(lacp_port, PORT_STATE_DEFAULTED); -+ } - break; - case PORT_STATE_DEFAULTED: - case PORT_STATE_DISABLED: -@@ -1321,7 +1333,11 @@ static void lacp_port_removed(struct teamd_context *ctx, + ++static int lacpdu_recv(struct lacp_port *lacp_port) ++{ ++ struct lacpdu lacpdu; ++ struct sockaddr_ll ll_from; ++ int err; ++ ++ err = teamd_recvfrom(lacp_port->sock, &lacpdu, sizeof(lacpdu), 0, ++ (struct sockaddr *) &ll_from, sizeof(ll_from)); ++ if (err <= 0) ++ return err; ++ ++ return lacpdu_process(lacp_port, &lacpdu); ++} ++ ++static int lacpdu_read(struct lacp_port *lacp_port) ++{ ++ FILE* fp; ++ char path[PATH_MAX]; ++ struct lacpdu lacpdu; ++ int err; ++ ++ strcpy(path, lacp_port->ctx->lacp_directory); ++ if (path[strlen(path) - 1] != '/') ++ strcat(path, "/"); /* Add trailing slash if we don't have one in the path */ ++ strcat(path, lacp_port->tdport->ifname); ++ fp = fopen(path, "r"); ++ if (!fp) { ++ teamd_log_err("Can't open lacp-saved dump from file %s: %s", path, strerror(errno)); ++ return errno; ++ } ++ ++ err = fread(&lacpdu, sizeof(struct lacpdu), 1, fp); ++ (void)fclose(fp); ++ if (err <= 0) { ++ teamd_log_err("Can't read lacp-saved dump from file %s: %s", path, strerror(errno)); ++ return err; ++ } ++ ++ return lacpdu_process(lacp_port, &lacpdu); ++} ++ + static int lacp_callback_timeout(struct teamd_context *ctx, int events, + void *priv) + { +@@ -1299,6 +1340,11 @@ static int lacp_port_added(struct teamd_context *ctx, + lacp_port_actor_init(lacp_port); + lacp_port_link_update(lacp_port); + ++ /* Read data from file and process it */ ++ if (ctx->warm_reboot && ctx->lacp_directory) { ++ (void)lacpdu_read(lacp_port); ++ } ++ + teamd_loop_callback_enable(ctx, LACP_SOCKET_CB_NAME, lacp_port); + return 0; + +@@ -1321,7 +1367,11 @@ static void lacp_port_removed(struct teamd_context *ctx, { struct lacp_port *lacp_port = priv; - lacp_port_set_state(lacp_port, PORT_STATE_DISABLED); -+ if (!lacp_port->ctx->warm_reboot) { -+ /* Don't transition into DISABLED state in warm-reboot mode, -+ teamd will sends EXPIRED LACP PDU update in DISABLED state */ ++ if (!lacp_port->ctx->keep_ports) { ++ /* Don't transition into DISABLED state, ++ which sends EXPIRED LACP PDU update */ + lacp_port_set_state(lacp_port, PORT_STATE_DISABLED); + } teamd_loop_callback_del(ctx, LACP_TIMEOUT_CB_NAME, lacp_port); teamd_loop_callback_del(ctx, LACP_PERIODIC_CB_NAME, lacp_port); teamd_loop_callback_del(ctx, LACP_SOCKET_CB_NAME, lacp_port); +@@ -1413,6 +1463,29 @@ static void lacp_event_watch_refresh(struct teamd_context *ctx, struct teamd_por + (void) lacpdu_send(lacp_port); + } + ++static void lacp_event_watch_port_flush_data(struct teamd_context *ctx, struct teamd_port *tdport, void *priv) ++{ ++ struct lacp *lacp = priv; ++ ++ struct lacp_port *lacp_port = lacp_port_get(lacp, tdport); ++ if(lacp_port->lacpdu_saved && lacp_port->ctx->lacp_directory) { ++ char filename[PATH_MAX]; ++ strcpy(filename, lacp_port->ctx->lacp_directory); ++ strcat(filename, lacp_port->tdport->ifname); ++ FILE *fp = fopen(filename, "wb"); ++ if (fp != NULL) { ++ (void)fwrite(&lacp_port->last_pdu, sizeof(struct lacpdu), 1, fp); ++ (void)fclose(fp); ++ } else { ++ teamd_log_err("Can't open file %s for writing %s", filename, strerror(errno)); ++ } ++ } else { ++ teamd_log_err("Can't dump received lacp pdu for port %s. " ++ "Either it wasn't received, or directory to save wasn't configured", ++ lacp_port->tdport->ifname); ++ } ++} ++ + static const struct teamd_event_watch_ops lacp_event_watch_ops = { + .hwaddr_changed = lacp_event_watch_hwaddr_changed, + .port_added = lacp_event_watch_port_added, +@@ -1420,6 +1493,7 @@ static const struct teamd_event_watch_ops lacp_event_watch_ops = { + .port_changed = lacp_event_watch_port_changed, + .admin_state_changed = lacp_event_watch_admin_state_changed, + .refresh = lacp_event_watch_refresh, ++ .port_flush_data = lacp_event_watch_port_flush_data, + }; + + static int lacp_carrier_init(struct teamd_context *ctx, struct lacp *lacp) +@@ -1946,7 +2020,7 @@ static void lacp_fini(struct teamd_context *ctx, void *priv) + teamd_state_val_unregister(ctx, &lacp_state_vg, lacp); + teamd_balancer_fini(lacp->tb); + teamd_event_watch_unregister(ctx, &lacp_event_watch_ops, lacp); +- lacp_carrier_fini(ctx, lacp); ++ if (!ctx->keep_ports) lacp_carrier_fini(ctx, lacp); + } + + const struct teamd_runner teamd_runner_lacp = {