--- httpd-2.2.2-old/server/mpm/experimental/peruser/peruser.c 2008-05-16 16:10:21.000000000 +0300 +++ httpd-2.2.2-new/server/mpm/experimental/peruser/peruser.c 2008-05-19 09:14:04.000000000 +0300 @@ -206,9 +206,14 @@ static int ap_min_processors=DEFAULT_MIN_PROCESSORS; static int ap_min_free_processors=DEFAULT_MIN_FREE_PROCESSORS; static int ap_max_processors=DEFAULT_MAX_PROCESSORS; +static int ap_min_multiplexers=DEFAULT_MIN_MULTIPLEXERS; +static int ap_max_multiplexers=DEFAULT_MAX_MULTIPLEXERS; static int ap_daemons_limit=0; /* MaxClients */ -static int expire_timeout=1800; -static int idle_timeout=900; +static int expire_timeout=DEFAULT_EXPIRE_TIMEOUT; +static int idle_timeout=DEFAULT_IDLE_TIMEOUT; +static int multiplexer_idle_timeout=DEFAULT_MULTIPLEXER_IDLE_TIMEOUT; +static int processor_wait_timeout=DEFAULT_PROCESSOR_WAIT_TIMEOUT; +static int processor_wait_steps=DEFAULT_PROCESSOR_WAIT_STEPS; static int server_limit = DEFAULT_SERVER_LIMIT; static int first_server_limit; static int changed_limit_at_restart; @@ -221,16 +226,20 @@ typedef struct { int processor_id; - + + const char *name; /* Server environment's unique string identifier */ + /* security settings */ uid_t uid; /* user id */ gid_t gid; /* group id */ const char *chroot; /* directory to chroot() to, can be null */ + int nice_lvl; /* resource settings */ int min_processors; int min_free_processors; int max_processors; + int availability; /* sockets */ int input; /* The socket descriptor */ @@ -437,6 +446,25 @@ return "UNKNOWN"; } +char* scoreboard_status_string(int status) { + switch(status) + { + case SERVER_DEAD: return "DEAD"; + case SERVER_STARTING: return "STARTING"; + case SERVER_READY: return "READY"; + case SERVER_BUSY_READ: return "BUSY_READ"; + case SERVER_BUSY_WRITE: return "BUSY_WRITE"; + case SERVER_BUSY_KEEPALIVE: return "BUSY_KEEPALIVE"; + case SERVER_BUSY_LOG: return "BUSY_LOG"; + case SERVER_BUSY_DNS: return "BUSY_DNS"; + case SERVER_CLOSING: return "CLOSING"; + case SERVER_GRACEFUL: return "GRACEFUL"; + case SERVER_NUM_STATUS: return "NUM_STATUS"; + } + + return "UNKNOWN"; +} + void dump_child_table() { #ifdef MPM_PERUSER_DEBUG @@ -1116,7 +1144,7 @@ apr_bucket *bucket; const apr_array_header_t *headers_in_array; const apr_table_entry_t *headers_in; - int counter; + int counter, wait_time, wait_step_size; apr_socket_t *thesock = ap_get_module_config(r->connection->conn_config, &core_module); @@ -1137,6 +1165,63 @@ apr_table_get(r->headers_in, "Host"), my_child_num, processor->senv->output); _DBG("r->the_request=\"%s\" len=%d", r->the_request, strlen(r->the_request)); + wait_step_size = 100 / processor_wait_steps; + + /* Check if the processor is available */ + if (total_processors(processor->id) == processor->senv->max_processors && + idle_processors(processor->id) == 0) { + /* The processor is currently busy, try to wait (a little) */ + _DBG("processor seems to be busy, trying to wait for it"); + + if (processor->senv->availability == 0) { + processor->senv->availability = 0; + + _DBG("processor is very busy (availability = 0) - not passing request"); + /* No point in waiting for the processor, it's very busy */ + return -1; + } + + /* We sleep a little (depending how available the processor usually is) */ + int i; + + wait_time = (processor_wait_timeout / processor_wait_steps) * 1000000; + + for(i = 0; i <= processor->senv->availability; i += wait_step_size) { + usleep(wait_time); + + /* Check if the processor is ready */ + if (total_processors(processor->id) < processor->senv->max_processors || + idle_processors(processor->id) > 0) { + /* The processor has freed - lets use it */ + _DBG("processor freed before wait time expired"); + break; + } + } + + if (processor->senv->availability <= wait_step_size) { + processor->senv->availability = 0; + } + else processor->senv->availability -= wait_step_size; + + /* Check if we waited all the time */ + if (i > processor->senv->availability) { + _DBG("processor is busy - not passing request (availability = %d)", + processor->senv->availability); + return -1; + } + + /* We could increase the availability a little here, + * because the processor got freed eventually + */ + } + else { + /* Smoothly increment the availability back to 100 */ + if (processor->senv->availability >= 100-wait_step_size) { + processor->senv->availability = 100; + } + else processor->senv->availability += wait_step_size; + } + ap_get_brigade(r->connection->input_filters, bb, AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, len); /* Scan the brigade looking for heap-buckets */ @@ -1402,6 +1487,10 @@ static int peruser_setup_child(int childnum) { server_env_t *senv = CHILD_INFO_TABLE[childnum].senv; + + if (senv->nice_lvl != 0) { + nice(senv->nice_lvl); + } if(senv->chroot) { _DBG("chdir to %s", senv->chroot); @@ -1599,7 +1688,8 @@ _DBG("updating processor stati", 0); for(i = 0; i < NUM_CHILDS; ++i) { - if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY) + if(CHILD_INFO_TABLE[i].type != CHILD_TYPE_MULTIPLEXER && + CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY) CHILD_INFO_TABLE[i].status = CHILD_STATUS_ACTIVE; } @@ -1740,7 +1830,8 @@ } if (CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_PROCESSOR || - CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER) + CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER || + CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER) { _DBG("CHECKING IF WE SHOULD CLONE A CHILD..."); @@ -1752,10 +1843,14 @@ idle_processors(my_child_num), CHILD_INFO_TABLE[my_child_num].senv->min_free_processors); - if(total_processors(my_child_num) < + if( + total_processors(my_child_num) < CHILD_INFO_TABLE[my_child_num].senv->max_processors && - idle_processors(my_child_num) <= - CHILD_INFO_TABLE[my_child_num].senv->min_free_processors) + (idle_processors(my_child_num) <= + CHILD_INFO_TABLE[my_child_num].senv->min_free_processors || + total_processors(my_child_num) < + CHILD_INFO_TABLE[my_child_num].senv->min_processors + )) { _DBG("CLONING CHILD"); child_clone(); @@ -1804,22 +1899,55 @@ clean_child_exit(0); } -static server_env_t* senv_add(int uid, int gid, const char* chroot) -{ +static server_env_t* find_senv_by_name(const char *name) { int i; - int socks[2]; + + if (name == NULL) return NULL; + + _DBG("name=%s", name); - _DBG("Searching for matching senv..."); + for(i = 0; i < NUM_SENV; i++) + { + if(SENV[i].name != NULL && !strcmp(SENV[i].name, name)) { + return &SENV[i]; + } + } + + return NULL; +} + +static server_env_t* find_matching_senv(server_env_t* senv) { + int i; + + _DBG("name=%s uid=%d gid=%d chroot=%s", senv->name, senv->uid, senv->gid, senv->chroot); for(i = 0; i < NUM_SENV; i++) { - if(SENV[i].uid == uid && SENV[i].gid == gid && - (SENV[i].chroot == NULL || !strcmp(SENV[i].chroot, chroot))) - { - _DBG("Found existing senv: %i", i); + if((senv->name != NULL && SENV[i].name != NULL && !strcmp(SENV[i].name, senv->name)) || + (senv->name == NULL && SENV[i].uid == senv->uid && SENV[i].gid == senv->gid && + ((SENV[i].chroot == NULL && senv->chroot == NULL) || ((SENV[i].chroot != NULL || senv->chroot != NULL) && !strcmp(SENV[i].chroot, senv->chroot)))) + ) { return &SENV[i]; } } + + return NULL; +} + +static server_env_t* senv_add(server_env_t *senv) +{ + int socks[2]; + server_env_t *old_senv; + + _DBG("Searching for matching senv..."); + + old_senv = find_matching_senv(senv); + + if (old_senv) { + _DBG("Found existing senv"); + senv = old_senv; + return old_senv; + } if(NUM_SENV >= server_limit) { @@ -1828,22 +1956,20 @@ } _DBG("Creating new senv"); + + memcpy(&SENV[NUM_SENV], senv, sizeof(server_env_t)); - SENV[NUM_SENV].uid = uid; - SENV[NUM_SENV].gid = gid; - SENV[NUM_SENV].chroot = chroot; - - SENV[NUM_SENV].min_processors = ap_min_processors; - SENV[NUM_SENV].min_free_processors = ap_min_free_processors; - SENV[NUM_SENV].max_processors = ap_max_processors; + SENV[NUM_SENV].availability = 100; socketpair(PF_UNIX, SOCK_STREAM, 0, socks); SENV[NUM_SENV].input = socks[0]; SENV[NUM_SENV].output = socks[1]; - + + senv = &SENV[NUM_SENV]; return &SENV[server_env_image->control->num++]; } + static const char* child_clone() { int i; @@ -1869,7 +1995,12 @@ new = &CHILD_INFO_TABLE[i]; new->senv = this->senv; - new->type = CHILD_TYPE_WORKER; + if (this->type == CHILD_TYPE_MULTIPLEXER) { + new->type = CHILD_TYPE_MULTIPLEXER; + } + else { + new->type = CHILD_TYPE_WORKER; + } new->sock_fd = this->sock_fd; new->status = CHILD_STATUS_STARTING; @@ -1878,7 +2009,7 @@ } static const char* child_add(int type, int status, - apr_pool_t *pool, uid_t uid, gid_t gid, const char* chroot) + apr_pool_t *pool, server_env_t *senv) { _DBG("adding child #%d", NUM_CHILDS); @@ -1888,10 +2019,10 @@ "Increase NumServers in your config file."; } - if (chroot && !ap_is_directory(pool, chroot)) - return apr_psprintf(pool, "Error: chroot directory [%s] does not exist", chroot); + if (senv->chroot && !ap_is_directory(pool, senv->chroot)) + return apr_psprintf(pool, "Error: chroot directory [%s] does not exist", senv->chroot); - CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(uid, gid, chroot); + CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(senv); if(CHILD_INFO_TABLE[NUM_CHILDS].senv == NULL) { @@ -1907,10 +2038,10 @@ CHILD_INFO_TABLE[NUM_CHILDS].status = status; _DBG("[%d] uid=%d gid=%d type=%d chroot=%s", - NUM_CHILDS, uid, gid, type, - chroot); + NUM_CHILDS, senv->uid, senv->gid, type, + senv->chroot); - if (uid == 0 || gid == 0) + if (senv->uid == 0 || senv->gid == 0) { _DBG("Assigning root user/group to a child.", 0); } @@ -2062,19 +2193,28 @@ if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_STARTING) make_child(ap_server_conf, i); } - else if(((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || + else if( + (((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER) && ap_scoreboard_image->parent[i].pid > 1) && - (idle_processors (i) > 1 || total_processes (i) == 1) && ( + (idle_processors (i) > CHILD_INFO_TABLE[i].senv->min_free_processors || CHILD_INFO_TABLE[i].senv->min_free_processors == 0) + && total_processes (i) > CHILD_INFO_TABLE[i].senv->min_processors && ( (expire_timeout > 0 && ap_scoreboard_image->servers[i][0].status != SERVER_DEAD && apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > expire_timeout) || (idle_timeout > 0 && ap_scoreboard_image->servers[i][0].status == SERVER_READY && - apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > idle_timeout))) + apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > idle_timeout)) + ) + || (CHILD_INFO_TABLE[i].type == CHILD_TYPE_MULTIPLEXER && + (multiplexer_idle_timeout > 0 && ap_scoreboard_image->servers[i][0].status == SERVER_READY && + apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > multiplexer_idle_timeout) && + total_processors(i) > CHILD_INFO_TABLE[i].senv->min_processors + ) + ) { CHILD_INFO_TABLE[i].pid = 0; CHILD_INFO_TABLE[i].status = CHILD_STATUS_STANDBY; - if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER) + if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER || CHILD_INFO_TABLE[i].type == CHILD_TYPE_MULTIPLEXER) { /* completely free up this slot */ @@ -2599,6 +2739,8 @@ ap_min_processors = DEFAULT_MIN_PROCESSORS; ap_min_free_processors = DEFAULT_MIN_FREE_PROCESSORS; ap_max_processors = DEFAULT_MAX_PROCESSORS; + ap_min_multiplexers = DEFAULT_MIN_MULTIPLEXERS; + ap_max_multiplexers = DEFAULT_MAX_MULTIPLEXERS; ap_daemons_limit = server_limit; ap_pid_fname = DEFAULT_PIDLOG; ap_lock_fname = DEFAULT_LOCKFILE; @@ -2608,6 +2750,13 @@ ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED; #endif + expire_timeout = DEFAULT_EXPIRE_TIMEOUT; + idle_timeout = DEFAULT_IDLE_TIMEOUT; + multiplexer_idle_timeout = DEFAULT_MULTIPLEXER_IDLE_TIMEOUT; + processor_wait_timeout = DEFAULT_PROCESSOR_WAIT_TIMEOUT; + processor_wait_steps = DEFAULT_PROCESSOR_WAIT_STEPS; + + apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); /* we need to know ServerLimit and ThreadLimit before we start processing @@ -2712,6 +2861,7 @@ server_env_image->control->num = 0; +/* for (i = 0; i < tmp_server_limit; i++) { SENV[i].processor_id = -1; @@ -2721,6 +2871,7 @@ SENV[i].input = -1; SENV[i].output = -1; } +*/ } return OK; @@ -2782,7 +2933,6 @@ { ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "Could not pass request to proper " "child, request will not be honoured."); - return DECLINED; } _DBG("doing longjmp",0); longjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer, 1); @@ -2859,32 +3009,37 @@ ap_rputs("
ID | PID | STATUS | TYPE | UID | " - "GID | CHROOT | INPUT | " + ap_rputs("||||||||
ID | PID | STATUS | SB STATUS | TYPE | UID | " + "GID | CHROOT | NICE | INPUT | " "OUTPUT | SOCK_FD | " "TOTAL PROCESSORS | MAX PROCESSORS | " - "IDLE PROCESSORS | MIN FREE PROCESSORS | IDLE PROCESSORS | MIN FREE PROCESSORS | " + "AVAIL | " + "\n", r); for (x = 0; x < NUM_CHILDS; x++) { senv = CHILD_INFO_TABLE[x].senv; - ap_rprintf(r, "
%3d | %5d | %8s | %12s | " - "%4d | %4d | %25s | %5d | " + ap_rprintf(r, "||||||||
%3d | %5d | %8s | %8s | %12s | " + "%4d | %4d | %25s | %3d | %5d | " "%6d | %7d | %d | %d | " - "%d | %d | %d | %d | %3d | \n", CHILD_INFO_TABLE[x].id, CHILD_INFO_TABLE[x].pid, child_status_string(CHILD_INFO_TABLE[x].status), + scoreboard_status_string(SCOREBOARD_STATUS(x)), child_type_string(CHILD_INFO_TABLE[x].type), senv == NULL ? -1 : senv->uid, senv == NULL ? -1 : senv->gid, senv == NULL ? NULL : senv->chroot, + senv == NULL ? 0 : senv->nice_lvl, senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->input, senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->output, CHILD_INFO_TABLE[x].sock_fd, total_processors(x), senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->max_processors, idle_processors(x), - senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->min_free_processors + senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->min_free_processors, + senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->availability ); } ap_rputs("