mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-10 00:57:02 +02:00
There's a rare TOCTOU case that happens from time to time with maxconn 1 and multiple threads. Between the moment we see the queue full and the moment we queue a request, it's possible that the last request on the server or proxy ended and that no other one is left to offer it its place. Given that all this code path is performance-critical and we cannot afford to increase the lock duration, better recheck for the condition after queueing. For this we need to be able to check for the condition and cleanly dequeue a request. That's what this patch provides via the new function pendconn_must_try_again(). It will catch more requests than absolutely needed though it will catch them all. It may find that around 1/1000 of requests are at risk, though testing shows that in practice, it's around 1 per million that really gets stuck (other ones benefit from timing and finishing late requests). Maybe in the future some conditions might be refined but it's harmless. What happens to such requests is that they're dequeued and their pendconn freed, so that the caller can decide to try to LB or queue them again. For now the function is not used, it's just added separately for easier tracking.
136 lines
4.4 KiB
C
136 lines
4.4 KiB
C
/*
|
|
* 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;
|
|
|
|
struct pendconn *pendconn_add(struct stream *strm);
|
|
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);
|
|
int pendconn_grab_from_px(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
|
|
* 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
|
|
* 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)
|
|
{
|
|
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:
|
|
*/
|