-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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
- Loading branch information
1 parent
4b21ff1
commit 4157f1d
Showing
2 changed files
with
346 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = { |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters