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 0000000000..a8f1561f9d --- /dev/null +++ b/src/libteam/0005-libteam-Add-warm_reboot-mode.patch @@ -0,0 +1,345 @@ +diff --git a/teamd/teamd.c b/teamd/teamd.c +index c987333..53aec1d 100644 +--- a/teamd/teamd.c ++++ b/teamd/teamd.c +@@ -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-start Warm-start 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++) { +@@ -129,7 +131,7 @@ static void print_help(const struct teamd_context *ctx) { + + static int parse_command_line(struct teamd_context *ctx, + int argc, char *argv[]) { +- int opt; ++ int opt, err; + static const struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "daemonize", no_argument, NULL, 'd' }, +@@ -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-start", 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:UuwL:", + long_options, NULL)) >= 0) { + + switch(opt) { +@@ -230,6 +234,17 @@ static int parse_command_line(struct teamd_context *ctx, + case 'u': + ctx->usock.enabled = false; + break; ++ case 'w': ++ ctx->warm_start = true; ++ break; ++ case 'L': ++ ctx->lacp_directory = strdup(optarg); ++ if (access(ctx->lacp_directory, R_OK | W_OK | X_OK) != 0) { ++ fprintf(stderr, "Can't write to the lacp directory '%s': %s\n", ctx->lacp_directory, strerror(errno)); ++ free(ctx->lacp_directory); ++ ctx->lacp_directory = NULL; ++ } ++ break; + default: + return -1; + } +@@ -384,8 +399,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 +449,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 +721,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 +1538,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..b1b6dfe 100644 +--- a/teamd/teamd.h ++++ b/teamd/teamd.h +@@ -125,6 +125,9 @@ struct teamd_context { + char * hwaddr; + uint32_t hwaddr_len; + bool hwaddr_explicit; ++ bool warm_start; ++ 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..1d908db 100644 +--- a/teamd/teamd_runner_lacp.c ++++ b/teamd/teamd_runner_lacp.c +@@ -174,6 +174,8 @@ 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_saved; ++ struct lacpdu last_pdu; + struct { + uint32_t speed; + uint8_t duplex; +@@ -1084,26 +1086,23 @@ static int lacpdu_send(struct lacp_port *lacp_port) + return err; + } + +-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; ++ + /* 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 +1117,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 +1132,53 @@ static int lacpdu_recv(struct lacp_port *lacp_port) + return 0; + } + ++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 filename[PATH_MAX]; ++ struct lacpdu lacpdu; ++ int err, nitems; ++ ++ strcpy(filename, lacp_port->ctx->lacp_directory); ++ if (filename[strlen(filename) - 1] != '/') ++ strcat(filename, "/"); /* Add trailing slash if we don't have one in the filename */ ++ strcat(filename, lacp_port->tdport->ifname); ++ fp = fopen(filename, "r"); ++ if (!fp) { ++ teamd_log_err("Can't open lacp-saved dump from file %s: %s", filename, strerror(errno)); ++ return errno; ++ } ++ ++ nitems = fread(&lacpdu, sizeof(struct lacpdu), 1, fp); ++ (void)fclose(fp); ++ ++ err = unlink(filename); ++ if(err < 0) { ++ teamd_log_err("Can't remove file %s: %s", filename, strerror(errno)); ++ } ++ ++ if (nitems != 1) { ++ teamd_log_err("Can't read lacp-saved dump from file %s: %s", filename, 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 +1345,13 @@ 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_start && ctx->lacp_directory) { ++ (void)lacpdu_read(lacp_port); ++ ctx->warm_start = false; ++ /* Once started, keep running in normal mode */ ++ } ++ + teamd_loop_callback_enable(ctx, LACP_SOCKET_CB_NAME, lacp_port); + return 0; + +@@ -1321,7 +1374,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->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 +1470,31 @@ 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); ++ if (filename[strlen(filename) - 1] != '/') ++ strcat(filename, "/"); /* Add trailing slash if we don't have one in the filename */ ++ 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 +1502,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 +2029,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 = { diff --git a/src/libteam/Makefile b/src/libteam/Makefile index a20f8dbd35..50ad002c50 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