]> git.ipfire.org Git - thirdparty/haproxy.git/log
thirdparty/haproxy.git
2 years agoMEDIUM: proto: stop protocols under thread isolation during soft stop
Willy Tarreau [Fri, 15 Jul 2022 17:15:02 +0000 (19:15 +0200)] 
MEDIUM: proto: stop protocols under thread isolation during soft stop

protocol_stop_now() is called from do_soft_stop_now() running on any
thread that received the signal. The problem is that it will call some
listener handlers to close the FD, resulting in an fd_delete() being
called from the wrong group. That's not clean and we cannot even rely
on the thread mask to show up.

One interesting long-term approach could be to have kill queues for
FDs, and maybe we'll need them in the long run. However that doesn't
work well for listeners in this situation.

Let's simply isolate ourselves during this instant. We know we'll be
alone dealing with the close and that the FD will be instantly deleted
since not in use by any other thread. It's not the cleanest solution
but it should last long enough without causing trouble.

2 years agoMEDIUM: debug/threads: make the lock debugging take tgroups into account
Willy Tarreau [Fri, 15 Jul 2022 15:53:10 +0000 (17:53 +0200)] 
MEDIUM: debug/threads: make the lock debugging take tgroups into account

Since we have to use masks to verify owners/waiters, we have no other
option but to have them per group. This definitely inflates the size
of the locks, but this is only used for extreme debugging anyway so
that's not dramatic.

Thus as of now, all masks in the lock stats are local bit masks, derived
from ti->ltid_bit. Since at boot ltid_bit might not be set, we just take
care of this situation (since some structs are initialized under look
during boot), and use bit 0 from group 0 only.

2 years agoCLEANUP: fd: get rid of the __GET_{NEXT,PREV} macros
Willy Tarreau [Wed, 6 Jul 2022 12:43:51 +0000 (14:43 +0200)] 
CLEANUP: fd: get rid of the __GET_{NEXT,PREV} macros

They were initially made to deal with both the cache and the update list
but there's no cache anymore and keeping them for the update list adds a
lot of obfuscation that is really not desired. Let's get rid of them now.

Their purpose was simply to get a pointer to fdtab[fd].update.{,next,prev}
in order to perform atomic tests and modifications. The offset passed in
argument to the functions (fd_add_to_fd_list() and fd_rm_from_fd_list())
was the offset of the ->update field in fdtab, and as it's not used anymore
it was removed. This also removes a number of casts, though those used by
the atomic ops have to remain since only scalars are supported.

2 years agoMINOR: listener/config: make "thread" always support up to LONGBITS
Willy Tarreau [Fri, 15 Jul 2022 15:18:23 +0000 (17:18 +0200)] 
MINOR: listener/config: make "thread" always support up to LONGBITS

The difference is subtle but in one place there was MAXTHREADS and this
will not work anymore once it goes over 64.

2 years agoMEDIUM: config: remove the "process" keyword on "bind" lines
Willy Tarreau [Fri, 15 Jul 2022 15:16:01 +0000 (17:16 +0200)] 
MEDIUM: config: remove the "process" keyword on "bind" lines

It was deprecated, marked for removal in 2.7 and was already emitting a
warning, let's get rid of it. Note that we've kept the keyword detection
to suggest to use "thread" instead.

2 years agoMEDIUM: config: remove deprecated "bind-process" directives from frontends
Willy Tarreau [Fri, 15 Jul 2022 15:14:40 +0000 (17:14 +0200)] 
MEDIUM: config: remove deprecated "bind-process" directives from frontends

This was already causing a deprecation warning and was marked for removal
in 2.7, now it happens. An error message indicates this doesn't exist
anymore.

2 years agoCLEANUP: applet: remove the obsolete command context from the appctx
Willy Tarreau [Fri, 15 Jul 2022 14:26:44 +0000 (16:26 +0200)] 
CLEANUP: applet: remove the obsolete command context from the appctx

The "ctx" and "st2" parts in the appctx were marked for removal in 2.7
and were emulated using memcpy/memset etc for possible external code.
Let's remove this now.

2 years agoMINOR: cli/activity: add a thread number argument to "show activity"
Willy Tarreau [Fri, 15 Jul 2022 14:51:16 +0000 (16:51 +0200)] 
MINOR: cli/activity: add a thread number argument to "show activity"

The output of "show activity" can be so large that the output is visually
unreadable on a screen. Let's add an option to filter on the desired
column (actually the thread number), use "0" to report only the first
column (aggregated/sum/avg), and use "-1", the default, for the normal
detailed dump.

2 years agoDEBUG: cli: add a new "debug dev deadlock" expert command
Willy Tarreau [Fri, 15 Jul 2022 06:25:03 +0000 (08:25 +0200)] 
DEBUG: cli: add a new "debug dev deadlock" expert command

This command will create the requested number of tasks competing on a
lock, resulting in triggering the watchdog and crashing the process.
This will help stress the watchdog and inspect the lock debugging parts.

2 years agoMINOR: cli/streams: show a stream's tgid next to its thread ID
Willy Tarreau [Fri, 15 Jul 2022 14:18:43 +0000 (16:18 +0200)] 
MINOR: cli/streams: show a stream's tgid next to its thread ID

We now display both the global thread ID and the tgid/ltid pair so that
it's easier to match it with the FD.

2 years agoBUG/MEDIUM: debug: fix parallel thread dumps again
Willy Tarreau [Fri, 15 Jul 2022 11:14:03 +0000 (13:14 +0200)] 
BUG/MEDIUM: debug: fix parallel thread dumps again

