diff --git a/include/haproxy/check-t.h b/include/haproxy/check-t.h index df75d4aca..ecb9a6862 100644 --- a/include/haproxy/check-t.h +++ b/include/haproxy/check-t.h @@ -59,6 +59,7 @@ enum chk_result { #define CHK_ST_FASTINTER 0x0400 /* force fastinter check */ #define CHK_ST_READY 0x0800 /* check ready to migrate or run, see below */ #define CHK_ST_SLEEPING 0x1000 /* check was sleeping, i.e. not currently bound to a thread, see below */ +#define CHK_ST_USE_SMALL_BUFF 0x2000 /* Use small buffers if possible for the request */ /* 4 possible states for CHK_ST_SLEEPING and CHK_ST_READY: * SLP RDY State Description diff --git a/include/haproxy/check.h b/include/haproxy/check.h index 5e34d6519..09e195a46 100644 --- a/include/haproxy/check.h +++ b/include/haproxy/check.h @@ -78,7 +78,7 @@ struct task *process_chk(struct task *t, void *context, unsigned int state); struct task *srv_chk_io_cb(struct task *t, void *ctx, unsigned int state); int check_buf_available(void *target); -struct buffer *check_get_buf(struct check *check, struct buffer *bptr); +struct buffer *check_get_buf(struct check *check, struct buffer *bptr, unsigned int small_buffer); void check_release_buf(struct check *check, struct buffer *bptr); const char *init_check(struct check *check, int type); void free_check(struct check *check); diff --git a/include/haproxy/tcpcheck-t.h b/include/haproxy/tcpcheck-t.h index 290a73584..3f48527ed 100644 --- a/include/haproxy/tcpcheck-t.h +++ b/include/haproxy/tcpcheck-t.h @@ -121,6 +121,7 @@ enum tcpcheck_rule_type { /* Unused 0x000000A0..0x00000FF0 (reserved for future proto) */ #define TCPCHK_RULES_TCP_CHK 0x00000FF0 #define TCPCHK_RULES_PROTO_CHK 0x00000FF0 /* Mask to cover protocol check */ +#define TCPCHK_RULES_MAY_USE_SBUF 0x00001000 /* checks may try to use small buffers if possible for the request */ struct check; struct tcpcheck_connect { diff --git a/src/check.c b/src/check.c index 57dc74b7f..f89769e6d 100644 --- a/src/check.c +++ b/src/check.c @@ -1515,13 +1515,15 @@ int check_buf_available(void *target) /* * Allocate a buffer. If it fails, it adds the check in buffer wait queue. */ -struct buffer *check_get_buf(struct check *check, struct buffer *bptr) +struct buffer *check_get_buf(struct check *check, struct buffer *bptr, unsigned int small_buffer) { struct buffer *buf = NULL; - if (likely(!LIST_INLIST(&check->buf_wait.list)) && - unlikely((buf = b_alloc(bptr, DB_CHANNEL)) == NULL)) { - b_queue(DB_CHANNEL, &check->buf_wait, check, check_buf_available); + if (small_buffer == 0 || (buf = b_alloc_small(bptr)) == NULL) { + if (likely(!LIST_INLIST(&check->buf_wait.list)) && + unlikely((buf = b_alloc(bptr, DB_CHANNEL)) == NULL)) { + b_queue(DB_CHANNEL, &check->buf_wait, check, check_buf_available); + } } return buf; } @@ -1533,8 +1535,11 @@ struct buffer *check_get_buf(struct check *check, struct buffer *bptr) void check_release_buf(struct check *check, struct buffer *bptr) { if (bptr->size) { + int defbuf = b_is_default(bptr); + b_free(bptr); - offer_buffers(check->buf_wait.target, 1); + if (defbuf) + offer_buffers(check->buf_wait.target, 1); } } @@ -1654,7 +1659,6 @@ int start_check_task(struct check *check, int mininter, */ static int start_checks() { - struct proxy *px; struct server *s; char *errmsg = NULL; @@ -1681,6 +1685,9 @@ static int start_checks() */ for (px = proxies_list; px; px = px->next) { for (s = px->srv; s; s = s->next) { + if (s->check.tcpcheck_rules->flags & TCPCHK_RULES_MAY_USE_SBUF) + s->check.state |= CHK_ST_USE_SMALL_BUFF; + if (s->check.state & CHK_ST_CONFIGURED) { nbcheck++; if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) && diff --git a/src/tcpcheck.c b/src/tcpcheck.c index fe8d4f766..3519a83e1 100644 --- a/src/tcpcheck.c +++ b/src/tcpcheck.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -1659,7 +1660,8 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r goto out; } - if (!check_get_buf(check, &check->bo)) { + retry: + if (!check_get_buf(check, &check->bo, (check->state & CHK_ST_USE_SMALL_BUFF))) { check->state |= CHK_ST_OUT_ALLOC; ret = TCPCHK_EVAL_WAIT; TRACE_STATE("waiting for output buffer allocation", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA|CHK_EV_TX_BLK, check); @@ -1679,6 +1681,13 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r case TCPCHK_SEND_STRING: case TCPCHK_SEND_BINARY: if (istlen(send->data) >= b_size(&check->bo)) { + if (b_is_small(&check->bo)) { + check->state &= ~CHK_ST_USE_SMALL_BUFF; + check_release_buf(check, &check->bo); + TRACE_DEVEL("Send fail with small buffer retry with default one", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA, check); + goto retry; + } + chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d", (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo), tcpcheck_get_step_id(check, rule)); @@ -1689,6 +1698,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r b_putist(&check->bo, send->data); break; case TCPCHK_SEND_STRING_LF: + BUG_ON(check->state & CHK_ST_USE_SMALL_BUFF); check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt); if (!b_data(&check->bo)) goto out; @@ -1696,7 +1706,8 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r case TCPCHK_SEND_BINARY_LF: { int len = b_size(&check->bo); - tmp = alloc_trash_chunk(); + BUG_ON(check->state & CHK_ST_USE_SMALL_BUFF); + tmp = alloc_trash_chunk_sz(len); if (!tmp) goto error_lf; tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt); @@ -1713,7 +1724,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r struct ist meth, uri, vsn, clen, body; unsigned int slflags = 0; - tmp = alloc_trash_chunk(); + tmp = alloc_trash_chunk_sz(b_size(&check->bo)); if (!tmp) goto error_htx; @@ -1838,6 +1849,12 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r htx_reset(htx); htx_to_buf(htx, &check->bo); } + if (b_is_small(&check->bo)) { + check->state &= ~CHK_ST_USE_SMALL_BUFF; + check_release_buf(check, &check->bo); + TRACE_DEVEL("Send fail with small buffer retry with default one", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA, check); + goto retry; + } chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d", tcpcheck_get_step_id(check, rule)); TRACE_ERROR("failed to build HTTP request", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA|CHK_EV_TCPCHK_ERR, check); @@ -1884,7 +1901,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_r goto wait_more_data; } - if (!check_get_buf(check, &check->bi)) { + if (!check_get_buf(check, &check->bi, 0)) { check->state |= CHK_ST_IN_ALLOC; TRACE_STATE("waiting for input buffer allocation", CHK_EV_RX_DATA|CHK_EV_RX_BLK, check); goto wait_more_data; @@ -4067,6 +4084,8 @@ static int check_proxy_tcpcheck(struct proxy *px) } } + /* Allow small buffer use by default. All send rules must be compatible */ + px->tcpcheck_rules.flags |= (global.tune.bufsize_small ? TCPCHK_RULES_MAY_USE_SBUF : 0); /* Remove all comment rules. To do so, when a such rule is found, the * comment is assigned to the following rule(s). @@ -4096,6 +4115,25 @@ static int check_proxy_tcpcheck(struct proxy *px) ha_free(&comment); break; case TCPCHK_ACT_SEND: + /* Disable small buffer use for rules using LF stirngs or too large data */ + switch (chk->send.type) { + case TCPCHK_SEND_STRING: + case TCPCHK_SEND_BINARY: + if (istlen(chk->send.data) >= global.tune.bufsize_small) + px->tcpcheck_rules.flags &= ~TCPCHK_RULES_MAY_USE_SBUF; + break; + case TCPCHK_SEND_STRING_LF: + case TCPCHK_SEND_BINARY_LF: + px->tcpcheck_rules.flags &= ~TCPCHK_RULES_MAY_USE_SBUF; + break; + case TCPCHK_SEND_HTTP: + if ((chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) || + (istlen(chk->send.http.body) >= global.tune.bufsize_small)) + px->tcpcheck_rules.flags &= ~TCPCHK_RULES_MAY_USE_SBUF; + default: + break; + } + __fallthrough; case TCPCHK_ACT_EXPECT: if (!chk->comment && comment) chk->comment = strdup(comment);