]> git.ipfire.org Git - thirdparty/haproxy.git/log
thirdparty/haproxy.git
15 months agoMAJOR: log: implement proper postparsing for logformat expressions
Aurelien DARRAGON [Fri, 23 Feb 2024 16:26:32 +0000 (17:26 +0100)] 
MAJOR: log: implement proper postparsing for logformat expressions

This patch tries to address a design flaw with how logformat expressions
are parsed from config. Indeed, some parse_logformat_string() calls are
performed during config parsing when the proxy mode is not yet known.

Here's a config example that illustrates the issue:

  defaults
     mode tcp

  listen test
     bind :8888
     http-response set-header custom-hdr "%trl" # needs http
     mode http

The above config should work, because the effective proxy mode is http,
yet haproxy fails with this error:

  [ALERT]    (99051) : config : parsing [repro.conf:6] : error detected in proxy 'test' while parsing 'http-response set-header' rule : format tag 'trl' is reserved for HTTP mode.

To fix the issue once and for all, let's implement smart postparsing for
logformat expressions encountered during config parsing:

  - split parse_logformat_string() (and subfonctions) in order to create a
    new lf_expr_postcheck() function that must be called to finish
    preparing and checking the logformat expression once the proxy type is
    known.
  - save some config hints info during parse_logformat_string() to
    generate more precise error messages during lf_expr_postcheck(), if
    needed, we rely on curpx->conf.args.{file,line} hints for that because
    parse_logformat_string() doesn't know about current file and line
    number.
  - lf_expr_postcheck() uses PR_FL_CHECKED proxy flag to know if the
    function may try to make the proxy compatible with the expression, or
    if it should simply fail as soon as an incompatibility is detected.
  - if parse_logformat_string() is called from an unchecked proxy, then
    schedule the expression for postparsing, else (ie: during runtime),
    run the postcheck right away.

This change will also allow for some logformat expression error handling
simplifications in the future.

15 months agoMINOR: proxy: add PR_FL_CHECKED flag
Aurelien DARRAGON [Sat, 9 Mar 2024 21:18:51 +0000 (22:18 +0100)] 
MINOR: proxy: add PR_FL_CHECKED flag

PR_FL_CHECKED is set on proxy once the proxy configuration was fully
checked (including postparsing checks).

This information may be useful to functions that need to know if some
config-related proxy properties are likely to change or not due to parsing
or postparsing/check logics. Also, during runtime, except for some rare cases
config-related proxy properties are not supposed to be changed.

15 months agoMEDIUM: tree-wide: add logformat expressions wrapper
Aurelien DARRAGON [Fri, 23 Feb 2024 14:57:21 +0000 (15:57 +0100)] 
MEDIUM: tree-wide: add logformat expressions wrapper

log format expressions are broadly used within the code: once they are
parsed from input string, they are converted to a linked list of
logformat nodes.

We're starting to face some limitations because we're simply storing the
converted expression as a generic logformat_node list.

The first issue we're facing is that storing logformat expressions that
way doesn't allow us to add metadata alongside the list, which is part
of the prerequites for implementing log-profiles.

Another issue with storing logformat expressions as generic lists of
logformat_node elements is that it's starting to become really hard to
tell when we rely on logformat expressions or not in the code given that
there isn't always a comment near the list declaration or manipulation
to indicate that it's relying on logformat expressions under the hood,
so this adds some complexity for code maintenance.

This patch looks quite impressive due to changes in a lot of header and
source files (since logformat expressions are broadly used), but it does
a simple thing: it defines the lf_expr structure which itself holds a
generic list of logformat nodes, and then declares some helpers to
manipulate lf_expr elements and fixes the code so that we now exclusively
manipulate logformat_node lists as lf_expr elements outside of log.c.

For now, lf_expr struct only contains the list of logformat nodes (no
additional metadata), but now that we have dedicated type and helpers,
doing so in the future won't be problematic at all and won't require
extensive code changes.

15 months agoMEDIUM: log: carry tag context in logformat node
Aurelien DARRAGON [Thu, 22 Feb 2024 19:20:41 +0000 (20:20 +0100)] 
MEDIUM: log: carry tag context in logformat node

This is a pretty simple patch despite requiring to make some visible
changes in the code:

When parsing a logformat string, log tags (ie: '%tag', AKA log tags) are
turned into logformat nodes with their type set to the type of the
corresponding logformat_tag element which was matched by name. Thus, when
"compiling" a logformat tag, we only keep a reference to the tag type
from the original logformat_tag.

For example, for "%B" log tag, we have the following logformat_tag
element:

  {
    .name = "B",
    .type = LOG_FMT_BYTES,
    .mode = PR_MODE_TCP,
    .lw = LW_BYTES,
    .config_callback = NULL
  }

When parsing "%B" string, we search for a matching logformat tag
inside logformat_tags[] array using the provided name, once we find a
matching element, we craft a logformat node whose type will be
LOG_FMT_BYTES, but from the node itself, we no longer have access to
other informations that are set in the logformat_tag struct element.

Thus from a logformat_node resulting from a log tag, with current
implementation, we cannot easily get back to matching logformat_tag
struct element as it would require us to scan the whole logformat_tags
array at runtime using node->type to find the matching element.

Let's take a simpler path and consider all tag-specific LOG_FMT_*
subtypes as being part of the same logformat node type: LOG_FMT_TAG.

Thanks to that, we're now able to distinguish logformat nodes made
from logformat tag from other logformat nodes, and link them to
their corresponding logformat_tag element from logformat_tags[] array. All
it costs is a simple indirection and an extra pointer in logformat_node
struct.

While at it, all LOG_FMT_* types related to logformat tags were moved
inside log.c as they have no use outside of it since they are simply
lookup indexes for sess_build_logline() and could even be replaced by
function pointers some day...

15 months agoMINOR: log: expose logformat_tag struct
Aurelien DARRAGON [Thu, 22 Feb 2024 18:28:40 +0000 (19:28 +0100)] 
MINOR: log: expose logformat_tag struct

rename logformat_type internal struct to logformat_tag to to make it less
confusing, then expose logformat_tag struct through header file so that it
can be referenced in other structs.

also rename logformat_keywords[] to logformat_tags[] for better
consistency.

15 months agoMEDIUM: log: rename logformat var to logformat tag
Aurelien DARRAGON [Tue, 2 Apr 2024 12:51:57 +0000 (14:51 +0200)] 
MEDIUM: log: rename logformat var to logformat tag

What we use to call logformat variable in the code is referred as
log-format tag in the documentation. Having both 'var' and 'tag' labels
referring to the same thing is really confusing. Let's make the code
comply with the documentation by replacing all logformat var/variable/VAR
occurences with either tag or TAG.

No functional change should be expected, the only visible side-effect from
user point of view is that "variable" was replaced by "tag" in some error
messages.

15 months agoBUG/MINOR: proxy: fix logformat expression leak in use_backend rules
Aurelien DARRAGON [Wed, 13 Mar 2024 15:29:38 +0000 (16:29 +0100)] 
BUG/MINOR: proxy: fix logformat expression leak in use_backend rules

When support for dynamic names was added for use_backend rules in
702d44f2f ("MEDIUM: proxy: support use_backend with dynamic names"), the
sample expression resulting from parse_logformat_string() was only freed
for non dynamic rules (when the expression resolved to a simple string
node). But for complex expressions (ie: multiple nodes), rule->dynamic
was set but the expression was never released, resulting in a small
memory leak when freeing the parent proxy.

To fix the issue, in free_proxy(), we free the switching rule expression
if the switching rule is dynamic.

This should be backported to every stable versions.
[ada: prior to 2.9, free_logformat_list() helper did not exist: we may
 use the same manual sample expr freeing logic as in server_rules pruning
 right above it]

15 months agoMINOR: systemd: Include MONOTONIC_USEC field in RELOADING=1 message
Tim Duesterhus [Wed, 3 Apr 2024 20:39:16 +0000 (22:39 +0200)] 
MINOR: systemd: Include MONOTONIC_USEC field in RELOADING=1 message

As per the `sd_notify` manual:

> A field carrying the monotonic timestamp (as per CLOCK_MONOTONIC) formatted
> in decimal in μs, when the notification message was generated by the client.
> This is typically used in combination with "RELOADING=1", to allow the
> service manager to properly synchronize reload cycles. See systemd.service(5)
> for details, specifically "Type=notify-reload".

Thus this change allows users with a recent systemd to switch to
`Type=notify-reload`, should they desire to do so. Correct behavior was
verified with a Fedora 39 VM.

see systemd/systemd#25916

[wla: the service file should be updated this way:]

    diff --git a/admin/systemd/haproxy.service.in b/admin/systemd/haproxy.service.in
    index 22a53d8aab..8c6dadb5e5 100644
    --- a/admin/systemd/haproxy.service.in
    +++ b/admin/systemd/haproxy.service.in
    @@ -8,12 +8,11 @@ EnvironmentFile=-/etc/default/haproxy
     EnvironmentFile=-/etc/sysconfig/haproxy
     Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" "EXTRAOPTS=-S /run/haproxy-master.sock"
     ExecStart=@SBINDIR@/haproxy -Ws -f $CONFIG -p $PIDFILE $EXTRAOPTS
    -ExecReload=@SBINDIR@/haproxy -Ws -f $CONFIG -c $EXTRAOPTS
    -ExecReload=/bin/kill -USR2 $MAINPID
     KillMode=mixed
     Restart=always
     SuccessExitStatus=143
    -Type=notify
    +Type=notify-reload
    +ReloadSignal=SIGUSR2

     # The following lines leverage SystemD's sandboxing options to provide
     # defense in depth protection at the expense of restricting some flexibility

Signed-off-by: William Lallemand <wlallemand@haproxy.com>
15 months agoBUILD: systemd: enable USE_SYSTEMD by default with TARGET=linux-glibc
William Lallemand [Thu, 4 Apr 2024 12:06:11 +0000 (14:06 +0200)] 
BUILD: systemd: enable USE_SYSTEMD by default with TARGET=linux-glibc

Since the systemd notify feature is now independant of any library,
lets enable it by default for linux-glibc.

