[teamd] Add Warm-reboot startup and shutdown mode for teamd (#2173)
* Add Warm-reboot startup and shutdown mode for teamd * Address comments, fix some bugs * Use tab instead of 8 spaces
This commit is contained in:
parent
4b21ff167f
commit
4157f1d230
345
src/libteam/0005-libteam-Add-warm_reboot-mode.patch
Normal file
345
src/libteam/0005-libteam-Add-warm_reboot-mode.patch
Normal file
@ -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 = {
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user