The previous attempt to fix thread dumps in commit 672972604 ("BUG/MEDIUM:
debug: fix possible hang when multiple threads dump at once") still had
some shortcomings. Sometimes parallel dumps are jerky essentially due to
the way that threads synchronize on startup and end. In addition the risk
of waiting forever for a stopped thread exists, and panics happening in
parallel to thread dumps are not more reliable either.

This commit revisits the state transitions so that all threads may request
a dump in parallel, that all of them wait for each other in the handler,
and that one thread is responsible for counting every other and checking
that the total matches the number of active threads.

Then for stopping there's a finishing phase that all threads wait for so
that none quits this area too early. Given that we now know the number of
participants to the dump, we can let them each decrement the counter when
leaving so that another dump may only start after the last participant
has completely left.

Now many thread dumps in parallel are running fine, so do panics. No
backport is needed as this was the result of the changes for thread
groups.

2 years agoBUG/MINOR: debug: enter ha_panic() only once
Willy Tarreau [Fri, 15 Jul 2022 10:48:58 +0000 (12:48 +0200)] 
BUG/MINOR: debug: enter ha_panic() only once

Some panic dumps are mangled or truncated due to the watchdog firing at
the same time on multiple threads and calling ha_panic() simultaneously.
What may happen in this case is that the second one waits for the first
one to finish but as soon as it's done the second one resets the buffer
and dumps again, sometimes resetting the first one's dump. Also the first
one's abort() may trigger while the second one is currently dumping,
resulting in a full dump followed by a truncated one, leading to
confusion. Sometimes some lines appear in the middle of a dump as well.
It doesn't happen often and is easier to trigger by causing massive
deadlocks.

There's no reason for the process to resist to a panic, so we can safely
add a counter and no nothing on subsequent calls. Ideally we'd wait there
forever but as this may happen inside a signal handler (e.g. watchdog),
it doesn't always work, so the easiest thing to do is to return so that
the thread is interrupted as soon as possible and brought to the debug
handler to be dumped.

This should be backported, at least to 2.6 and possibly to older versions
as well.

2 years agoBUG/MINOR: thread: use the correct thread's group in ha_tkillall()
Willy Tarreau [Fri, 15 Jul 2022 06:27:56 +0000 (08:27 +0200)] 
BUG/MINOR: thread: use the correct thread's group in ha_tkillall()

In ha_tkillall(), the current thread's group was used to check for the
thread being running instead of using the target thread's group mask.
Most of the time it would not have any effect unless some groups are
uneven where it can lead to incomplete thread dumps for example.

No backport is needed, this is purely 2.7.

2 years agoBUG/MEDIUM: cli/threads: make "show threads" more robust on applets
Willy Tarreau [Fri, 15 Jul 2022 10:08:40 +0000 (12:08 +0200)] 
BUG/MEDIUM: cli/threads: make "show threads" more robust on applets

Running several concurrent "show threads" in loops might occasionally
cause a segfault when trying to retrieve the stream from appctx_sc()
which may be null while the applet is finishing. It's not easy to
reproduce, it requires 3-5 sessions in parallel for about a minute
or so. The appctx_sc must be checked before passing it to sc_strm().

This must be backported to 2.6 which also has the bug.

2 years agoBUG/MINOR: threads: produce correct global mask for tgroup > 1
Willy Tarreau [Fri, 15 Jul 2022 17:38:52 +0000 (19:38 +0200)] 
BUG/MINOR: threads: produce correct global mask for tgroup > 1

In thread_resolve_group_mask(), if a global thread number is passed
and it belongs to a group greater than 1, an incorrect shift resulted
in shifting that ID again which made it appear nowhere or in a wrong
group possibly. The bug was introduced in 2.5 with commit 627def9e5
("MINOR: threads: add a new function to resolve config groups and
masks") though the groups only starts to be usable in 2.7, so there
is no impact for this bug, hence no backport is needed.

2 years agoMINOR: h3: implement graceful shutdown with GOAWAY
Amaury Denoyelle [Mon, 28 Mar 2022 12:53:45 +0000 (14:53 +0200)] 
MINOR: h3: implement graceful shutdown with GOAWAY

Implement graceful shutdown as specified in RFC 9114. A GOAWAY frame is
generated with stream ID to indicate range of processed requests.

This process is done via the release app protocol operation. The MUX
is responsible to emit the generated GOAWAY frame after app release. A
CONNECTION_CLOSE will be emitted once there is no unacknowledged STREAM
frames.

2 years agoMINOR: h3: store control stream in h3c
Amaury Denoyelle [Wed, 13 Jul 2022 13:17:29 +0000 (15:17 +0200)] 
MINOR: h3: store control stream in h3c

Store a reference to the HTTP/3 control stream in h3c context.

This will be useful to implement GOAWAY emission without having to store
the control stream ID on opening.

2 years agoMINOR: mux-quic: send one last time before release
Amaury Denoyelle [Mon, 13 Jun 2022 15:09:01 +0000 (17:09 +0200)] 
MINOR: mux-quic: send one last time before release

Call qc_send() on qc_release(). This is mostly useful for application
protocol with a connection closing procedure. Most notably, this will be
useful to implement HTTP/3 GOAWAY emission.

2 years agoCLEANUP: mux-quic: move qc_release()
Amaury Denoyelle [Fri, 15 Jul 2022 08:32:53 +0000 (10:32 +0200)] 
CLEANUP: mux-quic: move qc_release()

This change is purely cosmetic. qc_release() function is moved just
before qc_io_cb(). It's cleaner as it brings it closer where it is used.
More importantly, this will be required to be able to use it in
qc_send() function.

2 years agoMEDIUM: quic: send CONNECTION_CLOSE on released MUX
Amaury Denoyelle [Wed, 13 Jul 2022 13:18:16 +0000 (15:18 +0200)] 
MEDIUM: quic: send CONNECTION_CLOSE on released MUX

Send a CONNECTION_CLOSE if the MUX has been released and all STREAM data
are acknowledged. This is useful to prevent a client from trying to use
a connection which have the upper layer closed.

To implement this a new function qc_check_close_on_released_mux() has
been added. It is called on QUIC MUX release notification and each time
a qc_stream_desc has been released.

This commit is associated with the previous one :
  MINOR: mux-quic/h3: schedule CONNECTION_CLOSE on app release
Both patches are required to prevent the risk of browsers stuck on
webpage loading if MUX has been released. On CONNECTION_CLOSE reception,
the client will reopen a new QUIC connection.

2 years agoMINOR: mux-quic/h3: prepare CONNECTION_CLOSE on release
Amaury Denoyelle [Fri, 15 Jul 2022 08:58:25 +0000 (10:58 +0200)] 
MINOR: mux-quic/h3: prepare CONNECTION_CLOSE on release

When MUX is released, a CONNECTION_CLOSE frame should be emitted. This
will ensure that the client does not use anymore a half-dead connection.

App protocol layer is responsible to provide the error code via release
callback. For HTTP/3 NO_ERROR is used as specified in RFC 9114. If no
release callback is provided, generic QUIC NO_ERROR code is used. Note
that a graceful shutdown is used : quic_conn must emit CONNECTION_CLOSE
frame when possible. This will be provided in another patch.

This change should limit the risk of browsers stuck on webpage loading
if MUX has been released. On CONNECTION_CLOSE reception, the client will
reopen a new QUIC connection.

2 years agoMINOR: mux-quic: support app graceful shutdown
Amaury Denoyelle [Wed, 13 Jul 2022 13:15:58 +0000 (15:15 +0200)] 
MINOR: mux-quic: support app graceful shutdown

Adjust qcc_emit_cc_app() to allow the delay of emission of a
CONNECTION_CLOSE. This will only set the error code but the quic-conn
layer is not flagged for immediate close. The quic-conn will be
responsible to shut the connection when deemed suitable.

This change will allow to implement application graceful shutdown, such
as HTTP/3 with GOAWAY emission. This will allow to emit closing frames
on MUX release. Once all work is done at the lower layer, the quic-conn
should emit a CONNECTION_CLOSE with the registered error code.

2 years agoMINOR: quic: define a generic QUIC error type
Amaury Denoyelle [Wed, 13 Jul 2022 13:07:56 +0000 (15:07 +0200)] 
MINOR: quic: define a generic QUIC error type

Define a new structure quic_err to abstract a QUIC error type. This
allows to easily differentiate a transport and an application error
code. This simplifies error transmission from QUIC MUX and H3 layers.

This new type is defined in quic_frame module. It is used to replace
<err_code> field in <quic_conn>. QUIC_FL_CONN_APP_ALERT flag is removed
as it is now useless.

Utility functions are defined to be able to quickly instantiate
transport, tls and application errors.

2 years agoCLEANUP: quic: clean up include on quic_frame-t.h
Amaury Denoyelle [Wed, 13 Jul 2022 12:49:39 +0000 (14:49 +0200)] 
CLEANUP: quic: clean up include on quic_frame-t.h

quic_frame-t.h and xprt_quic-t.h include themselves mutually. This may
cause some troubles later.

In fact, xprt_quic does not need to include quic_frame so remove this.
And as quic_frame is a generic source file which is included in multiple
places, it is useful to also remove the xprt_quic include in it. Use
forward declaration for this.

3 years agoBUG/MINOR: quic: fix closing state on NO_ERROR code sent
Amaury Denoyelle [Wed, 13 Jul 2022 13:08:23 +0000 (15:08 +0200)] 
BUG/MINOR: quic: fix closing state on NO_ERROR code sent

Reception is disabled as soon as a CONNECTION_CLOSE emission is
required. An early return is done on qc_lstnr_pkt_rcv() to implement
this.

This condition is not functional if the error code sent is NO_ERROR
(0x00). To fix this, check the quic-conn flags instead of the
error code.

Currently this bug has no impact has NO_ERROR emission is not used.

This can be backported up to 2.6.

3 years agoBUG/MEDIUM: debug: fix possible hang when multiple threads dump at once
Willy Tarreau [Wed, 13 Jul 2022 06:59:39 +0000 (08:59 +0200)] 
BUG/MEDIUM: debug: fix possible hang when multiple threads dump at once

A bug in the thread dumper was introduced by commit 00c27b50c ("MEDIUM:
debug: make the thread dumper not rely on a thread mask anymore"). If
two or more threads try to trigger a thread dump exactly at the same
time, the second one may loop indefinitely trying to set the value to 1
while the other ones will wait for it to finish dumping before leaving.
This is a consequence of a logic change using thread numbers instead of
a thread mask, as threads do not need to see all other ones there anymore.

No backport is needed, this is only for 2.7.

3 years agoMEDIUM: mux-quic: implement STOP_SENDING handling
Amaury Denoyelle [Mon, 4 Jul 2022 09:44:53 +0000 (11:44 +0200)] 
MEDIUM: mux-quic: implement STOP_SENDING handling

Implement support for STOP_SENDING frame parsing. The stream is resetted
as specified by RFC 9000. This will automatically interrupt all future
send operation in qc_send(). A RESET_STREAM will be sent with the code
extracted from the original STOP_SENDING frame.

3 years agoMEDIUM: mux-quic: implement RESET_STREAM emission
Amaury Denoyelle [Mon, 4 Jul 2022 09:44:38 +0000 (11:44 +0200)] 
MEDIUM: mux-quic: implement RESET_STREAM emission

Implement functions to be able to reset a stream via RESET_STREAM.

If needed, a qcs instance is flagged with QC_SF_TO_RESET to schedule a
stream reset. This will interrupt all future send operations.

On stream emission, if a stream is flagged with QC_SF_TO_RESET, a
RESET_STREAM frame is generated and emitted to the transport layer. If
this operation succeeds, the stream is locally closed. If upper layer is
instantiated, error flag is set on it.

3 years agoMINOR: mux-quic: use stream states to mark as detached
Amaury Denoyelle [Mon, 11 Jul 2022 09:23:17 +0000 (11:23 +0200)] 
MINOR: mux-quic: use stream states to mark as detached

Adjust condition to detach a qcs instance : if the stream is not locally
close it is not directly free. This should improve stream closing by
ensuring that either FIN or a RESET_STREAM is sent before destroying it.

3 years agoMINOR: mux-quic: define basic stream states
Amaury Denoyelle [Fri, 1 Jul 2022 14:48:42 +0000 (16:48 +0200)] 
MINOR: mux-quic: define basic stream states

Implement a basic state machine to represent stream lifecycle. By
default a stream is idle. It is marked as open when sending or receiving
the first data on a stream.

Bidirectional streams has two states to represent the closing on both
receive and send channels. This distinction does not exists for
unidirectional streams which passed automatically from open to close
state.

This patch is mostly internal and has a limited visible impact. Some
behaviors are slightly updated :
* closed streams are garbage collected at the start of io handler
* send operation is interrupted if a stream is close locally

Outside of this, there is no functional change. However, some additional
BUG_ON guards are implemented to ensure that we do not conduct invalid
operation on a stream. This should strengthen the code safety. Also,
stream states are displayed on trace which should help debugging.

3 years agoMINOR: mux-quic: support stream opening via MAX_STREAM_DATA
Amaury Denoyelle [Wed, 6 Jul 2022 13:45:20 +0000 (15:45 +0200)] 
MINOR: mux-quic: support stream opening via MAX_STREAM_DATA

MAX_STREAM_DATA can be used as the first frame of a stream. In this
case, the stream should be opened, if it respects flow-control limit. To
implement this, simply replace plain lookup in stream tree by
qcc_get_qcs() at the start of the parsing function. This automatically
takes care of opening the stream if not already done.

As specified by RFC 9000, if MAX_STREAM_DATA is receive for a
receive-only stream, a STREAM_STATE_ERROR connection error is emitted.

3 years agoMINOR: mux-quic: do not ack STREAM frames on unrecoverable error
Amaury Denoyelle [Thu, 7 Jul 2022 13:02:32 +0000 (15:02 +0200)] 
MINOR: mux-quic: do not ack STREAM frames on unrecoverable error

Improve return path for qcc_recv() on STREAM parsing. It returns 0 on
success. On error, a non-zero value is returned which indicates to the
caller that the packet containing the frame should not be acknowledged.

When qcc_recv() generates a CONNECTION_CLOSE or RESET_STREAM, either
directly or via qcc_get_qcs(), an error is returned which ensure that no
acknowledgement is generated. This required an adjustment on
qcc_get_qcs() API which now returns a success/error code. The stream
instance is returned via a new out argument.

3 years agoMINOR: mux-quic: filter send/receive-only streams on frame parsing
Amaury Denoyelle [Wed, 6 Jul 2022 13:43:21 +0000 (15:43 +0200)] 
MINOR: mux-quic: filter send/receive-only streams on frame parsing

Extend the function qcc_get_qcs() to be able to filter send/receive-only
unidirectional streams. A connection error STREAM_STATE_ERROR is emitted
if this new filter does not match.

This will be useful when various frames handlers are converted with
qcc_get_qcs(). Depending on the frame type, it will be easy to filter on
the forbidden stream types as specified in RFC 9000.

3 years agoMINOR: mux-quic: implement qcs_alert()
Amaury Denoyelle [Wed, 6 Jul 2022 12:54:34 +0000 (14:54 +0200)] 
MINOR: mux-quic: implement qcs_alert()

Implement a simple function to notify a possible subscriber or wake up
the upper layer if a special condition happens on a stream. For the
moment, this is only used to replace identical code in
qc_wake_some_streams().

3 years agoMINOR: mux-quic: add traces on frame parsing functions
Amaury Denoyelle [Wed, 6 Jul 2022 13:44:16 +0000 (15:44 +0200)] 
MINOR: mux-quic: add traces on frame parsing functions

Add traces for parsing functions for MAX_DATA and MAX_STREAM_DATA.

3 years agoMINOR: mux-quic: rename stream purge function
Amaury Denoyelle [Fri, 8 Jul 2022 12:04:21 +0000 (14:04 +0200)] 
MINOR: mux-quic: rename stream purge function

Rename qc_release_detached_streams() to qc_purge_streams(). The aim is
to have a more generic name. It's expected to complete this function to
add other criteria to purge dead streams.

Also the function documentation has been corrected. It does not return a
number of streams. Instead it is a boolean value, to true if at least
one stream was released.

3 years agoREORG: mux-quic: rename stream initialization function
Amaury Denoyelle [Fri, 8 Jul 2022 09:53:22 +0000 (11:53 +0200)] 
REORG: mux-quic: rename stream initialization function

Rename both qcc_open_stream_local/remote() functions to
qcc_init_stream_local/remote(). This change is purely cosmetic. It will
reduces the ambiguity with the soon to be implemented OPEN states for
QCS instances.

3 years agoBUG/MEDIUM: mux-quic: fix server chunked encoding response
Amaury Denoyelle [Fri, 8 Jul 2022 15:19:40 +0000 (17:19 +0200)] 
BUG/MEDIUM: mux-quic: fix server chunked encoding response

QUIC MUX was not able to correctly deal with server response using
chunked transfer-encoding. All data will be transfered correctly to the
client but the FIN bit is missing. The transfer will never stop as the
client will wait indefinitely for the FIN bit.

This bug happened because the HTX message representing a chunked encoded
payload contains a final empty block with the EOM flag. However,
emission is skipped by QUIC MUX if there is no data to transfer. To fix
this, the condition was completed to ensure that there is no need to
send the FIN signal. If this is false, data emission will proceed even
if there is no data : this will generate an empty QUIC STREAM frame with
FIN set which will mark the end of the transfer.

To ensure that a FIN STREAM frame is sent only one time,
QC_SF_FIN_STREAM is resetted on send confirmation from the transport in
qcc_streams_sent_done().

This bug was reproduced when dealing with chunked transfer-encoding
response for the HTTP server.

This must be backported up to 2.6.

3 years agoBUILD: http: silence an uninitialized warning affecting gcc-5
Willy Tarreau [Sun, 10 Jul 2022 11:13:52 +0000 (13:13 +0200)] 
BUILD: http: silence an uninitialized warning affecting gcc-5

When building with gcc-5, one can see this warning:

  src/http_fetch.c: In function 'smp_fetch_meth':
  src/http_fetch.c:356:6: warning: 'htx' may be used uninitialized in this function [-Wmaybe-uninitialized]
     sl = http_get_stline(htx);
        ^

It's wrong since the only way to reach this code is to have met the
same condition a few lines before and initialized the htx variable.
The reason in fact is that the same test happens on different variables
of distinct types, so the compiler possibly doesn't know that the
condition is the same. Newer gcc versions do not have this problem.
Let's just move the assignment earlier and have the exact same test,
as it's sufficient to shut this up. This may have to be backported
to 2.6 since the code is the same there.

3 years agoBUILD: debug: silence warning on gcc-5
Willy Tarreau [Sun, 10 Jul 2022 11:09:54 +0000 (13:09 +0200)] 
BUILD: debug: silence warning on gcc-5

In 2.6, 8a0fd3a36 ("BUILD: debug: work around gcc-12 excessive
-Warray-bounds warnings") disabled some warnings that were reported
around the the BUG() statement. But the -Wnull-dereference warning
isn't known from gcc-5, it only arrived in gcc-6, hence makes gcc-5
complain loudly that it doesn't know this directive. Let's just
condition this one to gcc-6.

3 years agoMEDIUM: epoll: don't synchronously delete migrated FDs
Willy Tarreau [Sat, 9 Jul 2022 16:36:49 +0000 (18:36 +0200)] 
MEDIUM: epoll: don't synchronously delete migrated FDs

Between 1.8 and 1.9 commit d9e7e36c6 ("BUG/MEDIUM: epoll/threads: use one
epoll_fd per thread") split the epoll poller to use one poller per thread
(and this was backported to 1.8). This patch added a call to epoll_ctl(DEL)
on return from the I/O handler as a safe way to deal with a detected thread
migration when that code was still quite fragile. One aspect of this choice
was that by then we wanted to maintain support for the rare old bogus epoll
implementations that failed to remove events on close(), so risking to lose
the event was not an option.

Later in 2.5, commit 200bd50b7 ("MEDIUM: fd: rely more on fd_update_events()
to detect changes") changed the code to perform most of the operations
inside fd_update_events(), but it maintained that oddity, to the point that
strictly all pollers except epoll now just add an update to be dealt with at
the next round.

This approach is much more efficient, because under load and server-side
connection reuse, it's perfectly possible for a thread to see the same FD
several times in a poll loop, the first time to relinquish it after a
migration, then the other thread makes a request, gets its response, and
still during the same loop for the first one, grabbing an idle connection
to send a request and wait for a response will program a new update on
this FD. By using a synchronous epoll_ctl(DEL), we effectively lose the
opportunity to aggregate certain changes in the same update.

Some tests performed locally with 8 threads and one server show that on
average, by using an update instead of a synchronous call, we reduce the
number of epoll_ctl() calls by 25-30% (under low loads it will probably
not change anything).

So this patch implements the same method for all pollers and replaces the
synchronous epoll_ctl() with an update.

3 years agoBUG/MEDIUM: mux-h1: Handle connection error after a synchronous send
Christopher Faulet [Fri, 8 Jul 2022 13:20:31 +0000 (15:20 +0200)] 
BUG/MEDIUM: mux-h1: Handle connection error after a synchronous send

Since commit d1480cc8 ("BUG/MEDIUM: stream-int: do not rely on the
connection error once established"), connection errors are not handled
anymore by the stream-connector once established. But it is a problem for
the H1 mux when an error occurred during a synchronous send in
h1_snd_buf(). Because in this case, the connction error is just missed. It
leads to a session leak until a timeout is reached (client or server).

To fix the bug, the connection flags are now checked in h1_snd_buf(). If
there is an error, it is reported to the stconn level by setting SF_FL_ERROR
flags. But only if there is no pending data in the input buffer.

This patch should solve the issue #1774. It must be backported as far as
2.2.

3 years agoBUG/MEDIUM: http-ana: Don't wait to have an empty buf to switch in TUNNEL state
Christopher Faulet [Fri, 8 Jul 2022 07:22:17 +0000 (09:22 +0200)] 
BUG/MEDIUM: http-ana: Don't wait to have an empty buf to switch in TUNNEL state

When we want to establish a tunnel on a side, we wait to have flush all data
from the buffer. At this stage the other side is at least in DONE state. But
there is no real reason to wait. We are already in DONE state on its
side. So all the HTTP message was already forwarded or planned to be
forwarded. Depending on the scheduling if the mux already started to
transfer tunneled data, these data may block the switch in TUNNEL state and
thus block these data infinitly.

This bug exists since the early days of HTX. May it was mandatory but today
it seems useless. But I honestly don't remember why this prerequisite was
added. So be careful during the backports.

This patch should be backported with caution. At least as far as 2.4. For
2.2 and 2.0, it seems to be mandatory too. But a review must be performed.

3 years agoBUG/MINOR: mux-h1: Be sure to commit htx changes in the demux buffer
Christopher Faulet [Fri, 8 Jul 2022 07:02:59 +0000 (09:02 +0200)] 
BUG/MINOR: mux-h1: Be sure to commit htx changes in the demux buffer

When a buffer area is casted to an htx message, depending on the method
used, the underlying buffer may be updated or not. The htxbuf() function
does not change the buffer state. We assume the buffer was already prepared
to store an htx message. htx_from_buf() on its side, updates the
buffer. With the first function, we only need to commit changes to the
underlying buffer if the htx message is changed.  With last one, we must
always commit the changes. The idea is to be sure to keep non-empty HTX
messages while an empty message must be lead to an empty buffer after
commit.

All that said because in h1_process_demux(), the changes is not always
committed as expected. When the demux is blocked, we just leave the
function. So it is possible to have an empty htx message stored in a buffer
that appears non-empty. It is not critical, but the buffer cannot be
released in this state. And we should always release an empty buffer.

This patch must be backported as far as 2.4.

3 years agoMEDIUM: mworker/systemd: send STATUS over sd_notify
William Lallemand [Thu, 7 Jul 2022 12:00:36 +0000 (14:00 +0200)] 
MEDIUM: mworker/systemd: send STATUS over sd_notify

The sd_notify API is not able to change the "Active:" line in "systemcl
status". However a message can still be displayed on a "Status: " line,
even if the service is still green and "active (running)".

When startup succeed the Status will be set to "Ready.", upon a reload
it will be set to "Reloading Configuration." If the configuration
succeed "Ready." again. However if the reload failed, it will be set to
"Reload failed!".

Keep in mind that the "Active:" line won't change upon a reload failure,
and will still be green.

3 years agoREGTEESTS: filters: Fix CONNECT request in random-forwarding script
Christopher Faulet [Thu, 7 Jul 2022 07:52:54 +0000 (09:52 +0200)] 
REGTEESTS: filters: Fix CONNECT request in random-forwarding script

An invalid CONNECT request was used and make the script failed because of a
recent fix.

3 years agoBUG/MEDIUM: http-fetch: Don't fetch the method if there is no stream
Christopher Faulet [Wed, 6 Jul 2022 15:53:02 +0000 (17:53 +0200)] 
BUG/MEDIUM: http-fetch: Don't fetch the method if there is no stream

The "method" sample fetch does not perform any check on the stream existence
before using it. However, for errors triggered at the mux level, there is no
stream. When the log message is formatted, this sample fetch must fail. It
must also fail when it is called from a health-check.

This patch must be backported as far as 2.4.

3 years agoMINOR: http-htx: Use new HTTP functions for the scheme based normalization
Christopher Faulet [Tue, 5 Jul 2022 08:24:52 +0000 (10:24 +0200)] 
MINOR: http-htx: Use new HTTP functions for the scheme based normalization

Use http_get_host_port() and http_is_default_port() functions to perform the
scheme based normalization.

3 years agoBUG/MEDIUM: h1: Improve authority validation for CONNCET request
Christopher Faulet [Tue, 5 Jul 2022 12:50:17 +0000 (14:50 +0200)] 
BUG/MEDIUM: h1: Improve authority validation for CONNCET request

From time to time, users complain to get 400-Bad-request responses for
totally valid CONNECT requests. After analysis, it is due to the H1 parser
performs an exact match between the authority and the host header value. For
non-CONNECT requests, it is valid. But for CONNECT requests the authority
must contain a port while it is often omitted from the host header value
(for default ports).

So, to be sure to not reject valid CONNECT requests, a basic authority
validation is now performed during the message parsing. In addition, the
host header value is normalized. It means the default port is removed if
possible.

This patch should solve the issue #1761. It must be backported to 2.6 and
probably as far as 2.4.

3 years agoMINOR: http: Add function to detect default port
Christopher Faulet [Tue, 5 Jul 2022 07:53:37 +0000 (09:53 +0200)] 
MINOR: http: Add function to detect default port

http_is_default_port() can be used to test if a port is a default HTTP/HTTPS
port. A scheme may be specified. In this case, it is used to detect defaults
ports, 80 for "http://" and 443 for "https://". Otherwise, with no scheme, both
are considered as default ports.

3 years agoMINOR: http: Add function to get port part of a host
Christopher Faulet [Tue, 5 Jul 2022 07:48:39 +0000 (09:48 +0200)] 
MINOR: http: Add function to get port part of a host

http_get_host_port() function can be used to get the port part of a host. It
will be used to get the port of an uri authority or a host header
value. This function only look for a port starting from the end of the
host. It is the caller responsibility to call it with a valid host value. An
indirect string is returned.

3 years agoBUG/MINOR: http-htx: Fix scheme based normalization for URIs wih userinfo
Christopher Faulet [Wed, 6 Jul 2022 15:41:31 +0000 (17:41 +0200)] 
BUG/MINOR: http-htx: Fix scheme based normalization for URIs wih userinfo

The scheme based normalization is not properly handled the URI's userinfo,
if any. First, the authority parser is not called with "no_userinfo"
parameter set. Then it is skipped from the URI normalization.

This patch must be backported as far as 2.4.

3 years agoBUG/MINOR: peers: fix possible NULL dereferences at config parsing
William Lallemand [Wed, 6 Jul 2022 12:30:23 +0000 (14:30 +0200)] 
BUG/MINOR: peers: fix possible NULL dereferences at config parsing

Patch 49f6f4b ("BUG/MEDIUM: peers: fix segfault using multiple bind on
peers sections") introduced possible NULL dereferences when parsing the
peers configuration.

Fix the issue by checking the return value of bind_conf_uniq_alloc().

This patch should be backported as far as 2.0.

3 years agoCLEANUP: thread: also remove a thread's bit from stopping_threads on stop
Willy Tarreau [Wed, 6 Jul 2022 08:17:21 +0000 (10:17 +0200)] 
CLEANUP: thread: also remove a thread's bit from stopping_threads on stop

As much as possible we should take care of not leaving bits from stopped
threads in shared thread masks. It can avoid issues like the previous
fix and will also make debugging less confusing.

3 years agoBUG/MEDIUM: thread: mask stopping_threads with threads_enabled when checking it
Willy Tarreau [Wed, 6 Jul 2022 08:13:05 +0000 (10:13 +0200)] 
BUG/MEDIUM: thread: mask stopping_threads with threads_enabled when checking it

When soft-stopping, there's a comparison between stopping_threads and
threads_enabled to make sure all threads are stopped, but this is not
correct and is racy since the threads_enabled bit is removed when a
thread is stopped but not its stopping_threads bit. The consequence is
that depending on timing, when stopping, if the first stopping thread
is fast enough to remove its bit from threads_enabled, the other threads
will see that stopping_threads doesn't match threads_enabled anymore and
will wait forever. As such the mask must be applied to stopping_threads
during the test. This issue was introduced in recent commit ef422ced9
("MEDIUM: thread: make stopping_threads per-group and add stopping_tgroups"),
no backport is needed.

3 years agoBUG/MINOR: http-act: Properly generate 103 responses when several rules are used
Christopher Faulet [Tue, 5 Jul 2022 14:24:15 +0000 (16:24 +0200)] 
BUG/MINOR: http-act: Properly generate 103 responses when several rules are used

When several "early-hint" rules are used, we try, as far as possible, to
merge links into the same 103-early-hints response. However, it only works
if there is no ACLs. If a "early-hint" rule is not executed an invalid
response is generated. the EOH block or the start-line may be missing,
depending on the rule order.

To fix the bug, we use the transaction status code. It is unused at this
stage. Thus, it is set to 103 when a 103-early-hints response is in
progress. And it is reset when the response is forwarded. In addition, the
response is forwarded if the next rule is an "early-hint" rule with an
ACL. This way, the response is always valid.

This patch must be backported as far as 2.2.

3 years agoBUG/MINOR: http-check: Preserve headers if not redefined by an implicit rule
Christopher Faulet [Tue, 5 Jul 2022 13:33:53 +0000 (15:33 +0200)] 
BUG/MINOR: http-check: Preserve headers if not redefined by an implicit rule

When an explicit "http-check send" rule is used, if it is the first one, it
is merge with the implicit rule created by "option httpchk" statement. The
opposite is also true. Idea is to have only one send rule with the merged
info. It means info defined in the second rule override those defined in the
first one. However, if an element is not defined in the second rule, it must
be ignored, keeping this way info from the first rule. It works as expected
for the method, the uri and the request version. But it is not true for the
header list.

For instance, with the following statements, a x-forwarded-proto header is
added to healthcheck requests:

  option httpchk
  http-check send meth GET hdr x-forwarded-proto https

while by inverting the statements, no extra headers are added:

  http-check send meth GET hdr x-forwarded-proto https
  option httpchk

Now the old header list is overriden if the new one is not empty.

This patch should fix the issue #1772. It must be backported as far as 2.2.

3 years agoCLEANUP: bwlim: Set pointers to NULL when memory is released
Christopher Faulet [Fri, 24 Jun 2022 12:52:18 +0000 (14:52 +0200)] 
CLEANUP: bwlim: Set pointers to NULL when memory is released

Calls to free() are replaced by ha_free(). And otherwise, the pointers are
explicitly set to NULL after a release. There is no issue here but it could
help debugging sessions.

3 years agoBUG/MEDIUM: peers/config: properly set the thread mask
Willy Tarreau [Tue, 5 Jul 2022 14:00:56 +0000 (16:00 +0200)] 
BUG/MEDIUM: peers/config: properly set the thread mask

The peers didn't have their bind_conf thread mask nor group set, because
they're still not part of the global proxy list. Till 2.6 it seems it does
not have any visible impact, since most listener-oriented operations pass
through thread_mask() which detects null masks and turns them to
all_threads_mask. But starting with 2.7 it becomes a problem as won't
permit these null masks anymore.

This patch duplicates (yes, sorry) the loop that applies to the frontend's
bind_conf, though it is simplified (no sharding, etc).

As the code is right now, it simply seems impossible to trigger the second
(and largest) part of the check when leaving thread_resolve_group_mask()
on success, so it looks like it might be removed.

No backport is needed, unless a report in 2.6 or earlier mentions an issue
with a null thread_mask.

3 years agoBUG/MINOR: peers/config: always fill the bind_conf's argument
Willy Tarreau [Tue, 5 Jul 2022 13:54:09 +0000 (15:54 +0200)] 
BUG/MINOR: peers/config: always fill the bind_conf's argument

Some generic frontend errors mention the bind_conf by its name as
"bind '%s'", but if this is used on peers "bind" lines it shows
"(null)" because the argument is set to NULL in the call to
bind_conf_uniq_alloc() instead of passing the argument. Fortunately
that's trivial to fix.

This may be backported to older versions.

3 years agoMINOR: mux-quic: emit FINAL_SIZE_ERROR on invalid STREAM size
Amaury Denoyelle [Mon, 4 Jul 2022 08:02:04 +0000 (10:02 +0200)] 
MINOR: mux-quic: emit FINAL_SIZE_ERROR on invalid STREAM size

Add a check on stream size when the stream is in state Size Known. In
this case, a STREAM frame cannot change the stream size. If this is not
respected, a CONNECTION_CLOSE with FINAL_SIZE_ERROR will be emitted as
specified in the RFC 9000.

3 years agoMINOR: mux-quic: rename qcs flag FIN_RECV to SIZE_KNOWN
Amaury Denoyelle [Fri, 1 Jul 2022 14:11:03 +0000 (16:11 +0200)] 
MINOR: mux-quic: rename qcs flag FIN_RECV to SIZE_KNOWN

Rename QC_SF_FIN_RECV to the more generic name QC_SF_SIZE_KNOWN. This
better align with the QUIC RFC 9000 which uses the "Size Known" state
definition. This change is purely cosmetic.

3 years agoMEDIUM: mux-quic: refactor streams opening
Amaury Denoyelle [Mon, 4 Jul 2022 13:50:33 +0000 (15:50 +0200)] 
MEDIUM: mux-quic: refactor streams opening

Review the whole API used to access/instantiate qcs.

A public function qcc_open_stream_local() is available to the
application protocol layer. It allows to easily opening a local stream.
The ID is automatically attributed to the next one available.

For remote streams, qcc_open_stream_remote() has been implemented. It
will automatically take care of allocating streams in a linear way
according to the ID. This function is called via qcc_get_qcs() which can
be used for each qcc_recv*() operations. For the moment, it is only used
for STREAM frames via qcc_recv(), but soon it will be implemented for
other frames types which can also be used to open a new stream.

qcs_new() and qcs_free() has been restricted to the MUX QUIC only as
they are now reserved for internal usage.

This change is a pure refactoring and should not have any noticeable
impact. It clarifies the developer intent and help to ensure that a
stream is not automatically opened when not desired.

3 years agoMINOR: mux-quic: implement accessor for sedesc
Amaury Denoyelle [Mon, 4 Jul 2022 09:42:27 +0000 (11:42 +0200)] 
MINOR: mux-quic: implement accessor for sedesc

Implement a function <qcs_sc> to easily access to the stconn associated
with a QCS. This takes care of qcs.sd which may be NULL, for example for
unidirectional streams.

It is expected that in the future when implementing
STOP_SENDING/RESET_STREAM, stconn must be notify about the event. This
accessor will allow to easily test if the stconn is instantiated or not.

3 years agoREORG: mux-quic: reorganize flow-control fields
Amaury Denoyelle [Mon, 4 Jul 2022 13:31:43 +0000 (15:31 +0200)] 
REORG: mux-quic: reorganize flow-control fields

<qcc.cl_bidi_r> is used to implement STREAM ID flow control enforcement.
Move it with all fields related to this operation and separated from MAX
STREAM DATA calcul.

3 years agoCLEANUP: mux-quic: do not export qc_get_ncbuf
Amaury Denoyelle [Mon, 4 Jul 2022 13:48:57 +0000 (15:48 +0200)] 
CLEANUP: mux-quic: do not export qc_get_ncbuf

qc_get_ncbuf() is only used internally : thus its prototype in QUIC MUX
include is not required.

3 years agoCLEANUP: mworker: rename mworker_pipe to mworker_sockpair
William Lallemand [Tue, 5 Jul 2022 07:04:03 +0000 (09:04 +0200)] 
CLEANUP: mworker: rename mworker_pipe to mworker_sockpair

The function mworker_pipe_register_per_thread() is called this way
because the master first used pipes instead of socketpairs.

Rename mworker_pipe_register_per_thread() to
mworker_sockpair_register_per_thread() in order to be more consistent.

Also update a comment inside the function.

3 years agoMINOR: fd: Add BUG_ON checks on fd_insert()
Emeric Brun [Fri, 1 Jul 2022 11:57:39 +0000 (04:57 -0700)] 
MINOR: fd: Add BUG_ON checks on fd_insert()

This patch adds two BUG_ON on fd_insert() into the fdtab checking
if the fd has been correctly re-initialized into the fdtab
before a new insert.

It will raise a BUG if we try to insert the same fd multiple times
without an intermediate fd_delete().

First one checks that the owner for this fd in fdtab was reset to NULL.

Second one checks that the state flags for this fd in fdtab was reset
to 0.

This patch could be backported on version >= 2.4

3 years agoMEDIUM: mworker: set the iocb of the socketpair without using fd_insert()
William Lallemand [Mon, 4 Jul 2022 22:55:09 +0000 (00:55 +0200)] 
MEDIUM: mworker: set the iocb of the socketpair without using fd_insert()

The worker was previously changing the iocb of the socketpair in the
worker by mworker_accept_wrapper(). However, it was done using
fd_insert() instead of changing directly the callback in the
fdtab[].iocb pointer.

This patch cleans up this by part by removing fd_insert().

It also stops setting tid_bit on the thread mask, the socketpair will be
handled by any thread from now.

3 years agoCI: re-enable gcc asan builds
Ilya Shipitsin [Sat, 2 Jul 2022 05:30:28 +0000 (10:30 +0500)] 
CI: re-enable gcc asan builds

for some unclear reasons asan builds were limited to clang only. let us
enable them for gcc as well

3 years agoBUILD: Makefile: Add Lua 5.4 autodetect
Christian Ruppert [Tue, 28 Jun 2022 08:03:00 +0000 (10:03 +0200)] 
BUILD: Makefile: Add Lua 5.4 autodetect

This patch is based on:
https://www.mail-archive.com/haproxy@formilux.org/msg39689.html
Thanks to Callum Farmer!

Signed-off-by: Christian Ruppert <idl0r@qasl.de>
3 years agoMINOR: proxy: use tg->threads_enabled in hard_stop() to detect stopped threads
Willy Tarreau [Mon, 4 Jul 2022 11:33:13 +0000 (13:33 +0200)] 
MINOR: proxy: use tg->threads_enabled in hard_stop() to detect stopped threads

Let's rely on tg->threads_enabled there to detect running threads. We
should probably have a dedicated function for this in order to simplify
the code and avoid the risk of using the wrong group ID.

3 years agoBUG/MEDIUM: thread: check stopping thread against local bit and not global one
Willy Tarreau [Mon, 4 Jul 2022 12:07:29 +0000 (14:07 +0200)] 
BUG/MEDIUM: thread: check stopping thread against local bit and not global one

Commit ef422ced9 ("MEDIUM: thread: make stopping_threads per-group and add
stopping_tgroups") moved the stopping_threads mask to per-group, but one
test in the loop preserved its global value instead, resulting in stopping
threads never sleeping on stop and eating 100% CPU until all were stopped.

No backport is needed.

3 years agoBUG/MEDIUM: threads: fix incorrect thread group being used on soft-stop
Willy Tarreau [Mon, 4 Jul 2022 11:36:16 +0000 (13:36 +0200)] 
BUG/MEDIUM: threads: fix incorrect thread group being used on soft-stop

Commit 377e37a80 ("MINOR: tinfo: add the mask of enabled threads in each
group") forgot -1 on the tgid, thus the groups was not always correctly
tested, which is visible only when running with more than one group. No
backport is needed.

3 years agoBUILD: debug: re-export thread_dump_state
Willy Tarreau [Fri, 1 Jul 2022 19:18:03 +0000 (21:18 +0200)] 
BUILD: debug: re-export thread_dump_state

Building with threads and without thread dump (e.g. macos, freebsd)
warns that thread_dump_state is unused. This happened in fact with
recentcommit 1229ef312 ("MINOR: wdt: do not rely on threads_to_dump
anymore"). The solution would be to mark it unused, but after a
second thought, it can be convenient to keep it exported to help
debug crashes, so let's export it again. It's just not referenced in
include files since it's not needed outside.

3 years agoBUILD: debug: fix build issue on clang with previous commit
Willy Tarreau [Fri, 1 Jul 2022 17:37:42 +0000 (19:37 +0200)] 
BUILD: debug: fix build issue on clang with previous commit

Since the thread_dump_state type changed to uint, the old value in the
CAS needs to be the same as well.

3 years agoMEDIUM: debug: make the thread dumper not rely on a thread mask anymore
Willy Tarreau [Fri, 1 Jul 2022 17:08:56 +0000 (19:08 +0200)] 
MEDIUM: debug: make the thread dumper not rely on a thread mask anymore

The thread mask is too short to dump more than 64 bits. Thus here we're
using a different approach with two counters, one for the next thread ID
to dump (which always exists, as it's looked up), and the second one for
the number of threads done dumping. This allows to dump threads in ascending
order then to let them wait for all others to be done, then to leave without
the risk of an overlapping dump until the done count is null again.

This allows to remove threads_to_dump which was the last non-FD variable
using a global thread mask.

3 years agoMINOR: wdt: do not rely on threads_to_dump anymore
Willy Tarreau [Fri, 1 Jul 2022 15:26:15 +0000 (17:26 +0200)] 
MINOR: wdt: do not rely on threads_to_dump anymore

This flag is not needed anymore as we're already marking the waiting
threads as harmless, thus the thread's bit is already covered by this
information. The variable was unexported.

3 years agoMINOR: debug: mark oneself harmless while waiting for threads to finish
Willy Tarreau [Fri, 1 Jul 2022 15:19:14 +0000 (17:19 +0200)] 
MINOR: debug: mark oneself harmless while waiting for threads to finish

The debug_handler() function waits for other threads to join, but does
not mark itself as harmless, so if at the same time another thread tries
to isolate, this may deadlock. In practice this does not happen as the
signal is received during epoll_wait() hence under harmless mode, but
it can possibly arrive under other conditions.

In order to improve this, while waiting for other threads to join, we're
now marking the current thread as harmless, as it's doing nothing but
waiting for the other ones. This way another harmless waiter will be able
to proceed. It's valid to do this since we're not doing anything else in
this loop.

One improvement could be to also check for the thread being idle and
marking it idle in addition to harmless, so that it can even release a
full isolation requester. But that really doesn't look worth it.

3 years agoMINOR: thread: add is_thread_harmless() to know if a thread already is harmless
Willy Tarreau [Fri, 1 Jul 2022 15:11:03 +0000 (17:11 +0200)] 
MINOR: thread: add is_thread_harmless() to know if a thread already is harmless

The harmless status is not re-entrant, so sometimes for signal handling
it can be useful to know if we're already harmless or not. Let's add a
function doing that, and make the debugger use it instead of manipulating
the harmless mask.

3 years agoMAJOR: threads: change thread_isolate to support inter-group synchronization
Willy Tarreau [Fri, 1 Jul 2022 13:08:37 +0000 (15:08 +0200)] 
MAJOR: threads: change thread_isolate to support inter-group synchronization

thread_isolate() and thread_isolate_full() were relying on a set of thread
masks for all threads in different states (rdv, harmless, idle). This cannot
work anymore when the number of threads increases beyond LONGBITS so we need
to change the mechanism.

What is done here is to have a counter of requesters and the number of the
current isolated thread. Threads which want to isolate themselves increment
the request counter and wait for all threads to be marked harmless (or idle)
by scanning all groups and watching the respective masks. This is possible
because threads cannot escape once they discover this counter, unless they
also want to isolate and possibly pass first. Once all threads are harmless,
the requesting thread tries to self-assign the isolated thread number, and
if it fails it loops back to checking all threads. If it wins it's guaranted
to be alone, and can drop its harmless bit, so that other competing threads
go back to the loop waiting for all threads to be harmless. The benefit of
proceeding this way is that there's very little write contention on the
thread number (none during work), hence no cache line moves between caches,
thus frozen threads do not slow down the isolated one.

Once it's done, the isolated thread resets the thread number (hence lets
another thread take the place) and decrements the requester count, thus
possibly releasing all harmless threads.

With this change there's no more need for any global mask to synchronize
any thread, and we only need to loop over a number of groups to check
64 threads at a time per iteration. As such, tinfo's threads_want_rdv
could be dropped.

This was tested with 64 threads spread into 2 groups, running 64 tasks
(from the debug dev command), 20 "show sess" (thread_isolate()), 20
"add server blah/blah" (thread_isolate()), and 20 "del server blah/blah"
(thread_isolate_full()). The load remained very low (limited by external
socat forks) and no stuck nor starved thread was found.

3 years agoMEDIUM: thread: make stopping_threads per-group and add stopping_tgroups
Willy Tarreau [Tue, 28 Jun 2022 17:29:29 +0000 (19:29 +0200)] 
MEDIUM: thread: make stopping_threads per-group and add stopping_tgroups

Stopping threads need a mask to figure who's still there without scanning
everything in the poll loop. This means this will have to be per-group.
And we also need to have a global stopping groups mask to know what groups
were already signaled. This is used both to figure what thread is the first
one to catch the event, and which one is the first one to detect the end of
the last job. The logic isn't changed, though a loop is required in the
slow path to make sure all threads are aware of the end.

Note that for now the soft-stop still takes time for group IDs > 1 as the
poller is not yet started on these threads and needs to expire its timeout
as there's no way to wake it up. But all threads are eventually stopped.

3 years agoMEDIUM: tinfo: add a dynamic thread-group context
Willy Tarreau [Mon, 27 Jun 2022 14:02:24 +0000 (16:02 +0200)] 
MEDIUM: tinfo: add a dynamic thread-group context

The thread group info is not sufficient to represent a thread group's
current state as it's read-only. We also need something comparable to
the thread context to represent the aggregate state of the threads in
that group. This patch introduces ha_tgroup_ctx[] and tg_ctx for this.
It's indexed on the group id and must be cache-line aligned. The thread
masks that were global and that do not need to remain global were moved
there (want_rdv, harmless, idle).

Given that all the masks placed there now become group-specific, the
associated thread mask (tid_bit) now switches to the thread's local
bit (ltid_bit). Both are the same for nbtgroups 1 but will differ for
other values.

There's also a tg_ctx pointer in the thread so that it can be reached
from other threads.

3 years agoCLEANUP: thread: remove thread_sync_release() and thread_sync_mask
Willy Tarreau [Mon, 27 Jun 2022 13:05:44 +0000 (15:05 +0200)] 
CLEANUP: thread: remove thread_sync_release() and thread_sync_mask

This function was added in 2.0 when reworking the thread isolation
mechanism to make it more reliable. However it if fundamentally
incompatible with the full isolation mechanism provided by
thread_isolate_full() since that one will wait for all threads to
become idle while the former will wait for all threads to finish
waiting, causing a deadlock.

Given that it's not used, let's just drop it entirely before it gets
used by accident.

3 years agoMINOR: thread: add a new all_tgroups_mask variable to know about active tgroups
Willy Tarreau [Fri, 24 Jun 2022 13:55:11 +0000 (15:55 +0200)] 
MINOR: thread: add a new all_tgroups_mask variable to know about active tgroups

In order to kill all_threads_mask we'll need to have an equivalent for
the thread groups. The all_tgroups_mask does just this, it keeps one bit
set per enabled group.

3 years agoMINOR: thread: use ltid_bit in ha_tkillall()
Willy Tarreau [Mon, 27 Jun 2022 14:23:44 +0000 (16:23 +0200)] 
MINOR: thread: use ltid_bit in ha_tkillall()

Since commit cc7a11ee3 ("MINOR: threads: set the tid, ltid and their bit
in thread_cfg") we ought not use (1UL << thr) to get the group mask for
thread <thr>, but (ha_thread_info[thr].ltid_bit). ha_tkillall() needs
this.

3 years agoMINOR: clock: use ltid_bit in clock_report_idle()
Willy Tarreau [Mon, 27 Jun 2022 14:22:22 +0000 (16:22 +0200)] 
MINOR: clock: use ltid_bit in clock_report_idle()

Since commit cc7a11ee3 ("MINOR: threads: set the tid, ltid and their bit
in thread_cfg") we ought not use (1UL << thr) to get the group mask for
thread <thr>, but (ha_thread_info[thr].ltid_bit). clock_report_idle()
needs this.

This also implies not using all_threads_mask anymore but taking the mask
from the tgroup since it becomes relative now.

3 years agoMINOR: wdt: use ltid_bit in wdt_handler()
Willy Tarreau [Mon, 27 Jun 2022 14:20:13 +0000 (16:20 +0200)] 
MINOR: wdt: use ltid_bit in wdt_handler()

Since commit cc7a11ee3 ("MINOR: threads: set the tid, ltid and their bit
in thread_cfg") we ought not use (1UL << thr) to get the group mask for
thread <thr>, but (ha_thread_info[thr].ltid_bit). wdt_handler() needs
this.

3 years agoMINOR: debug: use ltid_bit in ha_thread_dump()
Willy Tarreau [Mon, 27 Jun 2022 14:13:50 +0000 (16:13 +0200)] 
MINOR: debug: use ltid_bit in ha_thread_dump()

Since commit cc7a11ee3 ("MINOR: threads: set the tid, ltid and their bit
in thread_cfg") we ought not use (1UL << thr) to get the group mask for
thread <thr>, but (ha_thread_info[thr].ltid_bit). ha_thread_dump() needs
this.

3 years agoMINOR: tinfo: add the mask of enabled threads in each group
Willy Tarreau [Fri, 24 Jun 2022 13:18:49 +0000 (15:18 +0200)] 
MINOR: tinfo: add the mask of enabled threads in each group

In order to replace the global "all_threads_mask" we'll need to have an
equivalent per group. Take this opportunity for calling it threads_enabled
and make sure which ones are counted there (in case in the future we allow
to stop some).

3 years agoMINOR: tinfo: replace the tgid with tgid_bit in tgroup_info
Willy Tarreau [Tue, 28 Jun 2022 15:48:07 +0000 (17:48 +0200)] 
MINOR: tinfo: replace the tgid with tgid_bit in tgroup_info

Now that the tgid is accessible from the thread, it's pointless to have
it in the group, and it was only set but never used. However we'll soon
frequently need the mask corresponding to the group ID and the risk of
getting it wrong with the +1 or to shift 1 instead of 1UL is important,
so let's store the tgid_bit there.

3 years agoMINOR: tinfo: add the tgid to the thread_info struct
Willy Tarreau [Tue, 28 Jun 2022 08:49:57 +0000 (10:49 +0200)] 
MINOR: tinfo: add the tgid to the thread_info struct

At several places we're dereferencing the thread group just to catch
the group number, and this will become even more required once we start
to use per-group contexts. Let's just add the tgid in the thread_info
struct to make this easier.

3 years agoMEDIUM: tasks/fd: replace sleeping_thread_mask with a TH_FL_SLEEPING flag
Willy Tarreau [Mon, 20 Jun 2022 07:23:24 +0000 (09:23 +0200)] 
MEDIUM: tasks/fd: replace sleeping_thread_mask with a TH_FL_SLEEPING flag

Every single place where sleeping_thread_mask was still used was to test
or set a single thread. We can now add a per-thread flag to indicate a
thread is sleeping, and remove this shared mask.

The wake_thread() function now always performs an atomic fetch-and-or
instead of a first load then an atomic OR. That's cleaner and more
reliable.

This is not easy to test, as broadcast FD events are rare. The good
way to test for this is to run a very low rate-limited frontend with
a listener that listens to the fewest possible threads (2), and to
send it only 1 connection at a time. The listener will periodically
pause and the wakeup task will sometimes wake up on a random thread
and will call wake_thread():

   frontend test
        bind :8888 maxconn 10 thread 1-2
        rate-limit sessions 5

Alternately, disabling/enabling a frontend in loops via the CLI also
broadcasts such events, but they're more difficult to observe since
this is causing connection failures.

3 years agoMEDIUM: thread: add a new per-thread flag TH_FL_NOTIFIED to remember wakeups
Willy Tarreau [Wed, 22 Jun 2022 13:38:38 +0000 (15:38 +0200)] 
MEDIUM: thread: add a new per-thread flag TH_FL_NOTIFIED to remember wakeups

Right now when an inter-thread wakeup happens, we preliminary check if the
thread was asleep, and if so we wake the poller up and remove its bit from
the sleeping mask. That's not very clean since the sleeping mask cannot be
entirely trusted since a thread that's about to wake up will already have
its sleeping bit removed.

This patch adds a new per-thread flag (TH_FL_NOTIFIED) to remember that a
thread was notified to wake up. It's cleared before checking the task lists
last, so that new wakeups can be considered again (since wake_thread() is
only used to notify about task wakeups and FD polling changes). This way
we do not need to modify a remote thread's sleeping mask anymore. As such
wake_thread() now only tests and sets the TH_FL_NOTIFIED flag but doesn't
clear sleeping anymore.

3 years agoMINOR: poller: update_fd_polling: wake a random other thread
Willy Tarreau [Thu, 23 Jun 2022 16:31:08 +0000 (18:31 +0200)] 
MINOR: poller: update_fd_polling: wake a random other thread

When enabling an FD that's only bound to another thread, instead of
always picking the first one, let's pick a random one. This is rarely
used (enabling a frontend, or session rate-limiting period ending),
and has greater chances of avoiding that some obscure corner cases
could degenerate into a poorly distributed load.

3 years agoMEDIUM: polling: make update_fd_polling() not care about sleeping threads
Willy Tarreau [Thu, 23 Jun 2022 16:38:06 +0000 (18:38 +0200)] 
MEDIUM: polling: make update_fd_polling() not care about sleeping threads

Till now, update_fd_polling() used to check if all the target threads were
sleeping, and only then would wake an owning thread up. This causes several
problems among which the need for the sleeping_thread_mask and the fact that
by the time we wake one thread up, it has changed.

This commit changes this by leaving it to wake_thread() to perform this
check on the selected thread, since wake_thread() is already capable of
doing this now. Concretely speaking, for updt_fd_polling() it will mean
performing one computation of an ffsl() before knowing the sleeping status
on a global FD state change (which is very rare and not important here,
as it basically happens after relaxing a rate-limit (i.e. once a second
at beast) or after enabling a frontend from the CLI); thus we don't care.

3 years agoMINOR: poller: centralize poll return handling
Willy Tarreau [Wed, 22 Jun 2022 13:21:34 +0000 (15:21 +0200)] 
MINOR: poller: centralize poll return handling

When returning from the polling syscall, all pollers have a certain
dance to follow, made of wall clock updates, thread harmless updates,
idle time management and sleeping mask updates. Let's have a centralized
function to deal with all of this boring stuff: fd_leaving_poll(), and
make all the pollers use it.

3 years agoMINOR: thread: only use atomic ops to touch the flags
Willy Tarreau [Wed, 22 Jun 2022 07:19:46 +0000 (09:19 +0200)] 
MINOR: thread: only use atomic ops to touch the flags

The thread flags are touched a little bit by other threads, e.g. the STUCK
flag may be set by other ones, and they're watched a little bit. As such
we need to use atomic ops only to manipulate them. Most places were already
using them, but here we generalize the practice. Only ha_thread_dump() does
not change because it's run under isolation.

3 years agoMINOR: thread: move the flags to the shared cache line
Willy Tarreau [Wed, 22 Jun 2022 07:00:08 +0000 (09:00 +0200)] 
MINOR: thread: move the flags to the shared cache line

The thread flags were once believed to be local to the thread, but as
it stands, even the STUCK flag is shared since it's looked at by the
watchdog. As such we'll need to use atomic ops to manipulate them, and
likely to move them into the shared area.

This patch only moves the flag into the shared area so that we can later
decide whether it's best to leave them there or to move them back to the
local area. Interestingly, some tests have shown a 3% better performance
on dequeuing with this, while they're not used by other threads yet, so
there are definitely alignment effects that might change over time.

3 years agoMINOR: thread: make wake_thread() take care of the sleeping threads mask
Willy Tarreau [Mon, 20 Jun 2022 07:14:40 +0000 (09:14 +0200)] 
MINOR: thread: make wake_thread() take care of the sleeping threads mask

Almost every call place of wake_thread() checks for sleeping threads and
clears the sleeping mask itself, while the function is solely used for
there. Let's move the check and the clearing of the bit inside the function
itself. Note that updt_fd_polling() still performs the check because its
rules are a bit different.