mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-02 02:02:03 +00:00
MINOR: quic: implement a basic "show quic" CLI handler
Implement a basic "show quic" CLI handler. This command will be useful to display various information on all the active QUIC frontend connections. This work is heavily inspired by "show sess". Most notably, a global list of quic_conn has been introduced to be able to loop over them. This list is stored per thread in ha_thread_ctx. Also add three CLI handlers for "show quic" in order to allocate and free the command context. The dump handler runs on thread isolation. Each quic_conn is referenced using a back-ref to handle deletion during handler yielding. For the moment, only a list of raw quic_conn pointers is displayed. The handler will be completed over time with more information as needed. This should be backported up to 2.7.
This commit is contained in:
parent
db991c2658
commit
15c74702d5
@ -2966,6 +2966,11 @@ show resolvers [<resolvers section id>]
|
||||
too_big: too big response
|
||||
outdated: number of response arrived too late (after an other name server)
|
||||
|
||||
show quic
|
||||
Dump information on all active QUIC frontend connections. This command is
|
||||
restricted and can only be issued on sockets configured for levels "operator"
|
||||
or "admin".
|
||||
|
||||
show servers conn [<backend>]
|
||||
Dump the current and idle connections state of the servers belonging to the
|
||||
designated backend (or all backends if none specified). A backend name or
|
||||
|
@ -733,6 +733,10 @@ struct quic_conn {
|
||||
|
||||
const struct qcc_app_ops *app_ops;
|
||||
struct quic_counters *prx_counters;
|
||||
|
||||
struct list el_th_ctx; /* list elem in ha_thread_ctx */
|
||||
struct list back_refs; /* list head of CLI context currently dumping this connection. */
|
||||
unsigned int qc_epoch; /* delimiter for newer instances started after "show quic". */
|
||||
};
|
||||
|
||||
#endif /* USE_QUIC */
|
||||
|
@ -130,6 +130,7 @@ struct thread_ctx {
|
||||
struct list pool_lru_head; /* oldest objects in thread-local pool caches */
|
||||
struct list buffer_wq; /* buffer waiters */
|
||||
struct list streams; /* list of streams attached to this thread */
|
||||
struct list quic_conns; /* list of quic-conns attached to this thread */
|
||||
|
||||
ALWAYS_ALIGN(2*sizeof(void*));
|
||||
struct list tasklets[TL_CLASSES]; /* tasklets (and/or tasks) to run, by class */
|
||||
|
154
src/quic_conn.c
154
src/quic_conn.c
@ -34,6 +34,8 @@
|
||||
#include <haproxy/tools.h>
|
||||
#include <haproxy/ticks.h>
|
||||
|
||||
#include <haproxy/applet-t.h>
|
||||
#include <haproxy/cli.h>
|
||||
#include <haproxy/connection.h>
|
||||
#include <haproxy/fd.h>
|
||||
#include <haproxy/freq_ctr.h>
|
||||
@ -58,8 +60,12 @@
|
||||
#include <haproxy/quic_tls.h>
|
||||
#include <haproxy/ssl_sock.h>
|
||||
#include <haproxy/task.h>
|
||||
#include <haproxy/thread.h>
|
||||
#include <haproxy/trace.h>
|
||||
|
||||
/* incremented by each "show quic". */
|
||||
static unsigned int qc_epoch = 0;
|
||||
|
||||
/* list of supported QUIC versions by this implementation */
|
||||
const struct quic_version quic_versions[] = {
|
||||
{
|
||||
@ -4886,6 +4892,8 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
|
||||
qc_init_fd(qc);
|
||||
|
||||
LIST_INIT(&qc->back_refs);
|
||||
|
||||
/* Now proceeds to allocation of qc members. */
|
||||
|
||||
buf_area = pool_alloc(pool_head_quic_conn_rxbuf);
|
||||
@ -5024,6 +5032,9 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
if (!qc_new_isecs(qc, ictx,qc->original_version, dcid->data, dcid->len, 1))
|
||||
goto err;
|
||||
|
||||
LIST_APPEND(&th_ctx->quic_conns, &qc->el_th_ctx);
|
||||
qc->qc_epoch = HA_ATOMIC_LOAD(&qc_epoch);
|
||||
|
||||
TRACE_LEAVE(QUIC_EV_CONN_INIT, qc);
|
||||
|
||||
return qc;
|
||||
@ -5051,6 +5062,7 @@ void quic_conn_release(struct quic_conn *qc)
|
||||
struct eb64_node *node;
|
||||
struct quic_tls_ctx *app_tls_ctx;
|
||||
struct quic_rx_packet *pkt, *pktback;
|
||||
struct bref *bref, *back;
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
|
||||
|
||||
@ -5126,6 +5138,26 @@ void quic_conn_release(struct quic_conn *qc)
|
||||
quic_free_arngs(qc, &qc->pktns[i].rx.arngs);
|
||||
}
|
||||
|
||||
/* Detach CLI context watchers currently dumping this connection.
|
||||
* Reattach them to the next quic_conn instance.
|
||||
*/
|
||||
list_for_each_entry_safe(bref, back, &qc->back_refs, users) {
|
||||
/* Remove watcher from this quic_conn instance. */
|
||||
LIST_DEL_INIT(&bref->users);
|
||||
|
||||
/* Attach it to next instance unless it was the last list element. */
|
||||
if (qc->el_th_ctx.n != &th_ctx->quic_conns) {
|
||||
struct quic_conn *next = LIST_NEXT(&qc->el_th_ctx,
|
||||
struct quic_conn *,
|
||||
el_th_ctx);
|
||||
LIST_APPEND(&next->back_refs, &bref->users);
|
||||
}
|
||||
bref->ref = qc->el_th_ctx.n;
|
||||
__ha_barrier_store();
|
||||
}
|
||||
/* Remove quic_conn from global ha_thread_ctx list. */
|
||||
LIST_DELETE(&qc->el_th_ctx);
|
||||
|
||||
pool_free(pool_head_quic_conn_rxbuf, qc->rx.buf.area);
|
||||
pool_free(pool_head_quic_conn, qc);
|
||||
TRACE_PROTO("QUIC conn. freed", QUIC_EV_CONN_FREED, qc);
|
||||
@ -7586,6 +7618,128 @@ void qc_notify_close(struct quic_conn *qc)
|
||||
TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc);
|
||||
}
|
||||
|
||||
|
||||
/* appctx context used by "show quic" command */
|
||||
struct show_quic_ctx {
|
||||
unsigned int epoch;
|
||||
struct bref bref; /* back-reference to the quic-conn being dumped */
|
||||
unsigned int thr;
|
||||
};
|
||||
|
||||
static int cli_parse_show_quic(char **args, char *payload, struct appctx *appctx, void *private)
|
||||
{
|
||||
struct show_quic_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_OPER))
|
||||
return 1;
|
||||
|
||||
ctx->epoch = _HA_ATOMIC_FETCH_ADD(&qc_epoch, 1);
|
||||
ctx->thr = 0;
|
||||
|
||||
LIST_INIT(&ctx->bref.users);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cli_io_handler_dump_quic(struct appctx *appctx)
|
||||
{
|
||||
struct show_quic_ctx *ctx = appctx->svcctx;
|
||||
struct stconn *sc = appctx_sc(appctx);
|
||||
struct quic_conn *qc;
|
||||
|
||||
thread_isolate();
|
||||
|
||||
if (ctx->thr >= global.nbthread)
|
||||
goto done;
|
||||
|
||||
if (unlikely(sc_ic(sc)->flags & CF_SHUTW)) {
|
||||
/* If we're forced to shut down, we might have to remove our
|
||||
* reference to the last stream being dumped.
|
||||
*/
|
||||
if (!LIST_ISEMPTY(&ctx->bref.users))
|
||||
LIST_DEL_INIT(&ctx->bref.users);
|
||||
goto done;
|
||||
}
|
||||
|
||||
chunk_reset(&trash);
|
||||
|
||||
if (!LIST_ISEMPTY(&ctx->bref.users)) {
|
||||
/* Remove show_quic_ctx from previous quic_conn instance. */
|
||||
LIST_DEL_INIT(&ctx->bref.users);
|
||||
}
|
||||
else if (!ctx->bref.ref) {
|
||||
/* First invocation. */
|
||||
ctx->bref.ref = ha_thread_ctx[ctx->thr].quic_conns.n;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int done = 0;
|
||||
|
||||
if (ctx->bref.ref == &ha_thread_ctx[ctx->thr].quic_conns) {
|
||||
done = 1;
|
||||
}
|
||||
else {
|
||||
qc = LIST_ELEM(ctx->bref.ref, struct quic_conn *, el_th_ctx);
|
||||
if ((int)(qc->qc_epoch - ctx->epoch) > 0)
|
||||
done = 1;
|
||||
}
|
||||
|
||||
if (done) {
|
||||
++ctx->thr;
|
||||
if (ctx->thr >= global.nbthread)
|
||||
break;
|
||||
ctx->bref.ref = ha_thread_ctx[ctx->thr].quic_conns.n;
|
||||
continue;
|
||||
}
|
||||
|
||||
chunk_appendf(&trash, "%p", qc);
|
||||
chunk_appendf(&trash, "\n");
|
||||
|
||||
if (applet_putchk(appctx, &trash) == -1) {
|
||||
/* Register show_quic_ctx to quic_conn instance. */
|
||||
LIST_APPEND(&qc->back_refs, &ctx->bref.users);
|
||||
goto full;
|
||||
}
|
||||
|
||||
ctx->bref.ref = qc->el_th_ctx.n;
|
||||
}
|
||||
|
||||
done:
|
||||
thread_release();
|
||||
return 1;
|
||||
|
||||
full:
|
||||
thread_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cli_release_show_quic(struct appctx *appctx)
|
||||
{
|
||||
struct show_quic_ctx *ctx = appctx->svcctx;
|
||||
|
||||
if (ctx->thr < global.nbthread) {
|
||||
thread_isolate();
|
||||
if (!LIST_ISEMPTY(&ctx->bref.users))
|
||||
LIST_DEL_INIT(&ctx->bref.users);
|
||||
thread_release();
|
||||
}
|
||||
}
|
||||
|
||||
static struct cli_kw_list cli_kws = {{ }, {
|
||||
{ { "show", "quic", NULL }, "show quic : display quic connections status", cli_parse_show_quic, cli_io_handler_dump_quic, cli_release_show_quic },
|
||||
}};
|
||||
|
||||
INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
|
||||
|
||||
static void init_quic()
|
||||
{
|
||||
int thr;
|
||||
|
||||
for (thr = 0; thr < MAX_THREADS; ++thr)
|
||||
LIST_INIT(&ha_thread_ctx[thr].quic_conns);
|
||||
}
|
||||
INITCALL0(STG_INIT, init_quic);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
|
Loading…
Reference in New Issue
Block a user