This performance optimization has a few cons, including security
concerns, but it improves CPU core utilization/balance in many SMP
environments and is supported by many high-performance servers. Enabled
by the new `*_port worker-queues` configuration option.
Worker-dedicated listening queues reduce client-worker affinity for
requests submitted over different TCP connections. The effect of that
reduction on Squid performance depends on the environment, but many busy
SMP proxies handling modern traffic should benefit.
TODO: Linux tests show load balancing effects of SO_REUSEPORT, but
untested FreeBSD probably needs SO_REUSEPORT_LB to get those effects.
Recommended reading:
* https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/
* https://lwn.net/Articles/542629/
* https://stackoverflow.com/a/
14388707
ftp_track_dirs(false),
vport(0),
disable_pmtu_discovery(0),
+ workerQueues(false),
listenConn()
{
memset(&tcp_keepalive, 0, sizeof(tcp_keepalive));
b->vhost = vhost;
b->vport = vport;
b->connection_auth_disabled = connection_auth_disabled;
+ b->workerQueues = workerQueues;
b->ftp_track_dirs = ftp_track_dirs;
b->disable_pmtu_discovery = disable_pmtu_discovery;
b->tcp_keepalive = tcp_keepalive;
int vport; ///< virtual port support. -1 if dynamic, >0 static
int disable_pmtu_discovery;
+ bool workerQueues; ///< whether listening queues should be worker-specific
struct {
unsigned int idle;
#if HAVE_GRP_H
#include <grp.h>
#endif
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
s->secure.parse(token+4);
} else if (strcmp(token, "ftp-track-dirs") == 0) {
s->ftp_track_dirs = true;
+ } else if (strcmp(token, "worker-queues") == 0) {
+#if !defined(SO_REUSEADDR)
+#error missing system #include that #defines SO_* constants
+#endif
+#if !defined(SO_REUSEPORT)
+ throw TexcHere(ToSBuf(cfg_directive, ' ', token, " option requires building Squid where SO_REUSEPORT is supported by the TCP stack"));
+#endif
+ s->workerQueues = true;
} else {
debugs(3, DBG_CRITICAL, "FATAL: Unknown " << cfg_directive << " option '" << token << "'.");
self_destruct();
The proxy_protocol_access is required to whitelist
downstream proxies which can be trusted.
+ worker-queues
+ Ask TCP stack to maintain a dedicated listening queue
+ for each worker accepting requests at this port.
+ Requires TCP stack that supports the SO_REUSEPORT socket
+ option.
+
+ SECURITY WARNING: Enabling worker-specific queues
+ allows any process running as Squid's effective user to
+ easily accept requests destined to this port.
+
If you run Squid on a dual-homed machine with an internal
and an external interface we recommend you to specify the
internal address:port in http_port. This way Squid will only be
s->listenConn->local = s->s;
s->listenConn->flags = COMM_NONBLOCKING | (s->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
- (s->flags.natIntercept ? COMM_INTERCEPTION : 0);
+ (s->flags.natIntercept ? COMM_INTERCEPTION : 0) |
+ (s->workerQueues ? COMM_REUSEPORT : 0);
typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
if (s->transport.protocol == AnyP::PROTO_HTTP) {
#include "pconn.h"
#include "profiler/Profiler.h"
#include "sbuf/SBuf.h"
+#include "sbuf/Stream.h"
#include "SquidConfig.h"
#include "StatCounters.h"
#include "StoreIOBuffer.h"
if ( addr.isNoAddr() )
debugs(5,0,"CRITICAL: Squid is attempting to bind() port " << addr << "!!");
+#if defined(SO_REUSEPORT)
+ if (flags & COMM_REUSEPORT) {
+ int on = 1;
+ if (setsockopt(new_socket, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char*>(&on), sizeof(on)) < 0) {
+ const auto savedErrno = errno;
+ const auto errorMessage = ToSBuf("cannot enable SO_REUSEPORT socket option when binding to ",
+ addr, ": ", xstrerr(savedErrno));
+ if (reconfiguring)
+ debugs(5, DBG_IMPORTANT, "ERROR: " << errorMessage);
+ else
+ throw TexcHere(errorMessage);
+ }
+ }
+#endif
if (commBind(new_socket, *AI) != Comm::OK) {
comm_close(new_socket);
return -1;
#define COMM_DOBIND 0x08 // requires a bind()
#define COMM_TRANSPARENT 0x10 // arrived via TPROXY
#define COMM_INTERCEPTION 0x20 // arrived via NAT
+#define COMM_REUSEPORT 0x40 //< needs SO_REUSEPORT
/**
* Store data about the physical and logical attributes of a connection.
Must(cbd);
cbd->conn = listenConn;
- if (UsingSmp()) { // if SMP is on, share
+ const auto giveEachWorkerItsOwnQueue = listenConn->flags & COMM_REUSEPORT;
+ if (!giveEachWorkerItsOwnQueue && UsingSmp()) {
+ // Ask Coordinator for a listening socket.
+ // All askers share one listening queue.
OpenListenerParams p;
p.sock_type = sock_type;
p.proto = proto;