Skip to content

Commit 1cc7b18

Browse files
committed
Gate unshare on exec_thread.listen, move before start_HttpProxyServer
Only call unshare(CLONE_FILES) when per-thread listen is enabled (exec_thread.listen=1, SO_REUSEPORT). Schedule before start_HttpProxyServer() so accept_per_thread creates listen FDs directly in each threads private table. Demote all logging to Dbg to avoid log spam when unshare is blocked (EPERM).
1 parent 5b97aa9 commit 1cc7b18

2 files changed

Lines changed: 21 additions & 23 deletions

File tree

src/iocore/net/UnixNet.cc

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -207,21 +207,19 @@ initialize_thread_for_net(EThread *thread)
207207
}
208208

209209
// Late-initialization continuation for ET_NET threads. Runs on each
210-
// thread after start_HttpProxyServer() has created all listen sockets
211-
// and all initialization FDs (eventfds, cache disks, DNS sockets, log
212-
// files, plugin FDs) are in place.
210+
// thread after all initialization FDs (eventfds, cache disks, DNS
211+
// sockets, log files, plugin FDs) are in place but BEFORE
212+
// start_HttpProxyServer() schedules accept_per_thread.
213213
//
214-
// On Linux, when accept is performed directly on ET_NET threads
215-
// (accept_threads == 0), this calls unshare(CLONE_FILES) to give each
216-
// thread its own private kernel FD table, eliminating spinlock
217-
// contention on accept4/close. The underlying kernel objects
218-
// (struct file) are shared, so cross-thread eventfd signalling and
219-
// shared cache-disk FDs keep working.
214+
// On Linux, when per-thread listen is enabled (exec_thread.listen=1,
215+
// SO_REUSEPORT), this calls unshare(CLONE_FILES) to give each thread
216+
// its own private kernel FD table, eliminating spinlock contention on
217+
// accept4/close. The subsequent do_listen() in accept_per_thread
218+
// creates the per-thread listen socket directly in the private table.
220219
//
221-
// This is NOT safe with dedicated accept threads (accept_threads > 0)
222-
// because accepted sockets are created on the accept thread and handed
223-
// off to ET_NET by FD number — after unshare those FDs would not exist
224-
// in the ET_NET thread's private table.
220+
// The underlying kernel objects (struct file) are shared, so
221+
// cross-thread eventfd signalling and shared cache-disk FDs keep
222+
// working.
225223

226224
namespace
227225
{
@@ -230,13 +228,13 @@ struct ExecThrLateCont : public Continuation {
230228
mainEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
231229
{
232230
#if defined(__linux__)
233-
int accept_threads = RecGetRecordInt("proxy.config.accept_threads").value_or(0);
234-
if (accept_threads > 0) {
235-
Dbg(dbg_ctl_iocore_net, "ET_NET thread %d: skipping unshare (accept_threads=%d)", this_ethread()->id, accept_threads);
236-
} else if (unshare(CLONE_FILES) < 0) {
237-
Warning("ET_NET thread %d: unshare(CLONE_FILES) failed: %s", this_ethread()->id, strerror(errno));
238-
} else {
239-
Dbg(dbg_ctl_iocore_net, "ET_NET thread %d: FD table unshared", this_ethread()->id);
231+
int listen_per_thread = RecGetRecordInt("proxy.config.exec_thread.listen").value_or(0);
232+
if (listen_per_thread == 1) {
233+
if (unshare(CLONE_FILES) < 0) {
234+
Dbg(dbg_ctl_iocore_net, "ET_NET thread %d: unshare(CLONE_FILES) failed: %s", this_ethread()->id, strerror(errno));
235+
} else {
236+
Dbg(dbg_ctl_iocore_net, "ET_NET thread %d: FD table unshared", this_ethread()->id);
237+
}
240238
}
241239
#endif
242240
delete this;
@@ -254,5 +252,5 @@ exec_thr_late_init()
254252
for (int i = 0; i < n; i++) {
255253
eventProcessor.thread_group[ET_NET]._thread[i]->schedule_imm(new ExecThrLateCont());
256254
}
257-
Note("Scheduled exec_thr_late_init() on %d ET_NET threads", n);
255+
Dbg(dbg_ctl_iocore_net, "Scheduled exec_thr_late_init() on %d ET_NET threads", n);
258256
}

src/traffic_server/traffic_server.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -897,8 +897,8 @@ CB_After_Cache_Init()
897897
// delayed the call to start_HttpProxyServer until we got here. We must
898898
// call accept on the ports now that the cache is initialized.
899899
Note("Enabling listen, cache initialization finished");
900+
exec_thr_late_init(); // Must be called before start_HttpProxyServer
900901
start_HttpProxyServer();
901-
exec_thr_late_init(); // Must be last call before emit_fully_initialized_message()
902902
emit_fully_initialized_message();
903903
}
904904

@@ -2458,8 +2458,8 @@ main(int /* argc ATS_UNUSED */, const char **argv)
24582458
//
24592459
// In either case we should not delay to accept the ports.
24602460
Dbg(dbg_ctl_http_listen, "Not delaying listen");
2461+
exec_thr_late_init(); // Must be called before start_HttpProxyServer
24612462
start_HttpProxyServer(); // PORTS_READY_HOOK called from in here
2462-
exec_thr_late_init(); // Must be last call before emit_fully_initialized_message()
24632463
emit_fully_initialized_message();
24642464
}
24652465
}

0 commit comments

Comments
 (0)