diff options
Diffstat (limited to 'server/mpm/worker/worker.c')
-rw-r--r-- | server/mpm/worker/worker.c | 265 |
1 files changed, 193 insertions, 72 deletions
diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 408d317650..4a729c0d35 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -30,6 +30,9 @@ #include "apr_thread_mutex.h" #include "apr_proc_mutex.h" #include "apr_poll.h" + +#include <stdlib.h> + #define APR_WANT_STRFUNC #include "apr_want.h" @@ -156,17 +159,31 @@ typedef struct worker_retained_data { /* * idle_spawn_rate is the number of children that will be spawned on the * next maintenance cycle if there aren't enough idle servers. It is - * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by - * without the need to spawn. + * maintained per listeners bucket, doubled up to MAX_SPAWN_RATE, and + * reset only when a cycle goes by without the need to spawn. */ - int idle_spawn_rate; + int *idle_spawn_rate; #ifndef MAX_SPAWN_RATE #define MAX_SPAWN_RATE (32) #endif int hold_off_on_exponential_spawning; + /* + * Current number of listeners buckets and maximum reached accross + * restarts (to size retained data according to dynamic num_buckets, + * eg. idle_spawn_rate). + */ + int num_buckets, max_buckets; } worker_retained_data; static worker_retained_data *retained; +typedef struct worker_child_bucket { + ap_pod_t *pod; + ap_listen_rec *listeners; + apr_proc_mutex_t *mutex; +} worker_child_bucket; +static worker_child_bucket *all_buckets, /* All listeners buckets */ + *my_bucket; /* Current child bucket */ + #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid) /* The structure used to pass unique initialization info to each thread */ @@ -188,8 +205,6 @@ typedef struct { #define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t) -static ap_pod_t *pod; - /* The worker MPM respects a couple of runtime flags that can aid * in debugging. Setting the -DNO_DETACH flag will prevent the root process * from detaching from its controlling terminal. Additionally, setting @@ -217,9 +232,6 @@ static pid_t ap_my_pid; /* Linux getpid() doesn't work except in main static pid_t parent_pid; static apr_os_thread_t *listener_os_thread; -/* Locks for accept serialization */ -static apr_proc_mutex_t *accept_mutex; - #ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT #define SAFE_ACCEPT(stmt) (ap_listeners->next ? (stmt) : APR_SUCCESS) #else @@ -701,7 +713,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy) clean_child_exit(APEXIT_CHILDSICK); } - for (lr = ap_listeners; lr != NULL; lr = lr->next) { + for (lr = my_bucket->listeners; lr != NULL; lr = lr->next) { apr_pollfd_t pfd = { 0 }; pfd.desc_type = APR_POLL_SOCKET; @@ -758,7 +770,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy) /* We've already decremented the idle worker count inside * ap_queue_info_wait_for_idler. */ - if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(accept_mutex))) + if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(my_bucket->mutex))) != APR_SUCCESS) { if (!listener_may_exit) { @@ -767,9 +779,9 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy) break; /* skip the lock release */ } - if (!ap_listeners->next) { + if (!my_bucket->listeners->next) { /* Only one listener, so skip the poll */ - lr = ap_listeners; + lr = my_bucket->listeners; } else { while (!listener_may_exit) { @@ -839,7 +851,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy) resource_shortage = 1; signal_threads(ST_GRACEFUL); } - if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex))) + if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(my_bucket->mutex))) != APR_SUCCESS) { if (listener_may_exit) { @@ -863,7 +875,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy) } } else { - if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex))) + if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(my_bucket->mutex))) != APR_SUCCESS) { int level = APLOG_EMERG; @@ -880,7 +892,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy) } } - ap_close_listeners(); + ap_close_listeners_ex(my_bucket->listeners); ap_queue_term(worker_queue); dying = 1; ap_scoreboard_image->parent[process_slot].quiescing = 1; @@ -1210,13 +1222,14 @@ static void join_start_thread(apr_thread_t *start_thread_id) } } -static void child_main(int child_num_arg) +static void child_main(int child_num_arg, int child_bucket) { apr_thread_t **threads; apr_status_t rv; thread_starter *ts; apr_threadattr_t *thread_attr; apr_thread_t *start_thread_id; + int i; mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this * child initializes @@ -1225,12 +1238,20 @@ static void child_main(int child_num_arg) ap_fatal_signal_child_setup(ap_server_conf); apr_pool_create(&pchild, pconf); + /* close unused listeners and pods */ + for (i = 0; i < retained->num_buckets; i++) { + if (i != child_bucket) { + ap_close_listeners_ex(all_buckets[i].listeners); + ap_mpm_podx_close(all_buckets[i].pod); + } + } + /*stuff to do before we switch id's, so we have permissions.*/ ap_reopen_scoreboard(pchild, NULL, 0); - rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&accept_mutex, - apr_proc_mutex_lockfile(accept_mutex), - pchild)); + rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&my_bucket->mutex, + apr_proc_mutex_lockfile(my_bucket->mutex), + pchild)); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(00280) "Couldn't initialize cross-process lock in child"); @@ -1338,7 +1359,7 @@ static void child_main(int child_num_arg) apr_signal(SIGTERM, dummy_signal_handler); /* Watch for any messages from the parent over the POD */ while (1) { - rv = ap_mpm_podx_check(pod); + rv = ap_mpm_podx_check(my_bucket->pod); if (rv == AP_MPM_PODX_NORESTART) { /* see if termination was triggered while we slept */ switch(terminate_mode) { @@ -1376,7 +1397,7 @@ static void child_main(int child_num_arg) clean_child_exit(resource_shortage ? APEXIT_CHILDSICK : 0); } -static int make_child(server_rec *s, int slot) +static int make_child(server_rec *s, int slot, int bucket) { int pid; @@ -1385,10 +1406,14 @@ static int make_child(server_rec *s, int slot) } if (one_process) { + my_bucket = &all_buckets[0]; + set_signals(); worker_note_child_started(slot, getpid()); - child_main(slot); + child_main(slot, 0); /* NOTREACHED */ + ap_assert(0); + return -1; } if ((pid = fork()) == -1) { @@ -1410,6 +1435,8 @@ static int make_child(server_rec *s, int slot) } if (!pid) { + my_bucket = &all_buckets[bucket]; + #ifdef HAVE_BINDPROCESSOR /* By default, AIX binds to a single processor. This bit unbinds * children which will then bind to another CPU. @@ -1424,10 +1451,12 @@ static int make_child(server_rec *s, int slot) RAISE_SIGSTOP(MAKE_CHILD); apr_signal(SIGTERM, just_die); - child_main(slot); + child_main(slot, bucket); /* NOTREACHED */ + ap_assert(0); + return -1; } - /* else */ + if (ap_scoreboard_image->parent[slot].pid != 0) { /* This new child process is squatting on the scoreboard * entry owned by an exiting child process, which cannot @@ -1436,6 +1465,7 @@ static int make_child(server_rec *s, int slot) worker_note_child_lost_slot(slot, pid); } ap_scoreboard_image->parent[slot].quiescing = 0; + ap_scoreboard_image->parent[slot].bucket = bucket; worker_note_child_started(slot, pid); return 0; } @@ -1449,14 +1479,14 @@ static void startup_children(int number_to_start) if (ap_scoreboard_image->parent[i].pid != 0) { continue; } - if (make_child(ap_server_conf, i) < 0) { + if (make_child(ap_server_conf, i, i % retained->num_buckets) < 0) { break; } --number_to_start; } } -static void perform_idle_server_maintenance(void) +static void perform_idle_server_maintenance(int child_bucket, int num_buckets) { int i, j; int idle_thread_count; @@ -1485,7 +1515,7 @@ static void perform_idle_server_maintenance(void) int all_dead_threads = 1; int child_threads_active = 0; - if (i >= retained->max_daemons_limit && totally_free_length == retained->idle_spawn_rate) + if (i >= retained->max_daemons_limit && totally_free_length == retained->idle_spawn_rate[child_bucket]) /* short cut if all active processes have been examined and * enough empty scoreboard slots have been found */ @@ -1513,7 +1543,8 @@ static void perform_idle_server_maintenance(void) loop if no pid? not much else matters */ if (status <= SERVER_READY && !ps->quiescing && - ps->generation == retained->my_generation) { + ps->generation == retained->my_generation && + ps->bucket == child_bucket) { ++idle_thread_count; } if (status >= SERVER_READY && status < SERVER_GRACEFUL) { @@ -1522,8 +1553,8 @@ static void perform_idle_server_maintenance(void) } } active_thread_count += child_threads_active; - if (any_dead_threads && totally_free_length < retained->idle_spawn_rate - && free_length < MAX_SPAWN_RATE + if (any_dead_threads && totally_free_length < retained->idle_spawn_rate[child_bucket] + && free_length < MAX_SPAWN_RATE / num_buckets && (!ps->pid /* no process in the slot */ || ps->quiescing)) { /* or at least one is going away */ if (all_dead_threads) { @@ -1579,12 +1610,13 @@ static void perform_idle_server_maintenance(void) retained->max_daemons_limit = last_non_dead + 1; - if (idle_thread_count > max_spare_threads) { + if (idle_thread_count > max_spare_threads / num_buckets) { /* Kill off one child */ - ap_mpm_podx_signal(pod, AP_MPM_PODX_GRACEFUL); - retained->idle_spawn_rate = 1; + ap_mpm_podx_signal(all_buckets[child_bucket].pod, + AP_MPM_PODX_GRACEFUL); + retained->idle_spawn_rate[child_bucket] = 1; } - else if (idle_thread_count < min_spare_threads) { + else if (idle_thread_count < min_spare_threads / num_buckets) { /* terminate the free list */ if (free_length == 0) { /* scoreboard is full, can't fork */ @@ -1615,13 +1647,13 @@ static void perform_idle_server_maintenance(void) ap_server_conf, APLOGNO(00288) "scoreboard is full, not at MaxRequestWorkers"); } - retained->idle_spawn_rate = 1; + retained->idle_spawn_rate[child_bucket] = 1; } else { - if (free_length > retained->idle_spawn_rate) { - free_length = retained->idle_spawn_rate; + if (free_length > retained->idle_spawn_rate[child_bucket]) { + free_length = retained->idle_spawn_rate[child_bucket]; } - if (retained->idle_spawn_rate >= 8) { + if (retained->idle_spawn_rate[child_bucket] >= 8) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00289) "server seems busy, (you may need " @@ -1632,7 +1664,7 @@ static void perform_idle_server_maintenance(void) idle_thread_count, total_non_dead); } for (i = 0; i < free_length; ++i) { - make_child(ap_server_conf, free_slots[i]); + make_child(ap_server_conf, free_slots[i], child_bucket); } /* the next time around we want to spawn twice as many if this * wasn't good enough, but not if we've just done a graceful @@ -1640,17 +1672,18 @@ static void perform_idle_server_maintenance(void) if (retained->hold_off_on_exponential_spawning) { --retained->hold_off_on_exponential_spawning; } - else if (retained->idle_spawn_rate < MAX_SPAWN_RATE) { - retained->idle_spawn_rate *= 2; + else if (retained->idle_spawn_rate[child_bucket] + < MAX_SPAWN_RATE / num_buckets) { + retained->idle_spawn_rate[child_bucket] *= 2; } } } else { - retained->idle_spawn_rate = 1; + retained->idle_spawn_rate[child_bucket] = 1; } } -static void server_main_loop(int remaining_children_to_start) +static void server_main_loop(int remaining_children_to_start, int num_buckets) { ap_generation_t old_gen; int child_slot; @@ -1694,22 +1727,25 @@ static void server_main_loop(int remaining_children_to_start) } /* non-fatal death... note that it's gone in the scoreboard. */ if (child_slot >= 0) { + process_score *ps; + for (i = 0; i < threads_per_child; i++) ap_update_child_status_from_indexes(child_slot, i, SERVER_DEAD, (request_rec *) NULL); worker_note_child_killed(child_slot, 0, 0); - ap_scoreboard_image->parent[child_slot].quiescing = 0; + ps = &ap_scoreboard_image->parent[child_slot]; + ps->quiescing = 0; if (processed_status == APEXIT_CHILDSICK) { /* resource shortage, minimize the fork rate */ - retained->idle_spawn_rate = 1; + retained->idle_spawn_rate[ps->bucket] = 1; } else if (remaining_children_to_start && child_slot < ap_daemons_limit) { /* we're still doing a 1-for-1 replacement of dead * children with new children */ - make_child(ap_server_conf, child_slot); + make_child(ap_server_conf, child_slot, ps->bucket); --remaining_children_to_start; } } @@ -1719,7 +1755,9 @@ static void server_main_loop(int remaining_children_to_start) if (processed_status == APEXIT_CHILDSICK && old_gen == retained->my_generation) { /* resource shortage, minimize the fork rate */ - retained->idle_spawn_rate = 1; + for (i = 0; i < num_buckets; i++) { + retained->idle_spawn_rate[i] = 1; + } } #if APR_HAS_OTHER_CHILD } @@ -1758,25 +1796,20 @@ static void server_main_loop(int remaining_children_to_start) continue; } - perform_idle_server_maintenance(); + for (i = 0; i < num_buckets; i++) { + perform_idle_server_maintenance(i, num_buckets); + } } } static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) { + int num_buckets = retained->num_buckets; int remaining_children_to_start; - apr_status_t rv; + int i; ap_log_pid(pconf, ap_pid_fname); - /* Initialize cross-process accept lock */ - rv = ap_proc_mutex_create(&accept_mutex, NULL, AP_ACCEPT_MUTEX_TYPE, NULL, - s, _pconf, 0); - if (rv != APR_SUCCESS) { - mpm_state = AP_MPMQ_STOPPING; - return DONE; - } - if (!retained->is_graceful) { if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) { mpm_state = AP_MPMQ_STOPPING; @@ -1790,9 +1823,18 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) restart_pending = shutdown_pending = 0; set_signals(); - /* Don't thrash... */ - if (max_spare_threads < min_spare_threads + threads_per_child) - max_spare_threads = min_spare_threads + threads_per_child; + + /* Don't thrash since num_buckets depends on the + * system and the number of online CPU cores... + */ + if (ap_daemons_limit < num_buckets) + ap_daemons_limit = num_buckets; + if (ap_daemons_to_start < num_buckets) + ap_daemons_to_start = num_buckets; + if (min_spare_threads < threads_per_child * num_buckets) + min_spare_threads = threads_per_child * num_buckets; + if (max_spare_threads < min_spare_threads + threads_per_child * num_buckets) + max_spare_threads = min_spare_threads + threads_per_child * num_buckets; /* If we're doing a graceful_restart then we're going to see a lot * of children exiting immediately when we get into the main loop @@ -1823,20 +1865,26 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00293) "Server built: %s", ap_get_server_built()); ap_log_command_line(plog, s); + ap_log_mpm_common(s); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00294) "Accept mutex: %s (default: %s)", - apr_proc_mutex_name(accept_mutex), + (all_buckets[0].mutex) + ? apr_proc_mutex_name(all_buckets[0].mutex) + : "none", apr_proc_mutex_defname()); mpm_state = AP_MPMQ_RUNNING; - server_main_loop(remaining_children_to_start); + server_main_loop(remaining_children_to_start, num_buckets); mpm_state = AP_MPMQ_STOPPING; if (shutdown_pending && !retained->is_graceful) { /* Time to shut down: * Kill child processes, tell them to call child_exit, etc... */ - ap_mpm_podx_killpg(pod, ap_daemons_limit, AP_MPM_PODX_RESTART); + for (i = 0; i < num_buckets; i++) { + ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit, + AP_MPM_PODX_RESTART); + } ap_reclaim_child_processes(1, /* Start with SIGTERM */ worker_note_child_killed); @@ -1857,7 +1905,11 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* Close our listeners, and then ask our children to do same */ ap_close_listeners(); - ap_mpm_podx_killpg(pod, ap_daemons_limit, AP_MPM_PODX_GRACEFUL); + + for (i = 0; i < num_buckets; i++) { + ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit, + AP_MPM_PODX_GRACEFUL); + } ap_relieve_child_processes(worker_note_child_killed); if (!child_fatal) { @@ -1897,7 +1949,10 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) * way, try and make sure that all of our processes are * really dead. */ - ap_mpm_podx_killpg(pod, ap_daemons_limit, AP_MPM_PODX_RESTART); + for (i = 0; i < num_buckets; i++) { + ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit, + AP_MPM_PODX_RESTART); + } ap_reclaim_child_processes(1, worker_note_child_killed); return DONE; @@ -1922,8 +1977,10 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00297) AP_SIG_GRACEFUL_STRING " received. Doing graceful restart"); /* wake up the children...time to die. But we'll have more soon */ - ap_mpm_podx_killpg(pod, ap_daemons_limit, AP_MPM_PODX_GRACEFUL); - + for (i = 0; i < num_buckets; i++) { + ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit, + AP_MPM_PODX_GRACEFUL); + } /* This is mostly for debugging... so that we know what is still * gracefully dealing with existing request. @@ -1935,7 +1992,10 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) * and a SIGHUP, we may as well use the same signal, because some user * pthreads are stealing signals from us left and right. */ - ap_mpm_podx_killpg(pod, ap_daemons_limit, AP_MPM_PODX_RESTART); + for (i = 0; i < num_buckets; i++) { + ap_mpm_podx_killpg(all_buckets[i].pod, ap_daemons_limit, + AP_MPM_PODX_RESTART); + } ap_reclaim_child_processes(1, /* Start with SIGTERM */ worker_note_child_killed); @@ -1953,7 +2013,11 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, { int startup = 0; int level_flags = 0; + int num_buckets = 0; + ap_listen_rec **listen_buckets; apr_status_t rv; + char id[16]; + int i; pconf = p; @@ -1970,14 +2034,72 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, return DONE; } - if (!one_process) { - if ((rv = ap_mpm_podx_open(pconf, &pod))) { + if (one_process) { + num_buckets = 1; + } + else if (retained->is_graceful) { + /* Preserve the number of buckets on graceful restarts. */ + num_buckets = retained->num_buckets; + } + if ((rv = ap_duplicate_listeners(pconf, ap_server_conf, + &listen_buckets, &num_buckets))) { + ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv, + (startup ? NULL : s), + "could not duplicate listeners"); + return DONE; + } + + all_buckets = apr_pcalloc(pconf, num_buckets * sizeof(*all_buckets)); + for (i = 0; i < num_buckets; i++) { + if (!one_process && /* no POD in one_process mode */ + (rv = ap_mpm_podx_open(pconf, &all_buckets[i].pod))) { ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv, (startup ? NULL : s), "could not open pipe-of-death"); return DONE; } + /* Initialize cross-process accept lock (safe accept needed only) */ + if ((rv = SAFE_ACCEPT((apr_snprintf(id, sizeof id, "%i", i), + ap_proc_mutex_create(&all_buckets[i].mutex, + NULL, AP_ACCEPT_MUTEX_TYPE, + id, s, pconf, 0))))) { + ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv, + (startup ? NULL : s), + "could not create accept mutex"); + return DONE; + } + all_buckets[i].listeners = listen_buckets[i]; } + + if (retained->max_buckets < num_buckets) { + int new_max, *new_ptr; + new_max = retained->max_buckets * 2; + if (new_max < num_buckets) { + new_max = num_buckets; + } + new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int)); + memcpy(new_ptr, retained->idle_spawn_rate, + retained->num_buckets * sizeof(int)); + retained->idle_spawn_rate = new_ptr; + retained->max_buckets = new_max; + } + if (retained->num_buckets < num_buckets) { + int rate_max = 1; + /* If new buckets are added, set their idle spawn rate to + * the highest so far, so that they get filled as quickly + * as the existing ones. + */ + for (i = 0; i < retained->num_buckets; i++) { + if (rate_max < retained->idle_spawn_rate[i]) { + rate_max = retained->idle_spawn_rate[i]; + } + } + for (/* up to date i */; i < num_buckets; i++) { + retained->idle_spawn_rate[i] = rate_max; + } + } + retained->num_buckets = num_buckets; + return OK; } @@ -2009,7 +2131,6 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog, if (!retained) { retained = ap_retained_data_create(userdata_key, sizeof(*retained)); retained->max_daemons_limit = -1; - retained->idle_spawn_rate = 1; } ++retained->module_loads; if (retained->module_loads == 2) { @@ -2241,7 +2362,7 @@ static int worker_check_config(apr_pool_t *p, apr_pool_t *plog, } /* ap_daemons_to_start > ap_daemons_limit checked in worker_run() */ - if (ap_daemons_to_start < 0) { + if (ap_daemons_to_start < 1) { if (startup) { ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00320) "WARNING: StartServers of %d not allowed, " |