]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: dynbuf: Add a pool for large buffers with a configurable size
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 30 Jan 2026 10:23:34 +0000 (11:23 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 18 Feb 2026 12:26:21 +0000 (13:26 +0100)
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
include/haproxy/dynbuf.h
include/haproxy/global-t.h
src/cfgparse.c
src/dynbuf.c
src/haproxy.c
src/stream.c

index 037f701e82864c94bc8e5b9d58c5c7869aa2a40b..0580adcb026bf924f45e8e215ed16b65b9feed7f 100644 (file)
@@ -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 <size>
   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 <size>
+  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 <size>
   Sets the size in bytes for small buffers. The defaults value is 1024.
 
index 4a6595d3cc2201914d4d5639dfe30a715986d992..ca16379c7e788ac53a403d8de199cac5dc2f511a 100644 (file)
@@ -36,6 +36,7 @@
 #include <haproxy/pool.h>
 
 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);              \
index 6b1f3e7b6bcb2c9807114278f4efc0dff3c23c29..6b3ba5cd504f0f090a49d814b4c30be0bb94e793 100644 (file)
@@ -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 */
index 5dd10faf4639d1e2b90f4e002300e81f9403cc87..d2f4f0e98ce5d938c4148819bfed9d9e05f42c50 100644 (file)
@@ -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);
index 12dac1a58d021d4e346c13bfbb1623756dc1331e..ff80b8198ad5f7004ad60e8b9e791e1b5648cdbc 100644 (file)
@@ -23,6 +23,7 @@
 #include <haproxy/tools.h>
 
 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 }
 }};
index 4edd66da149eb2ee411dc0ce8a124e7564d356d7..d1da0c3e30917e2900c3ea20b855d4a698b2cbb6 100644 (file)
@@ -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,
index 2d6b23da26cd2b290c1fd777453943d1dd05a8c0..c537f5209b11f94e998004baa9da3d178bef7d6d 100644 (file)
@@ -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);