diff --git a/include/types/proxy.h b/include/types/proxy.h index fea8a5ea3..9bda57838 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -301,6 +301,7 @@ struct proxy { struct pxcounters be_counters; /* backend statistics counters */ struct pxcounters fe_counters; /* frontend statistics counters */ + struct list listener_queue; /* list of the temporarily limited listeners because of lack of a proxy resource */ struct stktable table; /* table for storing sticking sessions */ int grace; /* grace time after stop request */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 6e38962d2..21ef3d995 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1041,6 +1041,7 @@ static void init_new_proxy(struct proxy *p) LIST_INIT(&p->tcp_req.l4_rules); LIST_INIT(&p->req_add); LIST_INIT(&p->rsp_add); + LIST_INIT(&p->listener_queue); /* Timeouts are defined as -1 */ proxy_reset_timeouts(p); diff --git a/src/proxy.c b/src/proxy.c index 2184a1bcb..25398a966 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -499,6 +499,10 @@ void maintain_proxies(int *next) goto do_block; } + /* The proxy is not limited so we can re-enable any waiting listener */ + if (!LIST_ISEMPTY(&p->listener_queue)) + dequeue_all_listeners(&p->listener_queue); + /* OK we have no reason to block, so let's unblock if we were blocking */ if (p->state == PR_STIDLE) { for (l = p->listen; l != NULL; l = l->next) diff --git a/src/session.c b/src/session.c index 750cf4023..65c92db0d 100644 --- a/src/session.c +++ b/src/session.c @@ -2096,6 +2096,9 @@ struct task *process_session(struct task *t) if (!LIST_ISEMPTY(&global_listener_queue)) dequeue_all_listeners(&global_listener_queue); + if (!LIST_ISEMPTY(&s->fe->listener_queue)) + dequeue_all_listeners(&s->fe->listener_queue); + if (unlikely((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))) { int len; diff --git a/src/stream_sock.c b/src/stream_sock.c index fa03a625b..a0b2c1114 100644 --- a/src/stream_sock.c +++ b/src/stream_sock.c @@ -1192,7 +1192,6 @@ int stream_sock_accept(int fd) int max_accept = global.tune.maxaccept; int cfd; int ret; - int loops = 0; if (unlikely(l->nbconn >= l->maxconn)) { listener_full(l); @@ -1201,15 +1200,31 @@ int stream_sock_accept(int fd) if (p && p->fe_sps_lim) { int max = freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0); + + if (unlikely(!max)) { + /* frontend accept rate limit was reached */ + limit_listener(l, &p->listener_queue); + return 0; + } + if (max_accept > max) max_accept = max; } - while ((!p || p->feconn < p->maxconn) && actconn < global.maxconn && max_accept--) { + while (max_accept--) { struct sockaddr_storage addr; socklen_t laddr = sizeof(addr); - loops++; + if (unlikely(actconn >= global.maxconn)) { + limit_listener(l, &global_listener_queue); + return 0; + } + + if (unlikely(p && p->feconn >= p->maxconn)) { + limit_listener(l, &p->listener_queue); + return 0; + } + cfd = accept(fd, (struct sockaddr *)&addr, &laddr); if (unlikely(cfd == -1)) { switch (errno) { @@ -1287,10 +1302,6 @@ int stream_sock_accept(int fd) } /* end of while (p->feconn < p->maxconn) */ - /* if we did not even enter the loop, we've reached resource limits */ - if (!loops && max_accept) - limit_listener(l, &global_listener_queue); - return 0; }