MINOR: proto_reverse_connect: refactor preconnect failure

When a connection is freed during preconnect before reversal, the error
must be notified to the listener to remove any connection reference and
rearm a new preconnect attempt. Currently, this can occur through 2 code
paths :
* conn_free() called directly by H2 mux
* error during conn_create_mux(). For this case, connection is flagged
  with CO_FL_ERROR and reverse_connect task is woken up. The process
  task handler is then responsible to call conn_free() for such
  connection.

Duplicated steps where done both in conn_free() and process task
handler. These are now removed. To facilitate code maintenance,
dedicated operation have been centralized in a new function
rev_notify_preconn_err() which is called by conn_free().
This commit is contained in:
Amaury Denoyelle 2023-09-22 16:28:35 +02:00
parent a37abee266
commit 1f43fb71be
3 changed files with 26 additions and 11 deletions

View File

@ -16,4 +16,6 @@ int rev_set_affinity(struct connection *conn, int new_tid);
int rev_accepting_conn(const struct receiver *rx);
void rev_notify_preconn_err(struct listener *l);
#endif /* _HAPROXY_PROTO_REVERSE_CONNECT_H */

View File

@ -25,6 +25,7 @@
#include <haproxy/log-t.h>
#include <haproxy/namespace.h>
#include <haproxy/net_helper.h>
#include <haproxy/proto_reverse_connect.h>
#include <haproxy/proto_tcp.h>
#include <haproxy/sample.h>
#include <haproxy/sc_strm.h>
@ -579,14 +580,7 @@ void conn_free(struct connection *conn)
if (conn_reverse_in_preconnect(conn)) {
struct listener *l = conn_active_reverse_listener(conn);
/* For the moment reverse connection are bound only on first thread. */
BUG_ON(tid != 0);
/* Receiver must reference a reverse connection as pending. */
BUG_ON(!l->rx.reverse_connect.pend_conn);
l->rx.reverse_connect.pend_conn = NULL;
l->rx.reverse_connect.task->expire = MS_TO_TICKS(now_ms + 1000);
task_queue(l->rx.reverse_connect.task);
rev_notify_preconn_err(l);
}
conn_force_unsubscribe(conn);

View File

@ -102,6 +102,26 @@ static struct connection *new_reverse_conn(struct listener *l, struct server *sr
return NULL;
}
/* Report that a connection used for preconnect on listener <l> is freed before
* reversal is completed. This is used to cleanup any reference to the
* connection and rearm a new preconnect attempt.
*/
void rev_notify_preconn_err(struct listener *l)
{
/* For the moment reverse connection are bound only on first thread. */
BUG_ON(tid != 0);
/* Receiver must reference a reverse connection as pending. */
BUG_ON(!l->rx.reverse_connect.pend_conn);
/* Remove reference to the freed connection. */
l->rx.reverse_connect.pend_conn = NULL;
/* Rearm a new preconnect attempt. */
l->rx.reverse_connect.task->expire = MS_TO_TICKS(now_ms + 1000);
task_queue(l->rx.reverse_connect.task);
}
struct task *rev_process(struct task *task, void *ctx, unsigned int state)
{
struct listener *l = ctx;
@ -111,10 +131,9 @@ struct task *rev_process(struct task *task, void *ctx, unsigned int state)
if (conn->flags & CO_FL_ERROR) {
conn_full_close(conn);
conn_free(conn);
l->rx.reverse_connect.pend_conn = NULL;
/* Retry on 1s on error. */
l->rx.reverse_connect.task->expire = MS_TO_TICKS(now_ms + 1000);
/* conn_free() must report preconnect failure using rev_notify_preconn_err(). */
BUG_ON(l->rx.reverse_connect.pend_conn);
}
else {
/* Spurrious receiver task wake up when pend_conn is not ready/on error. */