The -Ws mode still need to be used in order to use the sd_nofify()
function. And the function won't do anything if the NOTIFY_SOCKET
environment variable is not defined.

15 months agoBUG/MINOR: stick-tables: Missing stick-table key nullity check
Frederic Lecaille [Thu, 4 Apr 2024 09:08:56 +0000 (11:08 +0200)] 
BUG/MINOR: stick-tables: Missing stick-table key nullity check

This bug arrived with this commit:
     MAJOR: stktable: split the keys across multiple shards to reduce contention

At this time, there are no callers which call stktable_get_entry() without checking
the nullity of <key> passed as parameter. But the documentation of this function
says it supports this case where the <key> passed as parameter could be null.

Move the nullity test on <key> at first statement of this function.

Thanks to @chipitsine for having reported this issue in GH #2518.

15 months agoCI: extend Fedora Rawhide, add m32 mode
Ilya Shipitsin [Wed, 3 Apr 2024 18:56:22 +0000 (20:56 +0200)] 
CI: extend Fedora Rawhide, add m32 mode

hopefully it will allow to catch regressions like this
https://github.com/haproxy/haproxy/commit/e41638a

15 months agoMAJOR: stktable: split the keys across multiple shards to reduce contention
Willy Tarreau [Mon, 4 Mar 2024 16:09:28 +0000 (17:09 +0100)] 
MAJOR: stktable: split the keys across multiple shards to reduce contention

In order to reduce the contention on the table when keys expire quickly,
we're spreading the load over multiple trees. That counts for keys and
expiration dates. The shard number is calculated from the key value
itself, both when looking up and when setting it.

The "show table" dump on the CLI iterates over all shards so that the
output is not fully sorted, it's only sorted within each shard. The Lua
table dump just does the same. It was verified with a Lua program to
count stick-table entries that it works as intended (the test case is
reproduced here as it's clearly not easy to automate as a vtc):

  function dump_stk()
    local dmp = core.proxies['tbl'].stktable:dump({});
    local count = 0
    for _, __ in pairs(dmp) do
        count = count + 1
    end
    core.Info('Total entries: ' .. count)
  end

  core.register_action("dump_stk", {'tcp-req', 'http-req'}, dump_stk, 0);

  ##
  global
    tune.lua.log.stderr on
    lua-load-per-thread lua-cnttbl.lua

  listen front
    bind :8001
    http-request lua.dump_stk if { path_beg /stk }
    http-request track-sc1 rand(),upper,hex table tbl
    http-request redirect location /

  backend tbl
    stick-table size 100k type string len 12 store http_req_cnt

  ##
  $ h2load -c 16 -n 10000 0:8001/
  $ curl 0:8001/stk

  ## A count close to 100k appears on haproxy's stderr
  ## On the CLI, "show table tbl" | wc will show the same.

Some large parts were reindented only to add a top-level loop to iterate
over shards (e.g. process_table_expire()). Better check the diff using
git show -b.

The number of shards is decided just like for the pools, at build time
based on the max number of threads, so that we can keep a constant. Maybe
this should be done differently. For now CONFIG_HAP_TBL_BUCKETS is used,
and defaults to CONFIG_HAP_POOL_BUCKETS to keep the benefits of all the
measurements made for the pools. It turns out that this value seems to
be the most reasonable one without inflating the struct stktable too
much. By default for 1024 threads the value is 32 and delivers 980k RPS
in a test involving 80 threads, while adding 1kB to the struct stktable
(roughly doubling it). The same test at 64 gives 1008 kRPS and at 128
it gives 1040 kRPS for 8 times the initial size. 16 would be too low
however, with 675k RPS.

The stksess already have a shard number, it's the one used to decide which
peer connection to send the entry. Maybe we should also store the one
associated with the entry itself instead of recalculating it, though it
does not happen that often. The operation is done by hashing the key using
XXH32().

The peers also take and release the table's lock but the way it's used
it not very clear yet, so at this point it's sure this will not work.

At this point, this allowed to completely unlock the performance on a
80-thread setup:

 before: 5.4 Gbps, 150k RPS, 80 cores
  52.71%  haproxy    [.] stktable_lookup_key
  36.90%  haproxy    [.] stktable_get_entry.part.0
   0.86%  haproxy    [.] ebmb_lookup
   0.18%  haproxy    [.] process_stream
   0.12%  haproxy    [.] process_table_expire
   0.11%  haproxy    [.] fwrr_get_next_server
   0.10%  haproxy    [.] eb32_insert
   0.10%  haproxy    [.] run_tasks_from_lists

 after: 36 Gbps, 980k RPS, 80 cores
  44.92%  haproxy    [.] stktable_get_entry
   5.47%  haproxy    [.] ebmb_lookup
   2.50%  haproxy    [.] fwrr_get_next_server
   0.97%  haproxy    [.] eb32_insert
   0.92%  haproxy    [.] process_stream
   0.52%  haproxy    [.] run_tasks_from_lists
   0.45%  haproxy    [.] conn_backend_get
   0.44%  haproxy    [.] __pool_alloc
   0.35%  haproxy    [.] process_table_expire
   0.35%  haproxy    [.] connect_server
   0.35%  haproxy    [.] h1_headers_to_hdr_list
   0.34%  haproxy    [.] eb_delete
   0.31%  haproxy    [.] srv_add_to_idle_list
   0.30%  haproxy    [.] h1_snd_buf

WIP: uint64_t -> long

WIP: ulong -> uint

code is much smaller

15 months agoOPTIM: stick-tables: check the stksess without taking the read lock
Willy Tarreau [Tue, 2 Apr 2024 17:04:12 +0000 (19:04 +0200)] 
OPTIM: stick-tables: check the stksess without taking the read lock

Thanks to the previous commit, we can now simply perform an atomic read
on stksess->seen and take the write lock to recreate the entry only if
at least one peer has seen it, otherwise leave it untouched. On a test
on 40 cores, the performance used to drop from 2.10 to 1.14M RPS when
one peer was connected, now it drops to 2.05, thus there's basically
no impact of connecting a peer vs ~45% previously, all spent in the
read lock. This can be particularly important when often updating the
same entries (user-agent, source address during an attack etc).

15 months agoMINOR: stick-tables: mark the seen stksess with a flag "seen"
Willy Tarreau [Tue, 2 Apr 2024 16:49:53 +0000 (18:49 +0200)] 
MINOR: stick-tables: mark the seen stksess with a flag "seen"

Right now we're taking the stick-tables update lock for reads just for
the sake of checking if the update index is past it or not. That's
costly because even taking the read lock is sufficient to provoke a
cache line write, while when under load or attack it's frequent that
the update has not yet been propagated and wouldn't require anything.

This commit brings a new field to the stksess, "seen", which is zeroed
when the entry is updated, and set to one as soon as at least one peer
starts to consult it. This way it will reflect that the entry must be
updated again so that this peer can see it. Otherwise no update will
be necessary. For now the flag is only set/reset but not exploited.
A great care is taken to avoid writes whenever possible.

15 months agoBUG/MINOR: bwlim/config: fix missing '\n' after error messages
Willy Tarreau [Wed, 3 Apr 2024 09:30:07 +0000 (11:30 +0200)] 
BUG/MINOR: bwlim/config: fix missing '\n' after error messages

Some bwlim error messages at parsing time were missing the trailing '\n'
in commit 2b6777021d ("MEDIUM: bwlim: Add support of bandwith limitation
at the stream level"). This commit can be backported wherever the commit
above is (likely as far as 2.7).

15 months agoBUILD: systemd: fix build error on non-systemd systems with USE_SYSTEMD=1
Willy Tarreau [Wed, 3 Apr 2024 15:32:20 +0000 (17:32 +0200)] 
BUILD: systemd: fix build error on non-systemd systems with USE_SYSTEMD=1

Thanks to previous commit, we can now build with USE_SYSTEMD=1 on any
system without requiring any parts from systemd. It just turns our that
there was one remaining include in haproxy.c that needed to be replaced
with haproxy/systemd.h to build correctly. That's what this commit does.

15 months agoMEDIUM: mworker: get rid of libsystemd
William Lallemand [Wed, 3 Apr 2024 13:13:00 +0000 (15:13 +0200)] 
MEDIUM: mworker: get rid of libsystemd

Given the xz drama which allowed liblzma to be linked to openssh, lets remove
libsystemd to get rid of useless dependencies.

The sd_notify API seems to be stable and is now documented. This patch replaces
the sd_notify() and sd_notifyf() function by a reimplementation inspired by the
systemd documentation.

This should not change anything functionnally. The function will be built when
haproxy is built using USE_SYSTEMD=1.

References:
  https://github.com/systemd/systemd/issues/32028
  https://www.freedesktop.org/software/systemd/man/devel/sd_notify.html#Notes

Before:

wla@kikyo:~% ldd /usr/sbin/haproxy
linux-vdso.so.1 (0x00007ffcfaf65000)
libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x000074637fef4000)
libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x000074637fe4f000)
libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x000074637f400000)
liblua5.4.so.0 => /lib/x86_64-linux-gnu/liblua5.4.so.0 (0x000074637fe0d000)
libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x000074637f92a000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x000074637f365000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000074637f000000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x000074637f27a000)
libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x000074637fdff000)
libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x000074637eeb8000)
liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x000074637fdcd000)
libzstd.so.1 => /lib/x86_64-linux-gnu/libzstd.so.1 (0x000074637ee01000)
liblz4.so.1 => /lib/x86_64-linux-gnu/liblz4.so.1 (0x000074637fda8000)
/lib64/ld-linux-x86-64.so.2 (0x000074637ff5d000)
libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x000074637f904000)

After:

wla@kikyo:~% ldd /usr/sbin/haproxy
linux-vdso.so.1 (0x00007ffd51901000)
libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007f758d6c0000)
libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x00007f758d61b000)
libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f758ca00000)
liblua5.4.so.0 => /lib/x86_64-linux-gnu/liblua5.4.so.0 (0x00007f758d5d9000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f758d365000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f758d5ba000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f758c600000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f758c915000)
/lib64/ld-linux-x86-64.so.2 (0x00007f758d729000)

