This is an automated email from Gerrit. "Antonio Borneo <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6703
-- gerrit commit 5cf99c6678d4f5c0affc8dca1fc012af5ed442b4 Author: Antonio Borneo <[email protected]> Date: Tue Nov 16 01:44:07 2021 +0100 [WIP] server: accept connections during keep_alive GDB requires OpenOCD to reply to the first command within a delay of 6 seconds (3x remotetimeout). This is not always possible due to OpenOCD simple scheduler. Reuse the keep_alive() mechanism to sense an incoming connection, accept it and, in case of GDB, send keep-alive packets to prevent GDB to timeout. It doesn't work with LLDB, yet. Change-Id: I4b3418a46f147392a4f965db6c34c6ab1f50926b Signed-off-by: Antonio Borneo <[email protected]> diff --git a/src/helper/command.c b/src/helper/command.c index 53ee2508a..1e769d719 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -1145,6 +1145,7 @@ COMMAND_HANDLER(handle_sleep_command) int64_t then = timeval_ms(); while (timeval_ms() - then < (int64_t)duration) { target_call_timer_callbacks_now(); + keep_alive(); usleep(1000); } } else diff --git a/src/helper/log.c b/src/helper/log.c index caa0a66bf..9e0aecee1 100644 --- a/src/helper/log.c +++ b/src/helper/log.c @@ -429,6 +429,7 @@ static void gdb_timeout_warning(int64_t delta_time) delta_time); } +void handle_keep_alive(void); void keep_alive(void) { current_time = timeval_ms(); @@ -444,6 +445,7 @@ void keep_alive(void) if (delta_time > KEEP_ALIVE_KICK_TIME_MS) { last_time = current_time; + handle_keep_alive(); /* this will keep the GDB connection alive */ LOG_USER_N("%s", ""); diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index a16b4ccbe..e1fb158bb 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -960,15 +960,44 @@ static int gdb_target_callback_event_handler(struct target *target, return ERROR_OK; } -static int gdb_new_connection(struct connection *connection) +static void gdb_log_callback_dummy(void *priv, const char *file, unsigned line, + const char *function, const char *string) { - struct gdb_connection *gdb_connection = malloc(sizeof(struct gdb_connection)); + struct connection *connection = priv; + + /* just send something? not really: + * for gdb anything except '$' and '%' + * for lldb '+' is not ok */ + gdb_write(connection, "\n", 1); +} + +static int gdb_new_connection(struct connection *connection, enum add_connection_mode mode) +{ + struct gdb_connection *gdb_connection; + + if (mode == ADD_CONNECTION_TOP) { + gdb_connection = malloc(sizeof(struct gdb_connection)); + connection->priv = gdb_connection; + gdb_connection->closed = false; + log_add_callback(gdb_log_callback_dummy, connection); + return ERROR_OK; + } + + if (mode == ADD_CONNECTION_BOTTOM) + log_remove_callback(gdb_log_callback_dummy, connection); + + if (mode == ADD_CONNECTION_FULL) { + gdb_connection = malloc(sizeof(struct gdb_connection)); + connection->priv = gdb_connection; + gdb_connection->closed = false; + } + + gdb_connection = connection->priv; struct target *target; int retval; int initial_ack; target = get_target_from_connection(connection); - connection->priv = gdb_connection; connection->cmd_ctx->current_target = target; /* initialize gdb connection information */ @@ -977,7 +1006,6 @@ static int gdb_new_connection(struct connection *connection) gdb_connection->ctrl_c = false; gdb_connection->frontend_state = TARGET_HALTED; gdb_connection->vflash_image = NULL; - gdb_connection->closed = false; gdb_connection->busy = false; gdb_connection->noack_mode = 0; gdb_connection->sync = false; diff --git a/src/server/ipdbg.c b/src/server/ipdbg.c index ec2fae8c0..cd324af39 100644 --- a/src/server/ipdbg.c +++ b/src/server/ipdbg.c @@ -535,8 +535,11 @@ static int ipdbg_stop_polling(struct ipdbg_service *service) return ERROR_OK; } -static int ipdbg_on_new_connection(struct connection *connection) +static int ipdbg_on_new_connection(struct connection *connection, enum add_connection_mode mode) { + if (mode == ADD_CONNECTION_TOP) + return ERROR_OK; + struct ipdbg_service *service = connection->service->priv; connection->priv = &service->connection; /* initialize ipdbg connection information */ diff --git a/src/server/rtt_server.c b/src/server/rtt_server.c index d49e4d000..797eb9245 100644 --- a/src/server/rtt_server.c +++ b/src/server/rtt_server.c @@ -58,11 +58,14 @@ static int read_callback(unsigned int channel, const uint8_t *buffer, return ERROR_OK; } -static int rtt_new_connection(struct connection *connection) +static int rtt_new_connection(struct connection *connection, enum add_connection_mode mode) { int ret; struct rtt_service *service; + if (mode == ADD_CONNECTION_TOP) + return ERROR_OK; + service = connection->service->priv; LOG_DEBUG("rtt: New connection for channel %u", service->channel); diff --git a/src/server/server.c b/src/server/server.c index 3f579bfc6..48e0a35fe 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -64,7 +64,95 @@ static int polling_period = 100; /* address by name on which to listen for incoming TCP/IP connections */ static char *bindto_name; -static int add_connection(struct service *service, struct command_context *cmd_ctx) +static int add_connection(struct service *service, struct command_context *cmd_ctx, + int *new_fd, enum add_connection_mode mode); +static int remove_connection(struct service *service, struct connection *connection); + +/* list of fds to listen during keep_alive() */ +static fd_set keep_alive_listen_fds; +static fd_set keep_alive_accepted_fds; +static int keep_alive_listen_fd_max; +static bool keep_alive_accepted; +static struct command_context *keep_alive_cmd_ctx; + +static void prepare_keep_alive(struct command_context *cmd_ctx) +{ + /* prepare list of fds for keep_alive() */ + keep_alive_listen_fd_max = -1; + FD_ZERO(&keep_alive_listen_fds); + FD_ZERO(&keep_alive_accepted_fds); + keep_alive_accepted = false; + keep_alive_cmd_ctx = cmd_ctx; + + for (struct service *service = services; service; service = service->next) { + if (service->fd != -1) { + FD_SET(service->fd, &keep_alive_listen_fds); + if (keep_alive_listen_fd_max < service->fd) + keep_alive_listen_fd_max = service->fd; + } + } +} + +void handle_keep_alive(void) +{ + if (keep_alive_listen_fd_max == -1) + return; + + struct timeval tv = {0, 0}; + fd_set tmp_fds = keep_alive_listen_fds; + int retval = socket_select(keep_alive_listen_fd_max + 1, &tmp_fds, NULL, NULL, &tv); + if (retval <= 0) + return; + + for (struct service *service = services; service; service = service->next) { + /* handle new connections on listeners */ + if (service->fd != -1 && FD_ISSET(service->fd, &tmp_fds)) { + if (service->max_connections != 0) { + int fd = service->fd; + int new_fd; + retval = add_connection(service, keep_alive_cmd_ctx, &new_fd, ADD_CONNECTION_TOP); + if (retval == ERROR_OK) { + FD_SET(new_fd, &keep_alive_accepted_fds); + keep_alive_accepted = true; + } + if (service->fd == -1) + FD_CLR(fd, &keep_alive_listen_fds); + } else { + if (service->type == CONNECTION_TCP) { + struct sockaddr_in sin; + socklen_t address_size = sizeof(sin); + int tmp_fd; + tmp_fd = accept(service->fd, + (struct sockaddr *)&service->sin, + &address_size); + close_socket(tmp_fd); + } + LOG_INFO("rejected '%s' connection, no more connections allowed", service->name); + } + } + } +} + +static void finalize_keep_alive(void) +{ + if (keep_alive_accepted == false) + return; + + for (struct service *service = services; service; service = service->next) { + struct connection *next; + for (struct connection *c = service->connections; c; c = next) { + next = c->next; + if (c->fd == -1 || !FD_ISSET(c->fd, &keep_alive_accepted_fds)) + continue; + int retval = service->new_connection(c, ADD_CONNECTION_BOTTOM); + if (retval != ERROR_OK) + remove_connection(service, c); + } + } +} + +static int add_connection(struct service *service, struct command_context *cmd_ctx, + int *new_fd, enum add_connection_mode mode) { socklen_t address_size; struct connection *c, **p; @@ -85,6 +173,8 @@ static int add_connection(struct service *service, struct command_context *cmd_c address_size = sizeof(c->sin); c->fd = accept(service->fd, (struct sockaddr *)&service->sin, &address_size); + if (new_fd) + *new_fd = c->fd; c->fd_out = c->fd; /* This increases performance dramatically for e.g. GDB load which @@ -99,7 +189,7 @@ static int add_connection(struct service *service, struct command_context *cmd_c sizeof(int)); /* length of option value */ LOG_INFO("accepting '%s' connection on tcp/%s", service->name, service->port); - retval = service->new_connection(c); + retval = service->new_connection(c, mode); if (retval != ERROR_OK) { close_socket(c->fd); LOG_ERROR("attempted '%s' connection rejected", service->name); @@ -109,6 +199,8 @@ static int add_connection(struct service *service, struct command_context *cmd_c } } else if (service->type == CONNECTION_STDINOUT) { c->fd = service->fd; + if (new_fd) + *new_fd = c->fd; c->fd_out = fileno(stdout); #ifdef _WIN32 @@ -120,7 +212,7 @@ static int add_connection(struct service *service, struct command_context *cmd_c service->fd = -1; LOG_INFO("accepting '%s' connection from pipe", service->name); - retval = service->new_connection(c); + retval = service->new_connection(c, mode); if (retval != ERROR_OK) { LOG_ERROR("attempted '%s' connection rejected", service->name); command_done(c->cmd_ctx); @@ -129,6 +221,8 @@ static int add_connection(struct service *service, struct command_context *cmd_c } } else if (service->type == CONNECTION_PIPE) { c->fd = service->fd; + if (new_fd) + *new_fd = c->fd; /* do not check for new connections again on stdin */ service->fd = -1; @@ -143,7 +237,7 @@ static int add_connection(struct service *service, struct command_context *cmd_c } LOG_INFO("accepting '%s' connection from pipe %s", service->name, service->port); - retval = service->new_connection(c); + retval = service->new_connection(c, mode); if (retval != ERROR_OK) { LOG_ERROR("attempted '%s' connection rejected", service->name); command_done(c->cmd_ctx); @@ -544,7 +638,7 @@ int server_loop(struct command_context *command_context) if ((service->fd != -1) && (FD_ISSET(service->fd, &read_fds))) { if (service->max_connections != 0) - add_connection(service, command_context); + add_connection(service, command_context, NULL, ADD_CONNECTION_FULL); else { if (service->type == CONNECTION_TCP) { struct sockaddr_in sin; @@ -561,6 +655,8 @@ int server_loop(struct command_context *command_context) } } + prepare_keep_alive(command_context); + /* handle activity on connections */ if (service->connections) { struct connection *c; @@ -586,6 +682,8 @@ int server_loop(struct command_context *command_context) c = c->next; } } + + finalize_keep_alive(); } #ifdef _WIN32 diff --git a/src/server/server.h b/src/server/server.h index de18d2b4b..cd3102ac1 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -42,6 +42,12 @@ enum connection_type { CONNECTION_STDINOUT }; +enum add_connection_mode { + ADD_CONNECTION_FULL, + ADD_CONNECTION_TOP, + ADD_CONNECTION_BOTTOM, +}; + #define CONNECTION_LIMIT_UNLIMITED (-1) struct connection { @@ -55,7 +61,7 @@ struct connection { struct connection *next; }; -typedef int (*new_connection_handler_t)(struct connection *connection); +typedef int (*new_connection_handler_t)(struct connection *connection, enum add_connection_mode mode); typedef int (*input_handler_t)(struct connection *connection); typedef int (*connection_closed_handler_t)(struct connection *connection); diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c index e08823224..c1822e14d 100644 --- a/src/server/tcl_server.c +++ b/src/server/tcl_server.c @@ -42,7 +42,7 @@ struct tcl_connection { static char *tcl_port; /* handlers */ -static int tcl_new_connection(struct connection *connection); +static int tcl_new_connection(struct connection *connection, enum add_connection_mode mode); static int tcl_input(struct connection *connection); static int tcl_output(struct connection *connection, const void *buf, ssize_t len); static int tcl_closed(struct connection *connection); @@ -140,10 +140,13 @@ int tcl_output(struct connection *connection, const void *data, ssize_t len) } /* connections */ -static int tcl_new_connection(struct connection *connection) +static int tcl_new_connection(struct connection *connection, enum add_connection_mode mode) { struct tcl_connection *tclc; + if (mode == ADD_CONNECTION_TOP) + return ERROR_OK; + tclc = calloc(1, sizeof(struct tcl_connection)); if (!tclc) return ERROR_CONNECTION_REJECTED; diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index 2ebcff163..7ccf6605c 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -218,8 +218,11 @@ static void telnet_save_history(struct telnet_connection *t_con) free(history); } -static int telnet_new_connection(struct connection *connection) +static int telnet_new_connection(struct connection *connection, enum add_connection_mode mode) { + if (mode == ADD_CONNECTION_TOP) + return ERROR_OK; + struct telnet_connection *telnet_connection; struct telnet_service *telnet_service = connection->service->priv; int i; diff --git a/src/target/arm_tpiu_swo.c b/src/target/arm_tpiu_swo.c index 024521364..ef02d4a9e 100644 --- a/src/target/arm_tpiu_swo.c +++ b/src/target/arm_tpiu_swo.c @@ -241,8 +241,11 @@ int arm_tpiu_swo_cleanup_all(void) return ERROR_OK; } -static int arm_tpiu_swo_service_new_connection(struct connection *connection) +static int arm_tpiu_swo_service_new_connection(struct connection *connection, enum add_connection_mode mode) { + if (mode == ADD_CONNECTION_TOP) + return ERROR_OK; + struct arm_tpiu_swo_priv_connection *priv = connection->service->priv; struct arm_tpiu_swo_object *obj = priv->obj; struct arm_tpiu_swo_connection *c = malloc(sizeof(*c)); diff --git a/src/target/openrisc/jsp_server.c b/src/target/openrisc/jsp_server.c index e0a4475cf..f5ede28fe 100644 --- a/src/target/openrisc/jsp_server.c +++ b/src/target/openrisc/jsp_server.c @@ -77,8 +77,11 @@ static int jsp_poll_read(void *priv) return ERROR_OK; } -static int jsp_new_connection(struct connection *connection) +static int jsp_new_connection(struct connection *connection, enum add_connection_mode mode) { + if (mode == ADD_CONNECTION_TOP) + return ERROR_OK; + struct telnet_connection *telnet_connection = malloc(sizeof(struct telnet_connection)); struct jsp_service *jsp_service = connection->service->priv; --
