Skip to content

Commit a83169f

Browse files
committed
Wire the M5 nginx USR2 control plane: keep the master free of ff state, let the
new master reuse the resident primary and register its workers' epochs through the generation directory, and hand rx over on WINCH while the old master's workers drain through the M4 machinery. Rollback on a failed upgrade is a HUP to the old master (rx reclaim, upgrade tracking reset, respawn interlock against duplicate worker sets), and the pending wait no longer times out once the peer generation has registered. Fixes a pre-existing USR2 bug where an invalid ff listening fd leaked through NGINX_VAR and broke the new master's startup.
1 parent 86bf554 commit a83169f

3 files changed

Lines changed: 414 additions & 11 deletions

File tree

app/nginx-1.28.0/src/core/nginx.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,15 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
752752
if (ls[i].ignore) {
753753
continue;
754754
}
755+
#if (NGX_HAVE_FSTACK)
756+
/* This process runs no f-stack instance, so ff listening sockets
757+
* were never opened here (ngx_open_listening_sockets() skips them)
758+
* and their fd is -1. Emitting it would make the new binary log
759+
* "invalid socket number" and drop every descriptor after it. */
760+
if (ls[i].fd < 0) {
761+
continue;
762+
}
763+
#endif
755764
p = ngx_sprintf(p, "%ud;", ls[i].fd);
756765
}
757766

app/nginx-1.28.0/src/event/modules/ngx_ff_module.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,12 @@ ff_init_with_args(const char *conf, int proc_id, const char *proc_type)
351351
* Outside a reload window, or when this process is not the target
352352
* generation (the previous round's G_new keeps its registration), every
353353
* packet stays local: the steady state pays one cheap check per packet
354-
* and behaves exactly like an unregistered dispatcher. */
354+
* and behaves exactly like an unregistered dispatcher.
355+
*
356+
* F-M5-1 (USR2): the window may also be the cross-master one — the block
357+
* of a fresh master never opens the M4 window, so the peer check below
358+
* uses the block mirror (a plain shared read refreshed once per loop pass
359+
* by ff_reload_dir_sync, no directory scan on the per-packet path). */
355360
static int
356361
ngx_ff_flow_map_dispatcher(void *data, uint16_t *len, uint16_t queue_id,
357362
uint16_t nb_queues, struct ff_dispatcher_context context)
@@ -365,8 +370,8 @@ ngx_ff_flow_map_dispatcher(void *data, uint16_t *len, uint16_t queue_id,
365370
(void) context;
366371

367372
if (!ff_reload_state_attached()
368-
|| !ff_reload_hw_locked()
369-
|| ff_reload_gen() != ff_reload_target_gen())
373+
|| ff_reload_gen() != ff_reload_target_gen()
374+
|| (!ff_reload_hw_locked() && !ff_reload_peer_mirror_draining()))
370375
{
371376
return queue_id;
372377
}
@@ -466,13 +471,19 @@ ngx_ff_flow_map_dispatcher(void *data, uint16_t *len, uint16_t queue_id,
466471
* master hands rx over, so no packet can reach the callback before the
467472
* table is armed (this generation is parked off the hardware until then).
468473
* D-NR-303 (open at init, not at T3): a parked generation receives no
469-
* packets, so arming early is indistinguishable from arming at T3. */
474+
* packets, so arming early is indistinguishable from arming at T3.
475+
* F-M5-1 (USR2): the fresh master's anonymous block never opens the M4
476+
* reload window, so the block-only gate cannot arm its workers. The
477+
* directory provides the equivalent condition — a peer master epoch that
478+
* is live or still draining — and arming still happens at init, while
479+
* parked, so the first pass that owns rx already classifies (otherwise
480+
* every old-generation flow dies in a burst of RSTs at the WINCH flip). */
470481
static void
471482
ngx_ff_flow_map_arm(void)
472483
{
473484
if (!ff_reload_state_attached()
474-
|| !ff_reload_hw_locked()
475-
|| ff_reload_gen() != ff_reload_target_gen())
485+
|| ff_reload_gen() != ff_reload_target_gen()
486+
|| (!ff_reload_hw_locked() && !ff_reload_peer_draining()))
476487
{
477488
return;
478489
}

0 commit comments

Comments
 (0)