A backport to all stable versions could be considered at some point.

15 months agoBUG/MEDIUM: server/lbprm: fix crash in _srv_set_inetaddr_port()
Aurelien DARRAGON [Wed, 3 Apr 2024 09:41:57 +0000 (11:41 +0200)] 
BUG/MEDIUM: server/lbprm: fix crash in _srv_set_inetaddr_port()

Since faa8c3e ("MEDIUM: lb-chash: Deterministic node hashes based on
server address") the following configuration will cause haproxy to crash:

  backend test1
       mode http
       balance hash int(1)
       server s1 haproxy.org:80

This is because lbprm.update_server_eweight() method is now systematically
called in _srv_set_inetaddr_port() upon srv addr/port change (and with the
above config it happens during startup after initial dns resolution).

However, depending on the chosen lbprm algo, update_server_eweight function
may not be set (it is not a mandatory method, some lb implementations don't
define it).

Thus, using 'balance hash' with map-based hashing or 'balance sticky' will
cause a crash due to a NULL de-reference in _srv_set_inetaddr_port(). To
fix the issue, we first check that the update_server_eweight() method is
set before using it.

No backport needed unless faa8c3e ("MEDIUM: lb-chash: Deterministic node
hashes based on server address") gets backported.

15 months agoBUILD: quic: 32 bits compilation issue (QUIC_MIN() usage)
Frederic Lecaille [Wed, 3 Apr 2024 09:05:28 +0000 (11:05 +0200)] 
BUILD: quic: 32 bits compilation issue (QUIC_MIN() usage)

This issue arrived with this commit:

"MINOR: quic: HyStart++ implementation (RFC 9406)"

Thanks to @chipitsine for having reported this issue in GH #2513.

Should be backported where the previous commit will be backported.

15 months agoOPTIM: peers: avoid the locking dance around peer_send_teach_process_msgs()
Willy Tarreau [Tue, 2 Apr 2024 16:27:36 +0000 (18:27 +0200)] 
OPTIM: peers: avoid the locking dance around peer_send_teach_process_msgs()

In peer_send_msg(), we take a lock before calling
peer_send_teach_process_msgs because of the check on the flags and update
indexes, and the function then drops it then takes it again just to resume
in the same situation, so that on return we can drop it again! Not only
this is absurd because it doubles the costs of taking the lock, it's also
totally inefficient because it takes a write lock while the only usage
that is done with it is to read the indexes! Let's drop the lock from
peer_send_teach_process_msgs() and move it explicitly in its only caller
around the condition, and turn it into a read lock only.

15 months agoBUG/MAJOR: applet: fix a MIN vs MAX usage in appctx_raw_rcv_buf()
Willy Tarreau [Wed, 3 Apr 2024 07:25:43 +0000 (09:25 +0200)] 
BUG/MAJOR: applet: fix a MIN vs MAX usage in appctx_raw_rcv_buf()

The MAX() macro was used to limit the count of bytes to be transferred
in appctx_raw_rcv_buf() by commit ee53d8421f ("MEDIUM: applet: Simplify
a bit API to exchange data with applets") instead of MIN(). It didn't
seem to have any consequences until commit f37ddbeb4b ("MAJOR: cli:
Update the CLI applet to handle its own buffers") that triggers a BUG_ON()
in __b_putblk() when the other side is slow to read, because we're trying
to append a full buffer on top of a non-empty one. A way to reproduce it
is to dump a heavy stick table on the CLI with a screen scrolling.

No backport is needed since this was introduced in 3.0-dev3 and revealed
after dev5 only.

15 months agoBUG/MEDIUM: stick-table: use the update lock when reading tables from peers
Willy Tarreau [Tue, 2 Apr 2024 16:12:09 +0000 (18:12 +0200)] 
BUG/MEDIUM: stick-table: use the update lock when reading tables from peers

In 2.9, the stick-tables' locking was split between the lock used to
manipulate the contents (->lock) and the lock used to manipulate the
list of updates and the update indexes (->updt_lock). This was done
with commit 87e072eea5 ("MEDIUM: stick-table: use a distinct lock for
the updates tree"). However a part was overlooked in the peers code,
the parts that consult (and update) the indexes use the table's lock
instead of the update lock. It's surprising that it hasn't caused more
trouble. It's likely due to the fact that the tree nodes are not often
immediately freed and that their memory area remains connected to valid
nodes in the tree during peer_stksess_lookup(), while other parts only
check or update indexes, thus are not that critical.

This needs to be backported wherever the commit above is, thus logically
2.9.

15 months agoBUG/MEDIUM: stconn: Don't forward shutdown to SE if iobuf is not empty
Christopher Faulet [Tue, 2 Apr 2024 16:01:23 +0000 (18:01 +0200)] 
BUG/MEDIUM: stconn: Don't forward shutdown to SE if iobuf is not empty

It is only an issue when the kernel splicing is used. The zero-copy
forwarding via the buffers is not affected. When a shutdown is received on
the producer side and some data are blocked in the pipe for a while, the
shutdown may be forwarded to the other side. Usually, in this case, the
shutdown must be scheduled, waiting all output data (from the channel and
the consumer's iobuf) are sent. But only the channel was considered.

The bug was introduced by commit 20c463955d ("MEDIUM: channel: don't look at
iobuf to report an empty channel"). To fix the issue, we must also check
data blocked in the consummer iobuf.

This patch should solve the issue #2505. It must be backported to 2.9.

15 months agoMINOR: quic: HyStart++ implementation (RFC 9406)
Frederic Lecaille [Tue, 5 Mar 2024 17:30:41 +0000 (18:30 +0100)] 
MINOR: quic: HyStart++ implementation (RFC 9406)

This is a simple algorithm to replace the classic slow start phase of the
congestion control algorithms. It should reduce the high packet loss during
this step.

Implemented only for Cubic.

15 months agoBUG/MINOR: backend: properly handle redispatch 0
Willy Tarreau [Tue, 2 Apr 2024 13:15:32 +0000 (15:15 +0200)] 
BUG/MINOR: backend: properly handle redispatch 0

According to the documentation, "option redispatch 0" is expected to
disable redispatch just like "no option redispatch", but due to the
fact that it keeps PR_O_REDISP set, it doesn't actually work. Let's
make sure value 0 is properly handled and drops PR_O_REDISP. This can
be backported to all versions since it seems it has been broken since
its introduction in 1.6 with commit 726ab7145c ("MEDIUM: backend: Allow
redispatch on retry intervals").

As a workaround, "no option redispatch" does work though.

15 months agoREGTESTS: ssl: disable ssl/ocsp_auto_update.vtc
William Lallemand [Tue, 2 Apr 2024 12:20:39 +0000 (14:20 +0200)] 
REGTESTS: ssl: disable ssl/ocsp_auto_update.vtc

Test is broken, keep it disable for now.

  Add test: reg-tests/ssl/ocsp_auto_update.vtc
Testing with haproxy version: 3.0-dev6-9dd928-35
1 tests failed, 0 tests skipped, 0 tests passed
*    diag  0.0 /usr/bin/openssl
*    diag  0.0 /usr/bin/socat
make: *** [Makefile:1177: reg-tests] Error 1

15 months agoCI: vtest: show coredumps if any
Ilya Shipitsin [Wed, 27 Mar 2024 15:49:54 +0000 (16:49 +0100)] 
CI: vtest: show coredumps if any

if any coredump is found, it is passed to gdb with
'thread apply all bt full'

15 months agoCLEANUP: Reapply ha_free.cocci
Tim Duesterhus [Fri, 29 Mar 2024 17:21:53 +0000 (18:21 +0100)] 
CLEANUP: Reapply ha_free.cocci

This reapplies ha_free.cocci across the whole src/ tree.

15 months agoCLEANUP: Reapply xalloc_cast.cocci
Tim Duesterhus [Fri, 29 Mar 2024 17:21:52 +0000 (18:21 +0100)] 
CLEANUP: Reapply xalloc_cast.cocci

This reapplies xalloc_cast.cocci across the whole src/ tree.

15 months agoCLEANUP: Reapply strcmp.cocci (2)
Tim Duesterhus [Fri, 29 Mar 2024 17:21:51 +0000 (18:21 +0100)] 
CLEANUP: Reapply strcmp.cocci (2)

This reapplies strcmp.cocci across the whole src/ tree.

15 months agoCLEANUP: Reapply ist.cocci (3)
Tim Duesterhus [Fri, 29 Mar 2024 17:21:50 +0000 (18:21 +0100)] 
CLEANUP: Reapply ist.cocci (3)

This reapplies ist.cocci across the whole src/ tree.

15 months agoREGTESTS: Remove REQUIRE_VERSION=1.9 from all tests (2)
Tim Duesterhus [Fri, 29 Mar 2024 16:12:48 +0000 (17:12 +0100)] 
REGTESTS: Remove REQUIRE_VERSION=1.9 from all tests (2)

see also:

2a5fb62ad REGTESTS: Remove REQUIRE_VERSION=1.9 from all tests

15 months agoREGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+ (4)
Tim Duesterhus [Fri, 29 Mar 2024 16:12:47 +0000 (17:12 +0100)] 
REGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+ (4)

Introduced in:

dfb1cea69 REGTESTS: promex: Adapt script to be less verbose
36d936dd1 REGTESTS: write a full reverse regtest
b57f15158 REGTESTS: provide a reverse-server test with name argument
f0bff2947 REGTESTS: provide a reverse-server test

see also:

fbbbc33df REGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+

15 months agoBUG/MEDIUM: stick-tables: fix a small remaining race in expiration task
Willy Tarreau [Tue, 2 Apr 2024 05:07:57 +0000 (07:07 +0200)] 
BUG/MEDIUM: stick-tables: fix a small remaining race in expiration task

In 2.7 we addressed a race condition in the stick tables expiration task
with commit fbb934d ("BUG/MEDIUM: stick-table: fix a race condition when
updating the expiration task"). The issue was that the task could be
running on another thread which would destroy its expiration timer
while one had just recalculated it and prepares to queue it, causing
a bug due to the attempt to queue an expired task. The fix consisted in
enclosing the change into the stick-table's lock, which had a very low
cost since it's done only after having checked that the date changed,
i.e. no more than once every millisecond.

But as reported by Ricardo and Felipe from Taghos in github issue #2508,
a tiny race remained after the fix: the unlock() was done before the call
to task_queue(), leaving a tiny window for another thread to run between
unlock() and task_queue() and erase the timer. As confirmed, it's
sufficient to also protect the task_queue() call.

But overall this raises a point regarding the task_queue() API on tasks
that may run anywhere. A while ago an attempt was made at removing the
timer for woken up tasks, but something like this would be deserved
with more atomicity on the timer manipulation (e.g. atomically use
task_schedule() instead maybe). This should be backported to all
stable branches.

15 months agoMEDIUM: lb-chash: Deterministic node hashes based on server address
Anthony Deschamps [Fri, 16 Feb 2024 21:00:35 +0000 (16:00 -0500)] 
MEDIUM: lb-chash: Deterministic node hashes based on server address

Motivation: When services are discovered through DNS resolution, the order in
which DNS records get resolved and assigned to servers is arbitrary. Therefore,
even though two HAProxy instances using chash balancing might agree that a
particular request should go to server3, it is likely the case that they have
assigned different IPs and ports to the server in that slot.

This patch adds a server option, "hash-key <key>" which can be set to "id" (the
existing behaviour, default), "addr", or "addr-port". By deriving the keys for
the chash tree nodes from a server's address and port we ensure that independent
HAProxy instances will agree on routing decisions. If an address is not known
then the key is derived from the server's puid as it was previously.

When adjusting a server's weight, we now check whether the server's hash has
changed. If it has, we have to remove all its nodes first, since the node keys
will also have to change.

15 months agoBUG/BUILD: debug: fix unused variable error
Amaury Denoyelle [Fri, 29 Mar 2024 16:17:35 +0000 (17:17 +0100)] 
BUG/BUILD: debug: fix unused variable error

A compilation error occurs when using DEBUG_MEM_STATS due to a variable
now being unused in debug_iohandler_memstats() :

src/debug.c: In function ‘debug_iohandler_memstats’:
src/debug.c:1862:24: error: unused variable ‘sc’ [-Werror=unused-variable]
 1862 |         struct stconn *sc = appctx_sc(appctx);
      |                        ^~

This is caused since the following commit :
  94b8ed446f70a381dde0ea2dc891b62fcc3cc8e1
  MEDIUM: cli/applet: Stop to test opposite SC in I/O handler of CLI commands

This must not be backported.

15 months agoMEDIUM: log/balance: leverage lbprm api for log load-balancing
Aurelien DARRAGON [Thu, 28 Mar 2024 14:42:37 +0000 (15:42 +0100)] 
MEDIUM: log/balance: leverage lbprm api for log load-balancing

log load-balancing implementation was not seamlessly integrated within
lbprm API. The consequence is that it could become harder to maintain
over time since it added some specific cases just for the log backend.
Moreover, it resulted in some code duplication since balance algorithms
that are common to logs and regular (tcp, http) backends were specifically
rewritten for log backends.

Thanks to the previous commit, we now have all the prerequisites to make
log load-balancing fully leverage lbprm logic. Thus in this patch we make
__do_send_log_backend() use existing lbprm algorithms, and we no longer
require log-specific lbprm initialization in cfgparse.c and in
postcheck_log_backend().

As a bonus, for log backends this allows weighed algorithms to properly
support weights (ie: roundrobin, random and log-hash) since we now
leverage the same lb algorithms that we use for tcp/http backends
(doc was updated).

15 months agoMINOR: lbprm: implement true "sticky" balance algo
Aurelien DARRAGON [Thu, 28 Mar 2024 16:24:53 +0000 (17:24 +0100)] 
MINOR: lbprm: implement true "sticky" balance algo

As previously mentioned in cd352c0db ("MINOR: log/balance: rename
"log-sticky" to "sticky""), let's define a sticky algorithm that may be
used from any protocol. Sticky algorithm sticks on the same server as
long as it remains available.

The documentation was updated accordingly.

15 months agoBUG/MINOR: log/balance: detect if user tries to use unsupported algo
Aurelien DARRAGON [Thu, 28 Mar 2024 15:06:58 +0000 (16:06 +0100)] 
BUG/MINOR: log/balance: detect if user tries to use unsupported algo

b61147fd ("MEDIUM: log/balance: merge tcp/http algo with log ones")
introduced some ambiguities, because while it shares some algos with the
ones from mode {tcp,http}, we forgot report an error when the user tries
to use an algorithm that is not available in this mode (as per the doc).

Because of that, haproxy would silently drop log messages during runtime.

To fix that, we ensure that algo is one of the supported ones during log
backend postparsing. If the algo is not supported, we raise an error.

This should be backported in 2.9 with b61147fd

15 months agoDOC: config: balance 'first' not usable in LOG mode
Aurelien DARRAGON [Thu, 28 Mar 2024 14:48:05 +0000 (15:48 +0100)] 
DOC: config: balance 'first' not usable in LOG mode

b61147fd ("MEDIUM: log/balance: merge tcp/http algo with log ones")
introduced an ambiguity because 'first' algorithm is not usable in
LOG mode but it was not specified in the doc.

This should be backported in 2.9 with b61147fd.

15 months agoMAJOR: cli: Use a custom .snd_buf function to only copy the current command
Christopher Faulet [Tue, 20 Feb 2024 07:47:38 +0000 (08:47 +0100)] 
MAJOR: cli: Use a custom .snd_buf function to only copy the current command

The CLI applet is now using its own snd_buf callback function. Instead of
copying as most output data as possible, only one command is copied at a
time.

To do so, a new state CLI_ST_PARSEREQ is added for the CLI applet. In this
state, the CLI I/O handle knows a full command was copied into its input
buffer and it must parse this command to evaluate it.

15 months agoMINOR: stconn: Add a connection flag to notify sending data are the last ones
Christopher Faulet [Tue, 20 Feb 2024 07:43:09 +0000 (08:43 +0100)] 
MINOR: stconn: Add a connection flag to notify sending data are the last ones

This flag can be use by endpoints to know the data to send, via .snd_buf
callback function are the last ones. It is useful to know a shutdown is
pending but it cannot be delivered while sedning data are not consumed.

15 months agoMINOR: applet: Let's applets .snd_buf function deal with full input buffers
Christopher Faulet [Tue, 20 Feb 2024 07:29:50 +0000 (08:29 +0100)] 
MINOR: applet: Let's applets .snd_buf function deal with full input buffers

It is now the responsbility of applets .snd_buf callback function to notify
the input buffer is full. This will allow the applets to not consume all
data waiting for more data. Of course, it is only useful for applets using a
custom .snd_buf callback function.

15 months agoMAJOR: cli: Update the CLI applet to handle its own buffers
Christopher Faulet [Thu, 15 Feb 2024 12:34:05 +0000 (13:34 +0100)] 
MAJOR: cli: Update the CLI applet to handle its own buffers

It is the third applet to be refactored to use its own buffers. In addition to
the CLI applet, some I/O handlers of CLI commands were also updated, especially
the stats ones.

Some command I/O handlers were updated to use applet's buffers instead of
channels ones.

15 months agoBUG/MEDIUM: applet: State appctx have more data if its EOI/EOS/ERROR flag is set
Christopher Faulet [Thu, 15 Feb 2024 16:27:08 +0000 (17:27 +0100)] 
BUG/MEDIUM: applet: State appctx have more data if its EOI/EOS/ERROR flag is set

It is an harmless bug for now because only stats and cache applets are using
their own buffers and it is not possible to trigger this bug with these
applets. However, it remains important to try a receive if EOI, EOS or ERROR
is reached by the applet while no data was produced. Otherwise, it is not
possible to ack these events at the SE level.

No backport needed.

15 months agoMINOR: applet: Always use applet API to set appctx flags
Christopher Faulet [Thu, 15 Feb 2024 10:23:00 +0000 (11:23 +0100)] 
MINOR: applet: Always use applet API to set appctx flags

Some appctx flags were still set manually while there is a dedicated
function to do so. Be sure to always use applet_fl_set() to set appctx
flags.

15 months agoMEDIUM: cli/applet: Stop to test opposite SC in I/O handler of CLI commands
Christopher Faulet [Tue, 6 Feb 2024 13:54:54 +0000 (14:54 +0100)] 
MEDIUM: cli/applet: Stop to test opposite SC in I/O handler of CLI commands

The main CLI I/O handle is responsible to interrupt the processing on
shutdown/abort. It is not the responsibility of the I/O handler of CLI
commands to take care of it.

15 months agoMEDIUM: applet: Handle applets with their own buffers in put functions
Christopher Faulet [Tue, 6 Feb 2024 10:48:02 +0000 (11:48 +0100)] 
MEDIUM: applet: Handle applets with their own buffers in put functions

applet_putchk() and other similar functions are now testing the applet's
type to use the applet's outbuf instead of the channel's buffer. This will
ease applets convertion because most of them relies on these functions.

15 months agoMEDIUM: buf: Add b_getline() and b_getdelim() functions
Christopher Faulet [Tue, 6 Feb 2024 06:57:51 +0000 (07:57 +0100)] 
MEDIUM: buf: Add b_getline() and b_getdelim() functions

These functions are very similar to co_getline() and co_getdelim(). The
first one retrieves the longest part of the buffer that is composed
exclusively of characters not in the a delimiter set. The second one stops
on LF only and thus returns a line.

15 months agoMEDIUM: stream: Use generic version to perform sync receives and sends
Christopher Faulet [Thu, 1 Feb 2024 15:36:25 +0000 (16:36 +0100)] 
MEDIUM: stream: Use generic version to perform sync receives and sends

Instead of using connection versions, we now use generic versions. It means
we will also perfom sync receives and sync sends on applets too, but only
for applets using their own buffers. Old applets are not concerned.

15 months agoMINOR: sc_strm: Add generic version to perform sync receives and sends
Christopher Faulet [Thu, 1 Feb 2024 15:31:03 +0000 (16:31 +0100)] 
MINOR: sc_strm: Add generic version to perform sync receives and sends

sc_sync_recv() and sc_sync_send() were added to use connection or applet
versions, depending on the endpoint type. For now these functions are not
used. But this will be used by process_stream() to replace the connection
version.

15 months agoBUG/MINOR: cli: Report an error to user if command or payload is too big
Christopher Faulet [Thu, 28 Mar 2024 13:52:29 +0000 (14:52 +0100)] 
BUG/MINOR: cli: Report an error to user if command or payload is too big

Too big command, larger than a buffer, was silently rejected by the CLI
applet. It was handled as an error and the connection was closed, but no
error message was reported to user to notify him. Now an error is reported
before closing. It is only displayed if the chunk buffer used by the CLI
applet is full and no delimiter (\n or ;) is found to mark the end of the
command. It works for a simple command but also for a command with a huge
payload.

This patch could be backported to all stable versions.

15 months agoREGTESTS: Fix script about OCSP update compatibility tests
Christopher Faulet [Thu, 28 Mar 2024 16:23:51 +0000 (17:23 +0100)] 
REGTESTS: Fix script about OCSP update compatibility tests

There were two occurrences of the seventh test. I don't know really why, but
this triggered a VTC error:

---- h7    Assert error in _assert_VSB_state(), lib/vsb.c line 104:  Condition((s->s_flags & 0x00020000) == state) not true.  Errno=0 Success

Renumbering tests fixes the script.

15 months agoMINOR: server: allow cookie for dynamic servers
Amaury Denoyelle [Wed, 27 Mar 2024 09:50:21 +0000 (10:50 +0100)] 
MINOR: server: allow cookie for dynamic servers

This commit allows "cookie" keyword for dynamic servers. After code
review, nothing was found which could prevent a dynamic server to use
it. An extra warning is added under cli_parse_add_server() if cookie
value is ignored due to a non HTTP backend.

This patch is not considered a bugfix. However, it may backported if
needed as its impact seems minimal.

15 months agoBUG/MINOR: server: fix persistence cookie for dynamic servers
Damien Claisse [Wed, 27 Mar 2024 14:34:25 +0000 (14:34 +0000)] 
BUG/MINOR: server: fix persistence cookie for dynamic servers

When adding a server dynamically, we observe that when a backend has a
dynamic persistence cookie, the new server has no cookie as we receive
the following HTTP header:
set-cookie: test-cookie=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/
Whereas we were expecting to receive something like the following, which
is what we receive for a server added in the config file:
set-cookie: test-cookie=abcdef1234567890; path=/

After investigating code path, srv_set_dyncookie() is never called when
adding a server through CLI, it is only called when parsing config file
or using "set server bkd1/srv1 addr".

To fix this, call srv_set_dyncookie() inside cli_parse_add_server().

This patch must be backported up to 2.4.

15 months agoBUG/MINOR: server: reject enabled for dynamic server
Amaury Denoyelle [Fri, 22 Mar 2024 16:40:16 +0000 (17:40 +0100)] 
BUG/MINOR: server: reject enabled for dynamic server

Since their first implementation, dynamic servers are created into
maintenance state. This has been done purposely to avoid immediate
activation of a newly inserted server.

However, this principle is incompatible if "enabled" keyword is used on
"add server". The newly created instance will be unreacheable as proxy
load-balancing algorithm is not informed of its presence via
srv_lb_propagate(). The new server could be unblocked by toggling its
state with "disable server" / "enable server" commands, which will
trigger srv_lb_propagate() invocation.

To avoid this unexpected state, simply forbid "enabled" keyword for
dynamic servers. In the long-term, it could be possible to re authorize
it but at least this requires to call srv_lb_propagate() on dynamic
server creation.

This should fix github issue #2497.

This patch should not be backported as-is, to avoid breaking dynamic
servers API on stable versions. "enabled" should instead be ignored for
them. This will be implemented in a dedicated patch on top of 2.9.

15 months agoREGTESTS: ssl: Add functional test for global ocsp-update option
Remi Tricot-Le Breton [Mon, 25 Mar 2024 15:50:27 +0000 (16:50 +0100)] 
REGTESTS: ssl: Add functional test for global ocsp-update option

Add tests for the 'tune.ssl.ocsp-update.mode' global option that can be
used to enable ocsp auto update on all certificates.

15 months agoREGTESTS: ssl: Add OCSP update compatibility tests
Remi Tricot-Le Breton [Mon, 25 Mar 2024 15:50:26 +0000 (16:50 +0100)] 
REGTESTS: ssl: Add OCSP update compatibility tests

Add tests that focus on the incompatibility checks on ocsp-update mode.
This test will only call "haproxy -c" on multiple configurations that
combine the crt-list 'ocsp-update' option and the global
'tune.ssl.ocsp-update.mode'.

15 months agoMEDIUM: ssl: Add 'tune.ssl.ocsp-update.mode' global option
Remi Tricot-Le Breton [Mon, 25 Mar 2024 15:50:25 +0000 (16:50 +0100)] 
MEDIUM: ssl: Add 'tune.ssl.ocsp-update.mode' global option

This option can be used to set a default ocsp-update mode for all
certificates of a given conf file. It allows to activate ocsp-update on
certificates without the need to create separate crt-lists. It can still
be superseded by the crt-list 'ocsp-update' option. It takes either "on"
or "off" as value and defaults to "off".
Since setting this new parameter to "on" would mean that we try to
enable ocsp-update on any certificate, and also certificates that don't
have an OCSP URI, the checks performed in ssl_sock_load_ocsp were
softened. We don't systematically raise an error when trying to enable
ocsp-update on a certificate that does not have an OCSP URI, be it via
the global option or the crt-list one. We will still raise an error when
a user tries to load a certificate that does have an OCSP URI but a
missing issuer certificate (if ocsp-update is enabled).

15 months agoBUG/MINOR: ssl: Detect more 'ocsp-update' incompatibilities
Remi Tricot-Le Breton [Mon, 25 Mar 2024 15:50:24 +0000 (16:50 +0100)] 
BUG/MINOR: ssl: Detect more 'ocsp-update' incompatibilities

The inconsistencies in 'ocsp-update' parameter were only checked when
parsing a crt-list line so if a certificate was used on a bind line
after being used in a crt-list with 'ocsp-update' set to 'on', then no
error would be raised. This patch helps detect such inconsistencies.

This patch can be backported up to branch 2.8.

15 months agoBUG/MINOR: ssl: Wrong ocsp-update "incompatibility" error message
Remi Tricot-Le Breton [Mon, 25 Mar 2024 15:50:23 +0000 (16:50 +0100)] 
BUG/MINOR: ssl: Wrong ocsp-update "incompatibility" error message

In a crt-list such as the following:
    foo.pem [ocsp-update off] foo.com
    foo.pem bar.com
we would get a wrong "Incompatibilities found in OCSP update mode ..."
error message during init when the two lines are actually saying the
same thing since the default for 'ocsp-update' option is 'off'.

This patch can be backported up to branch 2.8.

15 months ago[RELEASE] Released version 3.0-dev6 v3.0-dev6
Willy Tarreau [Tue, 26 Mar 2024 14:36:49 +0000 (15:36 +0100)] 
[RELEASE] Released version 3.0-dev6

Released version 3.0-dev6 with the following main changes :
    - MINOR: mux-h2: always use h2c_report_glitch()
    - MEDIUM: mux-h2: allow to set the glitches threshold to kill a connection
    - MINOR: quic: simplify rescheduling for handshake
    - MINOR: quic: remove qc_treat_rx_crypto_frms()
    - DOC: configuration: clarify ciphersuites usage (V2)
    - MINOR: tools: use public interface for FreeBSD get_exec_path()
    - BUG/MINOR: ssl: fix possible ctx memory leak in sample_conv_aes_gcm()
    - BUG/MINOR: ssl: do not set the aead_tag flags in sample_conv_aes_gcm()
    - BUG/MINOR: server: fix first server template not being indexed
    - MEDIUM: ssl: initialize the SSL stack explicitely
    - MEDIUM: ssl: allow to change the OpenSSL security level from global section
    - CLEANUP: ssl: remove useless #ifdef in openssl-compat.h
    - CI: github: add -DDEBUG_LIST to the default builds
    - BUG/MINOR: hlua: segfault when loading the same filter from different contexts
    - BUG/MINOR: hlua: missing lock in hlua_filter_new()
    - BUG/MINOR: hlua: fix missing lock in hlua_filter_delete()
    - DEBUG: lua: precisely identify if stream is stuck inside lua or not
    - MINOR: hlua: use accessors for stream hlua ctx
    - BUG/MEDIUM: hlua: streams don't support mixing lua-load with lua-load-per-thread (2nd try)
    - MINOR: debug: enable insecure fork on the command line
    - CI: github: add -dI to haproxy arguments
    - BUG/MINOR: listener: Wake proxy's mngmt task up if necessary on session release
    - BUG/MINOR: listener: Don't schedule frontend without task in listener_release()
    - MINOR: session: rename private conns elements
    - BUG/MAJOR: server: do not delete srv referenced by session
    - BUG/MEDIUM: spoe: Don't rely on stream's expiration to detect processing timeout
    - BUG/MINOR: spoe: Be sure to be able to quickly close IDLE applets on soft-stop
    - MAJOR: spoe: Deprecate the SPOE filter
    - MINOR: cfgparse: Add a global option to expose deprecated directives
    - MINOR: spoe: Add SPOE filters in the exposed deprecated directives
    - CLEANUP: assorted typo fixes in the code and comments
    - CI: temporarily adjust kernel entropy to work with ASAN/clang
    - BUG/MEDIUM: spoe: Return an invalid frame on recv if size is too small
    - BUG/MINOR: session: ensure conn owner is set after insert into session
    - BUG/MEDIUM: http_ana: ignore NTLM for reuse aggressive/always and no H1
    - BUG/MAJOR: connection: fix server used_conns with H2 + reuse safe
    - BUG/MAJOR: ocsp: Separate refcount per instance and per store
    - REGTESTS: ssl: Add OCSP related tests
    - BUG/MEDIUM: ssl: Fix crash when calling "update ssl ocsp-response" when an update is ongoing
    - BUG/MEDIUM: ssl: Fix crash in ocsp-update log function
    - MEDIUM: ssl: Change output of ocsp-update log
    - MINOR: ssl: Change level of ocsp-update logs
    - CLEANUP: ssl: Remove undocumented ocsp fetches
    - REGTESTS: ssl: Add checks on ocsp-update log format
    - MINOR: connection: implement conn_release()
    - MINOR: connection: extend takeover with release option
    - MEDIUM: server: close idle conn on server deletion
    - MEDIUM: mux: prepare for takeover on private connections
    - MEDIUM: server: close private idle connection before server deletion
    - BUG/MINOR: mux-quic: close all QCS before freeing QCC tasklet
    - BUG/MEDIUM: mux-fcgi: Properly handle EOM flag on end-of-trailers HTX block
    - BUILD: server: fix build regression on old compilers (<= gcc-4.4)
    - OPTIM: http_ext: avoid useless copy in http_7239_extract_{ipv4,ipv6}
    - MINOR: debug: add "debug dev trace" to flood with traces
    - MINOR: atomic: add a read-specific variant of __ha_cpu_relax()
    - MINOR: applet: add new function applet_append_line()
    - MINOR: log/applet: add new function syslog_applet_append_event()
    - MEDIUM: ring/sink: use applet_append_line()/syslog_applet_append_event() for readers
    - REORG: dns/ring: split the ring between the generic one and the DNS one
    - MEDIUM: ring: move the ring reader code to ring_dispatch_messages()
    - MEDIUM: sink: move the generic ring forwarder code use ring_dispatch_messages()
    - MEDIUM: log/sink: make the log forwarder code use ring_dispatch_messages()
    - MINOR: buf: add b_add_ofs() to add a count to an absolute position
    - MINOR: buf: add b_rel_ofs() to turn an absolute offset into a relative one
    - MINOR: buf: add b_putblk_ofs() to copy a block at a specific position
    - MINOR: buf: add b_getblk_ofs() that works relative to area and not head
    - MINOR: ring: make the ring reader use only absolute offsets
    - MINOR: ring: reserve one special value for the readers count
    - MINOR: vecpair: add new vector pair based data manipulation mechanisms
    - MINOR: vecpair: add necessary functions to use vecpairss from/to ring APIs
    - MINOR: ring: rename totlen vs msglen in ring_write()
    - MINOR: ring: add ring_data() to report the amount of data in a ring
    - MINOR: ring: add ring_size() to return the ring's size
    - MINOR: ring: add ring_dup() to copy a ring into another one
    - MINOR: ring: also add ring_area(), ring_head(), ring_tail()
    - MINOR: ring: make callers use ring_data() and ring_size(), not ring->buf
    - MINOR: errors: use ring_dup() to duplicate the startup_logs
    - MINOR: ring: use ring_size(), ring_area(), ring_head() and ring_tail()
    - MINOR: ring: add a flag to indicate a mapped file
    - MAJOR: ring: insert an intermediary ring_storage level
    - MINOR: ring: resize only under thread isolation
    - MINOR: ring: allow to reduce a ring size
    - MEDIUM: ring: replace the buffer API in ring_write() with the vec<->ring API
    - MEDIUM: ring: change the ring reader to use the new vector-based API now
    - MEDIUM: ring: remove the struct buffer from the ring
    - MEDIUM: ring: align the head and tail fields in the ring_storage structure
    - MINOR: ring: make the reader check the readers count before inc/dec
    - MEDIUM: ring: lock the tail's readers counters before proceeding with the changes
    - MEDIUM: ring: protect the reader's positions against writers
    - MEDIUM: ring: use the topmost bit of the tail as a lock
    - MEDIUM: move the ring's lock to only protect the readers list
    - MEDIUM: ring: unlock the ring's tail earlier
    - MINOR: ring: don't take the readers lock if there are no readers
    - MEDIUM: ring/applet: turn the wait_entry list to an mt_list instead
    - MEDIUM: ring: protect the initialization of the initial reader offset
    - MINOR: ring: make sure ring_dispatch waits when facing a changing message
    - MAJOR: ring: drop the now unneeded lock
    - OPTIM: ring: don't even try to update offset when failed to read
    - OPTIM: ring: have only one thread at a time wake up all readers
    - MINOR: ring: keep a few frequently used pointers in the local stack
    - MINOR: ring: add the definition of a ring waiting cell
    - MINOR: ring: make the number of queues configurable
    - MAJOR: ring: implement a waiting queue in front of the ring
    - MEDIUM: ring: significant boost in the loop by checking the ring queue ptr first
    - MEDIUM: ring: improve speed in the queue waiting loop on x86_64
    - MINOR: ring: simplify the write loop a little bit
    - CLEANUP: ring: further simplify the write loop
    - MINOR: ring: it's not x86 but all non-ARMv8.1 which needs the read before OR
    - MINOR: ring: avoid writes to cells during copy
    - OPTIM: ring: use relaxed stores to release the threads
    - CLEANUP: ring: use only curr_cell and not next_cell in the main write loop
    - BUILD: ssl: fix build error on older compilers with openssl-3.2
    - BUG/MINOR: server: 'source' interface ignored from 'default-server' directive
    - BUG/MAJOR: ring: free the ring storage not the ring itself when using maps

15 months agoBUG/MAJOR: ring: free the ring storage not the ring itself when using maps
Willy Tarreau [Tue, 26 Mar 2024 14:12:19 +0000 (15:12 +0100)] 
BUG/MAJOR: ring: free the ring storage not the ring itself when using maps

A recent issue was uncovered by the CI which started to randomly report
segfaults on a few tests, and more systematically on FreeBSD. It turn
out that it was introduced by recent commit 03816ccfa9 ("MAJOR: ring:
insert an intermediary ring_storage level"), which overlooked the munmap()
path of the sink and startup logs: once the ring and its storage were
split, it was no longer correct to munmap() the ring, only its storage
area needs to be unmapped, and the ring must always be freed separately.

Thanks to Christopher and William for their help at trying to reproduce
it and figure the circumstances that triggers it.

No backport is needed.

15 months agoBUG/MINOR: server: 'source' interface ignored from 'default-server' directive
Aurelien DARRAGON [Tue, 26 Mar 2024 09:42:48 +0000 (10:42 +0100)] 
BUG/MINOR: server: 'source' interface ignored from 'default-server' directive

Sebastien Gross reported that 'interface' keyword ('source' subargument)
is silently ignored when used from 'default-server' directive despite the
documentation implicitly stating that the keyword should be supported
there.

When support for 'source' keyword was added to 'default-server' directive
in dba97077 ("MINOR: server: Make 'default-server' support 'source'
keyword."), we properly duplicated the conn iface_name from the default-
server but we forgot to copy the conn iface_len which must be set as well
since it is used as setsockopt()'s 'optlen' argument in
tcp_connect_server().

It should be backported to all stable versions.

15 months agoBUILD: ssl: fix build error on older compilers with openssl-3.2
Willy Tarreau [Mon, 25 Mar 2024 20:21:47 +0000 (21:21 +0100)] 
BUILD: ssl: fix build error on older compilers with openssl-3.2

OpenSSL 3.2 triggers the code part added by commit 25da217 ("MINOR: ssl:
Update ssl_fc_curve/ssl_bc_curve to use SSL_get0_group_name") which
contains a variable declaration in the for() statement and breaks on
older compilers, as reported in GH issues #2501.

Let's just declare it normally to fix the problem. This must be
backported wherever the commit above is (at least 2.9).

15 months agoCLEANUP: ring: use only curr_cell and not next_cell in the main write loop
Willy Tarreau [Sun, 17 Mar 2024 15:54:36 +0000 (16:54 +0100)] 
CLEANUP: ring: use only curr_cell and not next_cell in the main write loop

It turns out that we can reduce by one variable in the loop and this
clobbers one less register, making it slightly faster on Cortex A72.

15 months agoOPTIM: ring: use relaxed stores to release the threads
Willy Tarreau [Fri, 22 Mar 2024 15:47:17 +0000 (16:47 +0100)] 
OPTIM: ring: use relaxed stores to release the threads

We don't care in what order the threads are released, so we can write
their sent value using relaxed atomic stores. This brings a 3-5% perf
boost on ARM with 80 cores, reaching 7.25M/s, and doesn't change
anything on x86 since it keeps using strict ordering.

15 months agoMINOR: ring: avoid writes to cells during copy
Willy Tarreau [Fri, 15 Mar 2024 15:10:55 +0000 (16:10 +0100)] 
MINOR: ring: avoid writes to cells during copy

It has been found that performing a first pass consisting in copying
all messages, and a second one to notify about releases is more efficient
on AMD than updating all of them on the fly using a CAS, despite making
writers wait longer to be released.

Maybe it's related to the ability for the CPU to prefetch the contents
during a simple load while it wouldn't do it for an XCHG, it's unsure
at this point. This will also mater permit to use relaxed stores to
release threads.

On ARM the performance increased to 7.0M/s. If this patch is applied
before the dropping of the intermediary step, instead it drops to
3.9M/s. This shows the dependency between such changes that strive to
limit the number of writes on the fast path.

On x86_64, the EPYC at 3C6T saw a small drop from 4.57M to 4.45M, but
the 24C48T setup saw a nice 33% boost from 3.33M to 4.44M, i.e. we
get stable perf at 3 and 24 cores, despite having 8 CCX involved and
fighting with each other.

Other possibilities are:
  - use of HA_ATOMIC_XCHG() instead of FETCH_OR()
    => slightly faster (4.62/7.37 vs 4.58/7.34). Pb: requires to
       modify the readers to wait much longer since the tail value
       won't be valid in this case during updates, and it will have
       to wait by looping over it.
  - use other conditions to release a cell
    => to be tested

15 months agoMINOR: ring: it's not x86 but all non-ARMv8.1 which needs the read before OR
Willy Tarreau [Sun, 17 Mar 2024 15:55:09 +0000 (16:55 +0100)] 
MINOR: ring: it's not x86 but all non-ARMv8.1 which needs the read before OR

Archs relying on CAS benefit from a read prior to FETCH_OR, so it's
not just x86 that benefits from this. Let's just change the condition
to only exclude __ARM_FEATURE_ATOMICS which is the only one faster
without.

15 months agoCLEANUP: ring: further simplify the write loop
Willy Tarreau [Sun, 17 Mar 2024 11:19:29 +0000 (12:19 +0100)] 
CLEANUP: ring: further simplify the write loop

The loop was cleaned up a little bit so that the inner loops are more
readable and that the ifdef'd parts are whole blocks and not just an
"if" condition. A few conditions were adjusted to benefit from "break"
and "continue".

15 months agoMINOR: ring: simplify the write loop a little bit
Willy Tarreau [Sun, 17 Mar 2024 11:09:30 +0000 (12:09 +0100)] 
MINOR: ring: simplify the write loop a little bit

This is mostly a cleanup in that it turns the two-level loop into a
single one, but it also simplifies the code a little bit and brings
some performance savings again, which are mostly noticeable on ARM,
but don't change anything for x86.

15 months agoMEDIUM: ring: improve speed in the queue waiting loop on x86_64
Willy Tarreau [Sun, 17 Mar 2024 09:20:56 +0000 (10:20 +0100)] 
MEDIUM: ring: improve speed in the queue waiting loop on x86_64

x86_64 doesn't have a native atomic FETCH_OR(), it's implemented using
a CAS, which will always cause a write cycle. Here we know we can just
wait as long as the lock bit is held so better loop on a load, and only
attempt the CAS on success. This requires a tiny ifdef and brings nice
benefits. This brings the performance back from 3.33M to 3.75M at 24C48T
while doing no change at 3C6T.

15 months agoMEDIUM: ring: significant boost in the loop by checking the ring queue ptr first
Willy Tarreau [Sun, 17 Mar 2024 09:20:56 +0000 (10:20 +0100)] 
MEDIUM: ring: significant boost in the loop by checking the ring queue ptr first

By doing that and placing the cpu_relax at the right places, the ARM
reaches 6.0M/s on 80 threads. On x86_64, at 3C6T the EPYC sees a small
increase from 4.45M to 4.57M but at 24C48T it sees a drop from 3.82M
to 3.33M due to the write contention hidden behind the CAS that
implements the FETCH_OR(), that we'll address next.

15 months agoMAJOR: ring: implement a waiting queue in front of the ring
Willy Tarreau [Mon, 11 Mar 2024 13:57:37 +0000 (14:57 +0100)] 
MAJOR: ring: implement a waiting queue in front of the ring

The queue-based approach consists in forcing threads to wait away from
the work area so as not to disturb the current writer, and to prepare
the work by grouping them in a queue. The last arrived takes the head
of the queue by placing its preinitialized ring cell there, becomes the
queue's leader, informs itself about the amount of previously accumulated
bytes so that when its turn comes, it immediately knows how much room is
needed to be released.

It can then take the whole queue with it, leaving an empty one for new
threads to come while it's releasing the room needed to copy everything.

By doing so we're cascading contention areas so that multiple parts can
work in parallel.

Note that we must never leave a write counter set to 0xFF at tail, and
this happens when a message cannot fit and we give up, because in this
case we're writing back tail_ofs, and only later we restore the counter.

The solution here is to make a special case when we're going to drop
the messages, and to write the readers count before restoring tail.

This already shows a tremendous performance gain on ARM (385k -> 4.8M),
thanks to the fact that now all waiting threads wait on the queue's
head instead of polluting the tail lock. On x86_64, the EPYC sees a big
boost at 24C48T (1.88M -> 3.82M) and a slowdown at 3C6T (6.0->4.45)
though this one is much less of a concern as so few threads need less
bandwidth than bigger counts.

15 months agoMINOR: ring: make the number of queues configurable
Willy Tarreau [Thu, 14 Mar 2024 07:57:02 +0000 (08:57 +0100)] 
MINOR: ring: make the number of queues configurable

Now the rings have one wait queue per group. This should limit the
contention on systems such as EPYC CPUs where the performance drops
dramatically when using more than one CCX.

Tests were run with different numbers and it was showed that value
6 outperforms all other ones at 12, 24, 48, 64 and 80 threads on an
EPYC, a Xeon and an Ampere CPU. Value 7 sometimes comes close and
anything around these values degrades quickly. The value has been
left tunable in the global section.

This commit only introduces everything needed to set up the queue count
so that it's easier to adjust it in the forthcoming patches, but it was
initially added after the series, making it harder to compare.

It was also shown that trying to group the threads in queues by their
thread groups is counter-productive and that it was more efficient to
do that by applying a modulo on the thread number. As surprising as it
seems, it does have the benefit of well balancing any number of threads.

15 months agoMINOR: ring: add the definition of a ring waiting cell
Willy Tarreau [Mon, 11 Mar 2024 13:27:42 +0000 (14:27 +0100)] 
MINOR: ring: add the definition of a ring waiting cell

This is what will be used to describe one waiting thread, its message
in the queues, and the aggregation of pending messages after it.

15 months agoMINOR: ring: keep a few frequently used pointers in the local stack
Willy Tarreau [Fri, 22 Mar 2024 13:46:12 +0000 (14:46 +0100)] 
MINOR: ring: keep a few frequently used pointers in the local stack

Code disassembly shows that ring->storage->tail and ring->queue are
accessed a lot and reloaded a lot due to aliasing. Let's just have
variables for them in the local stack. It makes the code smaller and
slightly faster.

15 months agoOPTIM: ring: have only one thread at a time wake up all readers
Willy Tarreau [Sat, 2 Mar 2024 10:09:37 +0000 (11:09 +0100)] 
OPTIM: ring: have only one thread at a time wake up all readers

It's inefficient and counter-productive that each ring writer iterates
over all readers to wake them up. Let's just have one in charge of this,
it strongly limits contention. The only thing is that since the thread
is iterating over a list, we want to be sure that if the first readers
have already completed their job, they will be woken up again. For this
we keep a counter of messages delivered after the wakeup started, and
the waking thread will check it before going back to sleep. In order to
avoid looping forever, it will also drop its waking flag soon enough to
possibly let another one take it.

There used to be a few cases of watchdogs before this on a 24-core AMD
EPYC platform on the list iteration those never appeared anymore.
The perf has dropped a bit on 3C6T on the EPYC, from 6.61 to 6.0M but
remains unchanged at 24C48T.

15 months agoOPTIM: ring: don't even try to update offset when failed to read
Willy Tarreau [Thu, 29 Feb 2024 10:57:28 +0000 (11:57 +0100)] 
OPTIM: ring: don't even try to update offset when failed to read

If there's nothing to read, it's pointless for a reader to try to update
the offset pointer, that's two atomic ops to replace a value by itself
twice. Let's just stop this.

15 months agoMAJOR: ring: drop the now unneeded lock
Willy Tarreau [Wed, 28 Feb 2024 16:06:39 +0000 (17:06 +0100)] 
MAJOR: ring: drop the now unneeded lock

It was only used to protect the list which is now an mt_list so it
doesn't provide any required protection anymore. It obviously also
used to provide strict ordering between the writer and the reader
when the writer started to update the messages, but that's now
covered by the oredered tail updates and updates to the readers
count to protect the area.

The message rate on small thread counts (up to 12) saw a boost of
roughly 5% while on large counts while for large counts it lost
about 2% due to some contention now becoming visible elsewhere.
Typical measures are 6.13M -> 6.61M at 3C6T, and 1.88 -> 1.92M at
24C48T on the EPYC.

15 months agoMINOR: ring: make sure ring_dispatch waits when facing a changing message
Willy Tarreau [Thu, 29 Feb 2024 10:55:22 +0000 (11:55 +0100)] 
MINOR: ring: make sure ring_dispatch waits when facing a changing message

The writer is using tags 0xFF instead of readers count at the front of
messages that are undergoing an update, while the tail has already been
updated. The reader needs to take care of this because it can face these
messages and mistakenly parse data that's still being written, leading
to corruption (especially if this happens while the size is changing).

Let's just stop reading when facing reserved codes, since they indicate
that the end of usable messages was reached.

15 months agoMEDIUM: ring: protect the initialization of the initial reader offset
Willy Tarreau [Wed, 28 Feb 2024 16:42:56 +0000 (17:42 +0100)] 
MEDIUM: ring: protect the initialization of the initial reader offset

Since we're going to remove the lock, there's no more way to prevent the
ring from being fed while we're attaching a client to it. We need to
freeze the buffer while looking at its head so that we can attach there
and have a trustable one. We could do it by setting the lock bit on the
tail offset but quite frankly we don't need to bother with that, attaching
a client is rare enough to permit a thread_isolate().

15 months agoMEDIUM: ring/applet: turn the wait_entry list to an mt_list instead
Willy Tarreau [Wed, 28 Feb 2024 16:04:40 +0000 (17:04 +0100)] 
MEDIUM: ring/applet: turn the wait_entry list to an mt_list instead

Rings are keeping a lock only for the list, which apparently doesn't
need anything more than an mt_list, so let's first turn it into that
before dropping the lock. There should be no visible effect.

15 months agoMINOR: ring: don't take the readers lock if there are no readers
Willy Tarreau [Wed, 28 Feb 2024 11:07:51 +0000 (12:07 +0100)] 
MINOR: ring: don't take the readers lock if there are no readers

There's no point looking for freshly attached readers if there are none,
taking this lock requires an atomic write to a shared area, something we
clearly want to avoid.

A general test with 213-byte messages on different thread counts shows
how the performance degrades across CCX and how this patch improves the
situation:
                   Before          After
    3C6T/1CCX:    6.39 Mmsg/s     6.35 Mmsg/s
   6C12T/2CCX:    2.90 Mmsg/s     3.16 Mmsg/s
  12C24T/4CCX:    2.14 Mmsg/s     2.33 Mmsg/s
  24C48T/8CCX:    1.75 Mmsg/s     1.92 Mmsg/s

This tends to confirm that the queues will really be needed and that
they'll have to be per-ccx hence per thread-group. They will amortize
the number of updates on head & tail (one per multiple messages).

15 months agoMEDIUM: ring: unlock the ring's tail earlier
Willy Tarreau [Wed, 28 Feb 2024 08:57:00 +0000 (09:57 +0100)] 
MEDIUM: ring: unlock the ring's tail earlier

We know we can continue to protect the message area so we can unlock the
tail as soon as we know its new value. Now we're seeing ~6.4M msg/s vs
5.4M previously on 3C6T of a 3rd gen EPYC, and 1.88M vs 1.54M for 24C48T
threads, which is a significant gain!

This requires to carefully write the new head counter before releasing
the writers, and to change the calculation of the work area from
tail..head to tail...new_tail while writing the message.

15 months agoMEDIUM: move the ring's lock to only protect the readers list
Willy Tarreau [Wed, 28 Feb 2024 08:52:55 +0000 (09:52 +0100)] 
MEDIUM: move the ring's lock to only protect the readers list

Now the lock is only taken around the readers list. With careful
ordering of writes to head/tail, the ring remains protected.

The perf is a bit better, though (1.54M msg/s vs 1.4M at 48T on
a 3rd gen EPYC, and 5.4M vs 5.3M for a 3C6T setup).

15 months agoMEDIUM: ring: use the topmost bit of the tail as a lock
Willy Tarreau [Wed, 28 Feb 2024 08:37:47 +0000 (09:37 +0100)] 
MEDIUM: ring: use the topmost bit of the tail as a lock

We're now locking the tail while looking for some room in the ring. In
fact it's still while writing to it, but the goal definitely is to get
rid of the lock ASAP. For this we reserve the topmost bit of the tail
as a lock, which may have as a possible visible effect that buffers will
be limited to 2GB instead of 4GB on 32-bit machines (though in practise,
good luck for allocating more than 2GB contiguous on 32-bit), but in
practice since the size is read with atol() and some operating systems
limit it to LONG_MAX unless passing negative numbers, the limit is
already there.

For now the impact on x86_64 is significant (drop from 2.35 to 1.4M/s
on 48 threads on EPYC 24 cores) but this situation is only temporary
so that changes can be reviewable and bisectable.

Other approaches were attempted, such as using XCHG instead, which is
slightly faster on x86 with low thread counts (but causes more write
contention), and forces readers to stall under heavy traffic because
they can't access a valid value for the queue anymore. A CAS requires
preloading the value and is les good on ARMv8.1. XADD could also be
considered with 12-13 upper bits of the offset dedicated to locking,
but that looks overkill.

15 months agoMEDIUM: ring: protect the reader's positions against writers
Willy Tarreau [Wed, 28 Feb 2024 16:18:34 +0000 (17:18 +0100)] 
MEDIUM: ring: protect the reader's positions against writers

The reader now needs to protect the positions it's reading. This is
already done via the readers counter at the beginning of messages,
but as long as the lock is present, this counter is decremented
before starting to parse messages, and incremented at the end.

We must now do that in reverse, first protect the end of the messages,
and only then remove ourselves from the already processed messages, so
that at no point could a writer pass over and possibly overwrite data
we're currently watching.

15 months agoMEDIUM: ring: lock the tail's readers counters before proceeding with the changes
Willy Tarreau [Wed, 28 Feb 2024 08:20:54 +0000 (09:20 +0100)] 
MEDIUM: ring: lock the tail's readers counters before proceeding with the changes

The goal here is to start to protect the writing area inside the area
itself so that we'll later be able to release the ring's lock. We're not
there yet, but at least the tail is marked as protected for as long as the
message is not fully written.

15 months agoMINOR: ring: make the reader check the readers count before inc/dec
Willy Tarreau [Wed, 28 Feb 2024 08:03:46 +0000 (09:03 +0100)] 
MINOR: ring: make the reader check the readers count before inc/dec

We'll want to reserve some special values for the readers count to
temporary lock the following message, but for this it will be mandatory
that readers check for them before incrementing/decrementing the counter.
Let'sdo that using a CAS. The readers performance is not as critical as
the writer's anyway so the slight overhead is not a problem.

15 months agoMEDIUM: ring: align the head and tail fields in the ring_storage structure
Willy Tarreau [Wed, 28 Feb 2024 11:04:22 +0000 (12:04 +0100)] 
MEDIUM: ring: align the head and tail fields in the ring_storage structure

We really want to let the readers and writers act on different areas, so
we want to have the tail and the head on separate cache lines, themselves
separate from the rest of the ring. Doing so improves the performance from
2.15 to 2.35M msg/s at 48 threads on a 24-core EPYC.

This increases the header space from 32 to 192 bytes when threads are
enabled. But since we already have the header size available in the file,
haring remains able to detect the aligned vs unaligned formats and call
dump_v2a() when aligned is detected.

15 months agoMEDIUM: ring: remove the struct buffer from the ring
Willy Tarreau [Tue, 27 Feb 2024 08:17:45 +0000 (09:17 +0100)] 
MEDIUM: ring: remove the struct buffer from the ring

The purpose is to store a head and a tail that are independent so that
we can further improve the API to update them independently from each
other.

The struct was arranged like the original one so that as long as a ring
has its head set to zero (i.e. no recycling) it will continue to work.
The new format is already detectable thanks to the "rsvd" field which
indicates the number of reserved bytes at the beginning. It's located
where the buffer's area pointer previously was, so that older versions
of haring can continue to open the ring in repair mode, and newer ones
can use the fact that the upper bits of that variable are zero to guess
that it's working with the new format instead of the old one. Also let's
keep in mind that the layout will further change to place some alignment
constraints.

The haring tool will thus updated based on this and it detects that the
rsvd field is smaller than a page and that the sum of it with the size
equals the mapped size, in which case it uses the new dump_v2() function
instead of dump_v1(). The new function also creates a buffer from the
ring's area, size, head and tail and calls the generic one so that no
other code had to be adapted.

15 months agoMEDIUM: ring: change the ring reader to use the new vector-based API now
Willy Tarreau [Tue, 27 Feb 2024 06:58:26 +0000 (07:58 +0100)] 
MEDIUM: ring: change the ring reader to use the new vector-based API now

The code now looks cleaner and more easily shows what still needs to be
addressed. There are not that many changes in practice, these are mostly
mechanical, essentially hiding the buffer from the callers.

15 months agoMEDIUM: ring: replace the buffer API in ring_write() with the vec<->ring API
Willy Tarreau [Mon, 26 Feb 2024 10:03:03 +0000 (11:03 +0100)] 
MEDIUM: ring: replace the buffer API in ring_write() with the vec<->ring API

This is the start of the replacement of the buffer API calls. Only the
ring_write() function was touched. Instead of manipulating a buffer all
along, we now extract the ring buffer's head and tail upon entry, store
them locally and use them using the vec<->ring API until the last moment
where we can update the buffer with the new values. One subtle point is
that we must never fill the buffer past the last byte otherwise the
vec-to-ring conversion gets lost and there's no more possibility to know
where's the beginning nor the end (just like when dealing with head+tail
in fact), because it then becomes impossible to distinguish between an
empty and a full buffer.

15 months agoMINOR: ring: allow to reduce a ring size
Willy Tarreau [Thu, 14 Mar 2024 05:48:41 +0000 (06:48 +0100)] 
MINOR: ring: allow to reduce a ring size

In ring_resize() we used to check if the new ring was at least as large
as the previous one before resizing it, but what counts is that it's as
large as the previous one's contents. Initially it was thought this
would not really matter, but given that rings are initially created as
BUFSIZE, it's currently not possible to shrink them for debugging
purposes. Now with this change it is.

15 months agoMINOR: ring: resize only under thread isolation
Willy Tarreau [Wed, 28 Feb 2024 08:45:54 +0000 (09:45 +0100)] 
MINOR: ring: resize only under thread isolation

The ring resizing was already quite tricky, but when facing atomic
writes it will no longer be possible and we definitely do not want to
have to deal with a lock there. Since it's only done at boot time, and
possibly later from the CLI, let's just do it under thread isolation.

15 months agoMAJOR: ring: insert an intermediary ring_storage level
Willy Tarreau [Sun, 3 Mar 2024 16:20:10 +0000 (17:20 +0100)] 
MAJOR: ring: insert an intermediary ring_storage level

We'll need to add more complex structures in the ring, such as wait
queues. That's far too much to be stored into the area in case of
file-backed contents, so let's split the ring definition and its
storage once for all.

This patch introduces a struct ring_storage which is assigned to
ring->storage, which contains minimal information to represent the
storage layout, i.e. for now only the buffer, and all the rest
remains in the ring itself. The storage is appended immediately after
it and the buffer's pointer always points to that area. It has the
benefit of remaining 100% compatible with the existing file-backed
layout. In memory, the allocation loses the size of a struct buffer.

It's not even certain it's worth placing the size there, given that it's
constant and that a dump of a ring wouldn't really need it (the file size
is sufficient). But for now everything comes with the struct buffer, and
later this will change once split into head and tail. Also this area may
be completed with more information in the future (e.g. storage version,
format, endianness, word size etc).

15 months agoMINOR: ring: add a flag to indicate a mapped file
Willy Tarreau [Sun, 3 Mar 2024 16:50:11 +0000 (17:50 +0100)] 
MINOR: ring: add a flag to indicate a mapped file

Till now we used to rely on a heuristic pointer comparison to check if
a ring was mapped or allocated. Better assign a flag to clarify this
because it's going to become difficult otherwise.

15 months agoMINOR: ring: use ring_size(), ring_area(), ring_head() and ring_tail()
Willy Tarreau [Wed, 6 Mar 2024 15:50:40 +0000 (16:50 +0100)] 
MINOR: ring: use ring_size(), ring_area(), ring_head() and ring_tail()

Some open-coded constructs were updated to make use of the ring accessors
instead. This allows to remove some direct dependencies on the buffers
API a bit more.

15 months agoMINOR: errors: use ring_dup() to duplicate the startup_logs
Willy Tarreau [Tue, 27 Feb 2024 18:52:50 +0000 (19:52 +0100)] 
MINOR: errors: use ring_dup() to duplicate the startup_logs

In startup_logs_dup() we currently need to reference the ring's buffer,
better not do this as it will complicate operations when switching to
other types.