From 5291e87454438ef0439f388f26a686dc260e0cb9 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Fri, 30 Jan 2026 11:23:34 +0100 Subject: [PATCH] MEDIUM: dynbuf: Add a pool for large buffers with a configurable size Add the support for large bufers. A dedicated memory pool is added. The size of these buffers must be explicitly configured by setting "tune.bufsize.large" directive. If it is not set, the pool is not created. In addition, if the size for large buffers is the same than for regular buffer, the feature is automatically disable. For now, large buffers remain unused. --- doc/configuration.txt | 9 ++++++++ include/haproxy/dynbuf.h | 8 +++++++- include/haproxy/global-t.h | 1 + src/cfgparse.c | 11 ++++++++++ src/dynbuf.c | 42 ++++++++++++++++++++++++++++++++++++++ src/haproxy.c | 1 + src/stream.c | 1 + 7 files changed, 72 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index caa1810cc..b66512693 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1869,6 +1869,7 @@ The following keywords are supported in the "global" section : - tune.buffers.limit - tune.buffers.reserve - tune.bufsize + - tune.bufsize.large - tune.bufsize.small - tune.comp.maxlevel - tune.defaults.purge @@ -4118,6 +4119,14 @@ tune.bufsize value set using this parameter will automatically be rounded up to the next multiple of 8 on 32-bit machines and 16 on 64-bit machines. +tune.bufsize.large + Sets the size in butes for large buffers. By defaults, support for large + buffers is not enabled, it must explicitly be enable by setting this value. + + These buffers are designed to be used in some specific contexts where more + data must be bufferized without changing the size of regular buffers. The + large buffers are not implicitly used. + tune.bufsize.small Sets the size in bytes for small buffers. The defaults value is 1024. diff --git a/include/haproxy/dynbuf.h b/include/haproxy/dynbuf.h index 4a6595d3c..ca16379c7 100644 --- a/include/haproxy/dynbuf.h +++ b/include/haproxy/dynbuf.h @@ -36,6 +36,7 @@ #include extern struct pool_head *pool_head_buffer; +extern struct pool_head *pool_head_large_buffer; int init_buffer(void); void buffer_dump(FILE *o, struct buffer *b, int from, int to); @@ -136,13 +137,18 @@ static inline char *__b_get_emergency_buf(void) #define __b_free(_buf) \ do { \ char *area = (_buf)->area; \ + size_t sz = (_buf)->size; \ \ /* let's first clear the area to save an occasional "show sess all" \ * glancing over our shoulder from getting a dangling pointer. \ */ \ *(_buf) = BUF_NULL; \ __ha_barrier_store(); \ - if (th_ctx->emergency_bufs_left < global.tune.reserved_bufs) \ + /* if enabled, large buffers are always strictly greater \ + * than the default buffers */ \ + if (unlikely(pool_head_large_buffer && sz == pool_head_large_buffer->size)) \ + pool_free(pool_head_large_buffer, area); \ + else if (th_ctx->emergency_bufs_left < global.tune.reserved_bufs) \ th_ctx->emergency_bufs[th_ctx->emergency_bufs_left++] = area; \ else \ pool_free(pool_head_buffer, area); \ diff --git a/include/haproxy/global-t.h b/include/haproxy/global-t.h index 6b1f3e7b6..6b3ba5cd5 100644 --- a/include/haproxy/global-t.h +++ b/include/haproxy/global-t.h @@ -179,6 +179,7 @@ struct global { uint recv_enough; /* how many input bytes at once are "enough" */ uint bufsize; /* buffer size in bytes, defaults to BUFSIZE */ uint bufsize_small;/* small buffer size in bytes */ + uint bufsize_large;/* large buffer size in bytes */ int maxrewrite; /* buffer max rewrite size in bytes, defaults to MAXREWRITE */ int reserved_bufs; /* how many buffers can only be allocated for response */ int buf_limit; /* if not null, how many total buffers may only be allocated */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 5dd10faf4..d2f4f0e98 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2311,6 +2311,17 @@ int check_config_validity() global.nbthread = global.thread_limit; } + if (global.tune.bufsize_large > 0) { + if (global.tune.bufsize_large == global.tune.bufsize) + global.tune.bufsize_large = 0; + else if (global.tune.bufsize_large < global.tune.bufsize) { + ha_warning("tune.bufsize.large (%u) is lower than tune.bufsize (%u). large buffers support is disabled. " + "Please fix either value to remove this warning.\n", + global.tune.bufsize_large, global.tune.bufsize); + global.tune.bufsize_large = 0; + } + } + /* in the worst case these were supposed to be set in thread_detect_count() */ BUG_ON(!global.nbthread); BUG_ON(!global.nbtgroups); diff --git a/src/dynbuf.c b/src/dynbuf.c index 12dac1a58..ff80b8198 100644 --- a/src/dynbuf.c +++ b/src/dynbuf.c @@ -23,6 +23,7 @@ #include struct pool_head *pool_head_buffer __read_mostly; +struct pool_head *pool_head_large_buffer __read_mostly = NULL; /* perform minimal initializations, report 0 in case of error, 1 if OK. */ int init_buffer() @@ -36,6 +37,12 @@ int init_buffer() if (!pool_head_buffer) return 0; + if (global.tune.bufsize_large) { + pool_head_large_buffer = create_aligned_pool("large_buffer", global.tune.bufsize_large, 64, MEM_F_SHARED|MEM_F_EXACT); + if (!pool_head_large_buffer) + return 0; + } + /* make sure any change to the queues assignment isn't overlooked */ BUG_ON(DB_PERMANENT - DB_UNLIKELY - 1 != DYNBUF_NBQ); BUG_ON(DB_MUX_RX_Q < DB_SE_RX_Q || DB_MUX_RX_Q >= DYNBUF_NBQ); @@ -188,6 +195,40 @@ static int cfg_parse_tune_buffers_reserve(char **args, int section_type, struct return 0; } +/* config parse for global "tune.bufsize.large" */ +static int cfg_parse_tune_bufsize_large(char **args, int section_type, + struct proxy *curpx, const struct proxy *defpx, + const char *file, int line, char **err) +{ + const char *res; + uint size; + + if (too_many_args(1, args, err, NULL)) + goto err; + + if (*(args[1]) == 0) { + memprintf(err, "'%s' expects an integer argument.\n", args[0]); + goto err; + } + + res = parse_size_err(args[1], &size); + if (res != NULL) { + memprintf(err, "unexpected '%s' after size passed to '%s'", res, args[0]); + goto err; + } + + if (size <= 0) { + memprintf(err, "'%s' expects a positive integer argument.\n", args[0]); + goto err; + } + + global.tune.bufsize_large = size; + return 0; + + err: + return -1; +} + /* config parse for global "tune.bufsize.small" */ static int cfg_parse_tune_bufsize_small(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, @@ -261,6 +302,7 @@ static void free_emergency_buffers_per_thread(void) static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "tune.buffers.limit", cfg_parse_tune_buffers_limit }, { CFG_GLOBAL, "tune.buffers.reserve", cfg_parse_tune_buffers_reserve }, + { CFG_GLOBAL, "tune.bufsize.large", cfg_parse_tune_bufsize_large }, { CFG_GLOBAL, "tune.bufsize.small", cfg_parse_tune_bufsize_small }, { 0, NULL, NULL } }}; diff --git a/src/haproxy.c b/src/haproxy.c index 4edd66da1..d1da0c3e3 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -179,6 +179,7 @@ struct global global = { .options = GTUNE_LISTENER_MQ_OPT, .bufsize = (BUFSIZE + 2*sizeof(void *) - 1) & -(2*sizeof(void *)), .bufsize_small = BUFSIZE_SMALL, + .bufsize_large = 0, .maxrewrite = MAXREWRITE, .reserved_bufs = RESERVED_BUFS, .pattern_cache = DEFAULT_PAT_LRU_SIZE, diff --git a/src/stream.c b/src/stream.c index 2d6b23da2..c537f5209 100644 --- a/src/stream.c +++ b/src/stream.c @@ -764,6 +764,7 @@ void stream_free(struct stream *s) /* We may want to free the maximum amount of pools if the proxy is stopping */ if (fe && unlikely(fe->flags & (PR_FL_DISABLED|PR_FL_STOPPED))) { pool_flush(pool_head_buffer); + pool_flush(pool_head_large_buffer); pool_flush(pool_head_http_txn); pool_flush(pool_head_requri); pool_flush(pool_head_capture);