BUG/MEDIUM: stream: Fix a possible freeze during a forced shut on a stream
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 17 Mar 2025 13:49:45 +0000 (14:49 +0100)
committerAurelien DARRAGON <adarragon@haproxy.com>
Tue, 15 Apr 2025 18:13:13 +0000 (20:13 +0200)
When a forced shutdown is performed on a stream, it is possible to freeze it
infinitly because it is performed in an unexpected way from process_stream()
point of view, especially when the stream is waiting for a server
connection. The events sequence is a bit complex but at the end the stream
remains blocked in turn-around state and no event are trriggered to unblock
it.

By trying to fix the issue, we considered it was safer to rethink the
feature. The idea is to quickly shutdown a stream to release resources. For
instance to be able to delete a server. So, instead of scheduling a
shutdown, it is more efficient to trigger an error and detach the stream
from the server, if neecessary. The same code than the one used to deal with
connection errors in back_handle_st_cer() is used.

This patch must be slowly backported as far as 2.6.

(cherry picked from commit 51611a5b702e6dbf2e5ac56cbcef211326414282)
Signed-off-by: Aurelien DARRAGON <adarragon@haproxy.com>
(cherry picked from commit bf7dfa488dc3bf830179f13e1262386735a4be91)
Signed-off-by: Aurelien DARRAGON <adarragon@haproxy.com>

src/stream.c

index c17d5a6..f17df07 100644 (file)
@@ -2788,11 +2788,28 @@ void stream_shutdown_self(struct stream *stream, int why)
        if (stream->scb->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))
                return;
 
-       sc_schedule_shutdown(stream->scb);
-       sc_schedule_abort(stream->scb);
        stream->task->nice = 1024;
        if (!(stream->flags & SF_ERR_MASK))
                stream->flags |= why;
+
+       if (objt_server(stream->target)) {
+               if (stream->flags & SF_CURR_SESS) {
+                       stream->flags &= ~SF_CURR_SESS;
+                       _HA_ATOMIC_DEC(&__objt_server(stream->target)->cur_sess);
+               }
+
+               sess_change_server(stream, NULL);
+               if (may_dequeue_tasks(objt_server(stream->target), stream->be))
+                       process_srv_queue(objt_server(stream->target));
+       }
+
+       /* shutw is enough to stop a connecting socket */
+       stream->scb->flags |= SC_FL_ERROR | SC_FL_NOLINGER;
+       sc_shutdown(stream->scb);
+
+       stream->scb->state = SC_ST_CLO;
+       if (stream->srv_error)
+               stream->srv_error(stream, stream->scb);
 }
 
 /* dumps an error message for type <type> at ptr <ptr> related to stream <s>,