haproxy/include/haproxy/queue.h

135 lines
4.4 KiB
C
Raw Normal View History

/*
* include/haproxy/queue.h
* This file defines everything related to queues.
*
* Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_QUEUE_H
#define _HAPROXY_QUEUE_H
#include <haproxy/api.h>
#include <haproxy/backend.h>
#include <haproxy/pool.h>
#include <haproxy/proxy-t.h>
#include <haproxy/queue-t.h>
#include <haproxy/server-t.h>
#include <haproxy/stream-t.h>
extern struct pool_head *pool_head_pendconn;
REORG/MAJOR: session: rename the "session" entity to "stream" With HTTP/2, we'll have to support multiplexed streams. A stream is in fact the largest part of what we currently call a session, it has buffers, logs, etc. In order to catch any error, this commit removes any reference to the struct session and tries to rename most "session" occurrences in function names to "stream" and "sess" to "strm" when that's related to a session. The files stream.{c,h} were added and session.{c,h} removed. The session will be reintroduced later and a few parts of the stream will progressively be moved overthere. It will more or less contain only what we need in an embryonic session. Sample fetch functions and converters will have to change a bit so that they'll use an L5 (session) instead of what's currently called "L4" which is in fact L6 for now. Once all changes are completed, we should see approximately this : L7 - http_txn L6 - stream L5 - session L4 - connection | applet There will be at most one http_txn per stream, and a same session will possibly be referenced by multiple streams. A connection will point to a session and to a stream. The session will hold all the information we need to keep even when we don't yet have a stream. Some more cleanup is needed because some code was already far from being clean. The server queue management still refers to sessions at many places while comments talk about connections. This will have to be cleaned up once we have a server-side connection pool manager. Stream flags "SN_*" still need to be renamed, it doesn't seem like any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
struct pendconn *pendconn_add(struct stream *strm);
BUG/MAJOR: threads/queue: Fix thread-safety issues on the queues management The management of the servers and the proxies queues was not thread-safe at all. First, the accesses to <strm>->pend_pos were not protected. So it was possible to release it on a thread (for instance because the stream is released) and to use it in same time on another one (because we redispatch pending connections for a server). Then, the accesses to stream's information (flags and target) from anywhere is forbidden. To be safe, The stream's state must always be updated in the context of process_stream. So to fix these issues, the queue module has been refactored. A lock has been added in the pendconn structure. And now, when we try to dequeue a pending connection, we start by unlinking it from the server/proxy queue and we wake up the stream. Then, it is the stream reponsibility to really dequeue it (or release it). This way, we are sure that only the stream can create and release its <pend_pos> field. However, be careful. This new implementation should be thread-safe (hopefully...). But it is not optimal and in some situations, it could be really slower in multi-threaded mode than in single-threaded one. The problem is that, when we try to dequeue pending connections, we process it from the older one to the newer one independently to the thread's affinity. So we need to wait the other threads' wakeup to really process them. If threads are blocked in the poller, this will add a significant latency. This problem happens when maxconn values are very low. This patch must be backported in 1.8.
2018-03-14 11:18:06 -04:00
int pendconn_dequeue(struct stream *strm);
void process_srv_queue(struct server *s);
unsigned int srv_dynamic_maxconn(const struct server *s);
int pendconn_redistribute(struct server *s);
void pendconn_unlink(struct pendconn *p);
int pendconn_must_try_again(struct pendconn *p);
/* Removes the pendconn from the server/proxy queue. It supports being called
* with NULL for pendconn and with a pendconn not in the list. It is the
* function to be used by default when unsure. Do not call it with server
Revert "OPTIM: queue: don't call pendconn_unlink() when the pendconn is not queued" This reverts commit b7ba1d901174cb1193033f7d967987ef74e89856. Actually this test had already been removed in the past by commit fac0f645d ("BUG/MEDIUM: queue: make pendconn_cond_unlink() really thread-safe"), but the condition to reproduce the bug mentioned there was not clear. Now after analysis and a certain dose of code cleanup, things start to appear more obvious. what happens is that if we check the presence of the node in the tree without taking the lock, we can see the NULL at the instant the node is being unlinked by another thread in pendconn_process_next_strm() as part of __pendconn_unlink_prx() or __pendconn_unlink_srv(). Till now there is no issue except that the pendconn is not removed from the queue during this operation and that the task is scheduled to be woken up by pendconn_process_next_strm() with the stream being added to the list of the server's active connections by __stream_add_srv_conn(). The first thread finishes faster and gets back to stream_free() faster than the second one sets the srv_conn on the stream, so stream_free() skips the s->srv_conn test and doesn't try to dequeue the freshly queued entry. At the very least a barrier would be needed there but we can't afford to free the stream while it's being queued. So there's no other solution than making sure that either __pendconn_unlink_prx() or pendconn_cond_unlink() get the entry but never both, which is why the lock is required around the test. A possible solution would be to set p->target before unlinking the entry and using it to complete the test. This would leave no dead period where the pendconn is not seen as attached. It is possible, yet extremely difficult, to reproduce this bug, which was first noticed in bug #880. Running 100 servers with maxconn 1 and maxqueue 1 on leastconn and a connect timeout of 30ms under 16 threads with DEBUG_UAF, with a traffic making the backend's queue oscillate around zero (typically using 250 connections with a local httpterm server) may rarely manage to trigger a use-after-free. No backport is needed.
2020-10-23 02:57:33 -04:00
* or proxy locks held however. Warning: this is called from stream_free()
* which may run concurrently with pendconn_process_next_strm() which can be
* dequeuing the entry. The function must not return until the pendconn is
Revert "OPTIM: queue: don't call pendconn_unlink() when the pendconn is not queued" This reverts commit b7ba1d901174cb1193033f7d967987ef74e89856. Actually this test had already been removed in the past by commit fac0f645d ("BUG/MEDIUM: queue: make pendconn_cond_unlink() really thread-safe"), but the condition to reproduce the bug mentioned there was not clear. Now after analysis and a certain dose of code cleanup, things start to appear more obvious. what happens is that if we check the presence of the node in the tree without taking the lock, we can see the NULL at the instant the node is being unlinked by another thread in pendconn_process_next_strm() as part of __pendconn_unlink_prx() or __pendconn_unlink_srv(). Till now there is no issue except that the pendconn is not removed from the queue during this operation and that the task is scheduled to be woken up by pendconn_process_next_strm() with the stream being added to the list of the server's active connections by __stream_add_srv_conn(). The first thread finishes faster and gets back to stream_free() faster than the second one sets the srv_conn on the stream, so stream_free() skips the s->srv_conn test and doesn't try to dequeue the freshly queued entry. At the very least a barrier would be needed there but we can't afford to free the stream while it's being queued. So there's no other solution than making sure that either __pendconn_unlink_prx() or pendconn_cond_unlink() get the entry but never both, which is why the lock is required around the test. A possible solution would be to set p->target before unlinking the entry and using it to complete the test. This would leave no dead period where the pendconn is not seen as attached. It is possible, yet extremely difficult, to reproduce this bug, which was first noticed in bug #880. Running 100 servers with maxconn 1 and maxqueue 1 on leastconn and a connect timeout of 30ms under 16 threads with DEBUG_UAF, with a traffic making the backend's queue oscillate around zero (typically using 250 connections with a local httpterm server) may rarely manage to trigger a use-after-free. No backport is needed.
2020-10-23 02:57:33 -04:00
* guaranteed not to be known, which means that we must check its presence
* in the tree under the queue's lock so that penconn_process_next_strm()
* finishes before we return in case it would have grabbed this pendconn. See
* github bugs #880 and #908, and the commit log for this fix for more details.
*/
static inline void pendconn_cond_unlink(struct pendconn *p)
{
Revert "OPTIM: queue: don't call pendconn_unlink() when the pendconn is not queued" This reverts commit b7ba1d901174cb1193033f7d967987ef74e89856. Actually this test had already been removed in the past by commit fac0f645d ("BUG/MEDIUM: queue: make pendconn_cond_unlink() really thread-safe"), but the condition to reproduce the bug mentioned there was not clear. Now after analysis and a certain dose of code cleanup, things start to appear more obvious. what happens is that if we check the presence of the node in the tree without taking the lock, we can see the NULL at the instant the node is being unlinked by another thread in pendconn_process_next_strm() as part of __pendconn_unlink_prx() or __pendconn_unlink_srv(). Till now there is no issue except that the pendconn is not removed from the queue during this operation and that the task is scheduled to be woken up by pendconn_process_next_strm() with the stream being added to the list of the server's active connections by __stream_add_srv_conn(). The first thread finishes faster and gets back to stream_free() faster than the second one sets the srv_conn on the stream, so stream_free() skips the s->srv_conn test and doesn't try to dequeue the freshly queued entry. At the very least a barrier would be needed there but we can't afford to free the stream while it's being queued. So there's no other solution than making sure that either __pendconn_unlink_prx() or pendconn_cond_unlink() get the entry but never both, which is why the lock is required around the test. A possible solution would be to set p->target before unlinking the entry and using it to complete the test. This would leave no dead period where the pendconn is not seen as attached. It is possible, yet extremely difficult, to reproduce this bug, which was first noticed in bug #880. Running 100 servers with maxconn 1 and maxqueue 1 on leastconn and a connect timeout of 30ms under 16 threads with DEBUG_UAF, with a traffic making the backend's queue oscillate around zero (typically using 250 connections with a local httpterm server) may rarely manage to trigger a use-after-free. No backport is needed.
2020-10-23 02:57:33 -04:00
if (p)
pendconn_unlink(p);
}
/* Releases the pendconn associated to stream <s> if it has any, and decreases
* the pending count if needed. The connection might have been queued to a
* specific server as well as to the proxy. The stream also gets marked
* unqueued.
*
* This function must be called by the stream itself, so in the context of
* process_stream, without any lock held among the pendconn, the server's queue
* nor the proxy's queue.
*/
static inline void pendconn_free(struct stream *s)
{
struct pendconn *p = s->pend_pos;
if (p) {
pendconn_cond_unlink(p);
s->pend_pos = NULL;
pool_free(pool_head_pendconn, p);
}
}
/* Returns 0 if all slots are full on a server, or 1 if there are slots available. */
static inline int server_has_room(const struct server *s) {
return !s->maxconn || s->cur_sess < srv_dynamic_maxconn(s);
}
/* returns 0 if nothing has to be done for server <s> regarding queued connections,
* and non-zero otherwise. If the server is down, we only check its own queue. Suited
* for and if/else usage.
*/
static inline int may_dequeue_tasks(const struct server *s, const struct proxy *p) {
return (s && (s->queue.length || (p->queue.length && srv_currently_usable(s))) &&
(!s->maxconn || s->cur_sess < srv_dynamic_maxconn(s)));
}
static inline int queue_limit_class(int class)
{
if (class < -0x7ff)
return -0x7ff;
if (class > 0x7ff)
return 0x7ff;
return class;
}
static inline int queue_limit_offset(int offset)
{
if (offset < -0x7ffff)
return -0x7ffff;
if (offset > 0x7ffff)
return 0x7ffff;
return offset;
}
/* initialize the queue <queue> for proxy <px> and server <sv>. A server's
* always has both a valid proxy and a valid server. A proxy's queue only
* has a valid proxy and NULL for the server queue. This is how they're
* distinguished during operations.
*/
static inline void queue_init(struct queue *queue, struct proxy *px, struct server *sv)
{
queue->head = EB_ROOT;
queue->length = 0;
queue->idx = 0;
queue->px = px;
queue->sv = sv;
HA_SPIN_INIT(&queue->lock);
}
#endif /* _HAPROXY_QUEUE_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/