. auto/feature
+ngx_feature="SO_REUSEPORT"
+ngx_feature_name="NGX_HAVE_REUSEPORT"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, SOL_SOCKET, SO_REUSEPORT, NULL, 0)"
+. auto/feature
+
+
ngx_feature="SO_ACCEPTFILTER"
ngx_feature_name="NGX_HAVE_DEFERRED_ACCEPT"
ngx_feature_run=no
}
+ngx_int_t
+ngx_clone_listening(ngx_conf_t *cf, ngx_listening_t *ls)
+{
+#if (NGX_HAVE_REUSEPORT)
+
+ ngx_int_t n;
+ ngx_core_conf_t *ccf;
+ ngx_listening_t ols;
+
+ if (!ls->reuseport) {
+ return NGX_OK;
+ }
+
+ ols = *ls;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,
+ ngx_core_module);
+
+ for (n = 1; n < ccf->worker_processes; n++) {
+
+ /* create a socket for each worker process */
+
+ ls = ngx_array_push(&cf->cycle->listening);
+ if (ls == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ls = ols;
+ ls->worker = n;
+ }
+
+#endif
+
+ return NGX_OK;
+}
+
+
ngx_int_t
ngx_set_inherited_sockets(ngx_cycle_t *cycle)
{
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
int timeout;
#endif
+#if (NGX_HAVE_REUSEPORT)
+ int reuseport;
+#endif
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
#endif
#endif
+#if (NGX_HAVE_REUSEPORT)
+
+ reuseport = 0;
+ olen = sizeof(int);
+
+ if (getsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT,
+ (void *) &reuseport, &olen)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "getsockopt(SO_REUSEPORT) %V failed, ignored",
+ &ls[i].addr_text);
+
+ } else {
+ ls[i].reuseport = reuseport ? 1 : 0;
+ }
+
+#endif
+
#if (NGX_HAVE_TCP_FASTOPEN)
olen = sizeof(int);
continue;
}
+#if (NGX_HAVE_REUSEPORT)
+
+ if (ls[i].add_reuseport) {
+
+ /*
+ * to allow transition from a socket without SO_REUSEPORT
+ * to multiple sockets with SO_REUSEPORT, we have to set
+ * SO_REUSEPORT on the old socket before opening new ones
+ */
+
+ int reuseport = 1;
+
+ if (setsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT,
+ (const void *) &reuseport, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "setsockopt(SO_REUSEPORT) %V failed, ignored",
+ &ls[i].addr_text);
+ }
+
+ ls[i].add_reuseport = 0;
+ }
+#endif
+
if (ls[i].fd != (ngx_socket_t) -1) {
continue;
}
return NGX_ERROR;
}
+#if (NGX_HAVE_REUSEPORT)
+
+ if (ls[i].reuseport) {
+ int reuseport;
+
+ reuseport = 1;
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT,
+ (const void *) &reuseport, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ "setsockopt(SO_REUSEPORT) %V failed, ignored",
+ &ls[i].addr_text);
+
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[i].addr_text);
+ }
+
+ return NGX_ERROR;
+ }
+ }
+#endif
+
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
if (ls[i].sockaddr->sa_family == AF_INET6) {
ngx_listening_t *previous;
ngx_connection_t *connection;
+ ngx_uint_t worker;
+
unsigned open:1;
unsigned remain:1;
unsigned ignore:1;
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
unsigned ipv6only:1;
+#endif
+#if (NGX_HAVE_REUSEPORT)
+ unsigned reuseport:1;
+ unsigned add_reuseport:1;
#endif
unsigned keepalive:2;
ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, void *sockaddr,
socklen_t socklen);
+ngx_int_t ngx_clone_listening(ngx_conf_t *cf, ngx_listening_t *ls);
ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle);
ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle);
void ngx_configure_listening_sockets(ngx_cycle_t *cycle);
continue;
}
+ if (ls[i].remain) {
+ continue;
+ }
+
if (ngx_cmp_sockaddr(nls[n].sockaddr, nls[n].socklen,
ls[i].sockaddr, ls[i].socklen, 1)
== NGX_OK)
nls[n].add_deferred = 1;
}
#endif
+
+#if (NGX_HAVE_REUSEPORT)
+ if (nls[n].reuseport && !ls[i].reuseport) {
+ nls[n].add_reuseport = 1;
+ }
+#endif
+
break;
}
}
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
+#if (NGX_HAVE_REUSEPORT)
+ if (ls[i].reuseport && ls[i].worker != ngx_worker) {
+ continue;
+ }
+#endif
+
c = ngx_get_connection(ls[i].fd, cycle->log);
if (c == NULL) {
static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);
-static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle);
+static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all);
static void ngx_close_accepted_connection(ngx_connection_t *c);
}
if (err == NGX_EMFILE || err == NGX_ENFILE) {
- if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle)
+ if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle, 1)
!= NGX_OK)
{
return;
"accept mutex lock failed: %ui", ngx_accept_mutex_held);
if (ngx_accept_mutex_held) {
- if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
+ if (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) {
return NGX_ERROR;
}
c = ls[i].connection;
- if (c->read->active) {
+ if (c == NULL || c->read->active) {
continue;
}
static ngx_int_t
-ngx_disable_accept_events(ngx_cycle_t *cycle)
+ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all)
{
ngx_uint_t i;
ngx_listening_t *ls;
c = ls[i].connection;
- if (!c->read->active) {
+ if (c == NULL || !c->read->active) {
continue;
}
+#if (NGX_HAVE_REUSEPORT)
+
+ /*
+ * do not disable accept on worker's own sockets
+ * when disabling accept events due to accept mutex
+ */
+
+ if (ls[i].reuseport && !all) {
+ continue;
+ }
+
+#endif
+
if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)
== NGX_ERROR)
{
break;
}
+ if (ngx_clone_listening(cf, ls) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
addr++;
last--;
}
ls->fastopen = addr->opt.fastopen;
#endif
+#if (NGX_HAVE_REUSEPORT)
+ ls->reuseport = addr->opt.reuseport;
+#endif
+
return ls;
}
#endif
}
+ if (ngx_strcmp(value[n].data, "reuseport") == 0) {
+#if (NGX_HAVE_REUSEPORT)
+ lsopt.reuseport = 1;
+ lsopt.set = 1;
+ lsopt.bind = 1;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "reuseport is not supported "
+ "on this platform, ignored");
+#endif
+ continue;
+ }
+
if (ngx_strcmp(value[n].data, "ssl") == 0) {
#if (NGX_HTTP_SSL)
lsopt.ssl = 1;
#endif
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
unsigned ipv6only:1;
+#endif
+#if (NGX_HAVE_REUSEPORT)
+ unsigned reuseport:1;
#endif
unsigned so_keepalive:2;
unsigned proxy_protocol:1;
break;
}
+ if (ngx_clone_listening(cf, ls) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
addr++;
last--;
}
#endif
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
unsigned ipv6only:1;
+#endif
+#if (NGX_HAVE_REUSEPORT)
+ unsigned reuseport:1;
#endif
unsigned so_keepalive:2;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
#endif
}
+ if (ngx_strcmp(value[i].data, "reuseport") == 0) {
+#if (NGX_HAVE_REUSEPORT)
+ ls->reuseport = 1;
+ ls->bind = 1;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "reuseport is not supported "
+ "on this platform, ignored");
+#endif
+ continue;
+ }
+
if (ngx_strcmp(value[i].data, "ssl") == 0) {
#if (NGX_STREAM_SSL)
ls->ssl = 1;