/* This function is responsible for building the trees in case of fast * weighted least-conns. It also sets p->lbprm.wdiv to the eweight to * uweight ratio. Both active and backup groups are initialized. */ void fas_init_server_tree(struct proxy *p) { struct server *srv; struct eb_root init_head = EB_ROOT; p->lbprm.set_server_status_up = fas_set_server_status_up; p->lbprm.set_server_status_down = fas_set_server_status_down; p->lbprm.update_server_eweight = fas_update_server_weight; p->lbprm.server_take_conn = fas_srv_reposition; p->lbprm.server_drop_conn = fas_srv_reposition; p->lbprm.wdiv = BE_WEIGHT_SCALE; for (srv = p->srv; srv; srv = srv->next) { srv->eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult; srv_lb_commit_status(srv); } recount_servers(p); update_backend_weight(p); p->lbprm.fas.act = init_head; p->lbprm.fas.bck = init_head; /* queue active and backup servers in two distinct groups */ for (srv = p->srv; srv; srv = srv->next) { if (!srv_is_usable(srv)) continue; srv->lb_tree = (srv->state & SRV_BACKUP) ? &p->lbprm.fas.bck : &p->lbprm.fas.act; fas_queue_srv(srv); } }
/* This function updates the server trees according to server <srv>'s new * state. It should be called when server <srv>'s status changes to up. * It is not important whether the server was already down or not. It is not * important either that the new state is completely UP (the caller may not * know all the variables of a server's state). This function will not change * the weight of a server which was already up. */ static void fwrr_set_server_status_up(struct server *srv) { struct proxy *p = srv->proxy; struct fwrr_group *grp; if (srv->state == srv->prev_state && srv->eweight == srv->prev_eweight) return; if (!srv_is_usable(srv->state, srv->eweight)) goto out_update_state; if (srv_is_usable(srv->prev_state, srv->prev_eweight)) /* server was already up */ goto out_update_backend; grp = (srv->state & SRV_BACKUP) ? &p->lbprm.fwrr.bck : &p->lbprm.fwrr.act; grp->next_weight += srv->eweight; if (srv->state & SRV_BACKUP) { p->lbprm.tot_wbck = p->lbprm.fwrr.bck.next_weight; p->srv_bck++; if (!(p->options & PR_O_USE_ALL_BK)) { if (!p->lbprm.fbck) { /* there was no backup server anymore */ p->lbprm.fbck = srv; } else { /* we may have restored a backup server prior to fbck, * in which case it should replace it. */ struct server *srv2 = srv; do { srv2 = srv2->next; } while (srv2 && (srv2 != p->lbprm.fbck)); if (srv2) p->lbprm.fbck = srv; } } } else { p->lbprm.tot_wact = p->lbprm.fwrr.act.next_weight; p->srv_act++; } /* note that eweight cannot be 0 here */ fwrr_get_srv(srv); srv->npos = grp->curr_pos + (grp->next_weight + grp->curr_weight - grp->curr_pos) / srv->eweight; fwrr_queue_srv(srv); out_update_backend: /* check/update tot_used, tot_weight */ update_backend_weight(p); out_update_state: srv->prev_state = srv->state; srv->prev_eweight = srv->eweight; }
/* This function updates the server trees according to server <srv>'s new * state. It should be called when server <srv>'s status changes to up. * It is not important whether the server was already down or not. It is not * important either that the new state is completely UP (the caller may not * know all the variables of a server's state). This function will not change * the weight of a server which was already up. */ static void fas_set_server_status_up(struct server *srv) { struct proxy *p = srv->proxy; if (!srv_lb_status_changed(srv)) return; if (!srv_is_usable(srv)) goto out_update_state; if (srv_was_usable(srv)) /* server was already up */ goto out_update_backend; if (srv->state & SRV_BACKUP) { srv->lb_tree = &p->lbprm.fas.bck; p->lbprm.tot_wbck += srv->eweight; p->srv_bck++; if (!(p->options & PR_O_USE_ALL_BK)) { if (!p->lbprm.fbck) { /* there was no backup server anymore */ p->lbprm.fbck = srv; } else { /* we may have restored a backup server prior to fbck, * in which case it should replace it. */ struct server *srv2 = srv; do { srv2 = srv2->next; } while (srv2 && (srv2 != p->lbprm.fbck)); if (srv2) p->lbprm.fbck = srv; } } } else { srv->lb_tree = &p->lbprm.fas.act; p->lbprm.tot_wact += srv->eweight; p->srv_act++; } /* note that eweight cannot be 0 here */ fas_queue_srv(srv); out_update_backend: /* check/update tot_used, tot_weight */ update_backend_weight(p); out_update_state: srv_lb_commit_status(srv); }
/* This function updates the server trees according to server <srv>'s new * state. It should be called when server <srv>'s status changes to down. * It is not important whether the server was already down or not. It is not * important either that the new state is completely down (the caller may not * know all the variables of a server's state). */ static void fwrr_set_server_status_down(struct server *srv) { struct proxy *p = srv->proxy; struct fwrr_group *grp; if (srv->state == srv->prev_state && srv->eweight == srv->prev_eweight) return; if (srv_is_usable(srv->state, srv->eweight)) goto out_update_state; if (!srv_is_usable(srv->prev_state, srv->prev_eweight)) /* server was already down */ goto out_update_backend; grp = (srv->state & SRV_BACKUP) ? &p->lbprm.fwrr.bck : &p->lbprm.fwrr.act; grp->next_weight -= srv->prev_eweight; if (srv->state & SRV_BACKUP) { p->lbprm.tot_wbck = p->lbprm.fwrr.bck.next_weight; p->srv_bck--; if (srv == p->lbprm.fbck) { /* we lost the first backup server in a single-backup * configuration, we must search another one. */ struct server *srv2 = p->lbprm.fbck; do { srv2 = srv2->next; } while (srv2 && !((srv2->state & SRV_BACKUP) && srv_is_usable(srv2->state, srv2->eweight))); p->lbprm.fbck = srv2; } } else { p->lbprm.tot_wact = p->lbprm.fwrr.act.next_weight; p->srv_act--; } fwrr_dequeue_srv(srv); fwrr_remove_from_tree(srv); out_update_backend: /* check/update tot_used, tot_weight */ update_backend_weight(p); out_update_state: srv->prev_state = srv->state; srv->prev_eweight = srv->eweight; }
/* This function must be called after an update to server <srv>'s effective * weight. It may be called after a state change too. */ static void fas_update_server_weight(struct server *srv) { int old_state, new_state; struct proxy *p = srv->proxy; if (!srv_lb_status_changed(srv)) return; /* If changing the server's weight changes its state, we simply apply * the procedures we already have for status change. If the state * remains down, the server is not in any tree, so it's as easy as * updating its values. If the state remains up with different weights, * there are some computations to perform to find a new place and * possibly a new tree for this server. */ old_state = srv_was_usable(srv); new_state = srv_is_usable(srv); if (!old_state && !new_state) { srv_lb_commit_status(srv); return; } else if (!old_state && new_state) { fas_set_server_status_up(srv); return; } else if (old_state && !new_state) { fas_set_server_status_down(srv); return; } if (srv->lb_tree) fas_dequeue_srv(srv); if (srv->state & SRV_BACKUP) { p->lbprm.tot_wbck += srv->eweight - srv->prev_eweight; srv->lb_tree = &p->lbprm.fas.bck; } else { p->lbprm.tot_wact += srv->eweight - srv->prev_eweight; srv->lb_tree = &p->lbprm.fas.act; } fas_queue_srv(srv); update_backend_weight(p); srv_lb_commit_status(srv); }
/* This function updates the server trees according to server <srv>'s new * state. It should be called when server <srv>'s status changes to down. * It is not important whether the server was already down or not. It is not * important either that the new state is completely down (the caller may not * know all the variables of a server's state). */ static void fas_set_server_status_down(struct server *srv) { struct proxy *p = srv->proxy; if (!srv_lb_status_changed(srv)) return; if (srv_is_usable(srv)) goto out_update_state; if (!srv_was_usable(srv)) /* server was already down */ goto out_update_backend; if (srv->state & SRV_BACKUP) { p->lbprm.tot_wbck -= srv->prev_eweight; p->srv_bck--; if (srv == p->lbprm.fbck) { /* we lost the first backup server in a single-backup * configuration, we must search another one. */ struct server *srv2 = p->lbprm.fbck; do { srv2 = srv2->next; } while (srv2 && !((srv2->state & SRV_BACKUP) && srv_is_usable(srv2))); p->lbprm.fbck = srv2; } } else { p->lbprm.tot_wact -= srv->prev_eweight; p->srv_act--; } fas_dequeue_srv(srv); fas_remove_from_tree(srv); out_update_backend: /* check/update tot_used, tot_weight */ update_backend_weight(p); out_update_state: srv_lb_commit_status(srv); }
/* This function is responsible for building the weight trees in case of fast * weighted round-robin. It also sets p->lbprm.wdiv to the eweight to uweight * ratio. Both active and backup groups are initialized. */ void fwrr_init_server_groups(struct proxy *p) { struct server *srv; struct eb_root init_head = EB_ROOT; p->lbprm.set_server_status_up = fwrr_set_server_status_up; p->lbprm.set_server_status_down = fwrr_set_server_status_down; p->lbprm.update_server_eweight = fwrr_update_server_weight; p->lbprm.wdiv = BE_WEIGHT_SCALE; for (srv = p->srv; srv; srv = srv->next) { srv->prev_eweight = srv->eweight = srv->uweight * BE_WEIGHT_SCALE; srv->prev_state = srv->state; } recount_servers(p); update_backend_weight(p); /* prepare the active servers group */ p->lbprm.fwrr.act.curr_pos = p->lbprm.fwrr.act.curr_weight = p->lbprm.fwrr.act.next_weight = p->lbprm.tot_wact; p->lbprm.fwrr.act.curr = p->lbprm.fwrr.act.t0 = p->lbprm.fwrr.act.t1 = init_head; p->lbprm.fwrr.act.init = &p->lbprm.fwrr.act.t0; p->lbprm.fwrr.act.next = &p->lbprm.fwrr.act.t1; /* prepare the backup servers group */ p->lbprm.fwrr.bck.curr_pos = p->lbprm.fwrr.bck.curr_weight = p->lbprm.fwrr.bck.next_weight = p->lbprm.tot_wbck; p->lbprm.fwrr.bck.curr = p->lbprm.fwrr.bck.t0 = p->lbprm.fwrr.bck.t1 = init_head; p->lbprm.fwrr.bck.init = &p->lbprm.fwrr.bck.t0; p->lbprm.fwrr.bck.next = &p->lbprm.fwrr.bck.t1; /* queue active and backup servers in two distinct groups */ for (srv = p->srv; srv; srv = srv->next) { if (!srv_is_usable(srv->state, srv->eweight)) continue; fwrr_queue_by_weight((srv->state & SRV_BACKUP) ? p->lbprm.fwrr.bck.init : p->lbprm.fwrr.act.init, srv); } }
/* This function updates the map according to server <srv>'s new state */ static void map_set_server_status_up(struct server *srv) { struct proxy *p = srv->proxy; if (srv->state == srv->prev_state && srv->eweight == srv->prev_eweight) return; if (!srv_is_usable(srv->state, srv->eweight)) goto out_update_state; /* FIXME: could be optimized since we know what changed */ recount_servers(p); update_backend_weight(p); p->lbprm.map.state |= LB_MAP_RECALC; out_update_state: srv->prev_state = srv->state; srv->prev_eweight = srv->eweight; }
/* This function is responsible of building the server MAP for map-based LB * algorithms, allocating the map, and setting p->lbprm.wmult to the GCD of the * weights if applicable. It should be called only once per proxy, at config * time. */ void init_server_map(struct proxy *p) { struct server *srv; int pgcd; int act, bck; p->lbprm.set_server_status_up = map_set_server_status_up; p->lbprm.set_server_status_down = map_set_server_status_down; p->lbprm.update_server_eweight = NULL; if (!p->srv) return; /* We will factor the weights to reduce the table, * using Euclide's largest common divisor algorithm. * Since we may have zero weights, we have to first * find a non-zero weight server. */ pgcd = 1; srv = p->srv; while (srv && !srv->uweight) srv = srv->next; if (srv) { pgcd = srv->uweight; /* note: cannot be zero */ while (pgcd > 1 && (srv = srv->next)) { int w = srv->uweight; while (w) { int t = pgcd % w; pgcd = w; w = t; } } } /* It is sometimes useful to know what factor to apply * to the backend's effective weight to know its real * weight. */ p->lbprm.wmult = pgcd; act = bck = 0; for (srv = p->srv; srv; srv = srv->next) { srv->eweight = srv->uweight / pgcd; srv->prev_eweight = srv->eweight; srv->prev_state = srv->state; if (srv->state & SRV_BACKUP) bck += srv->eweight; else act += srv->eweight; } /* this is the largest map we will ever need for this servers list */ if (act < bck) act = bck; if (!act) act = 1; p->lbprm.map.srv = (struct server **)calloc(act, sizeof(struct server *)); /* recounts servers and their weights */ p->lbprm.map.state = LB_MAP_RECALC; recount_servers(p); update_backend_weight(p); recalc_server_map(p); }
/* This function must be called after an update to server <srv>'s effective * weight. It may be called after a state change too. */ static void fwrr_update_server_weight(struct server *srv) { int old_state, new_state; struct proxy *p = srv->proxy; struct fwrr_group *grp; if (srv->state == srv->prev_state && srv->eweight == srv->prev_eweight) return; /* If changing the server's weight changes its state, we simply apply * the procedures we already have for status change. If the state * remains down, the server is not in any tree, so it's as easy as * updating its values. If the state remains up with different weights, * there are some computations to perform to find a new place and * possibly a new tree for this server. */ old_state = srv_is_usable(srv->prev_state, srv->prev_eweight); new_state = srv_is_usable(srv->state, srv->eweight); if (!old_state && !new_state) { srv->prev_state = srv->state; srv->prev_eweight = srv->eweight; return; } else if (!old_state && new_state) { fwrr_set_server_status_up(srv); return; } else if (old_state && !new_state) { fwrr_set_server_status_down(srv); return; } grp = (srv->state & SRV_BACKUP) ? &p->lbprm.fwrr.bck : &p->lbprm.fwrr.act; grp->next_weight = grp->next_weight - srv->prev_eweight + srv->eweight; p->lbprm.tot_wact = p->lbprm.fwrr.act.next_weight; p->lbprm.tot_wbck = p->lbprm.fwrr.bck.next_weight; if (srv->lb_tree == grp->init) { fwrr_dequeue_srv(srv); fwrr_queue_by_weight(grp->init, srv); } else if (!srv->lb_tree) { /* FIXME: server was down. This is not possible right now but * may be needed soon for slowstart or graceful shutdown. */ fwrr_dequeue_srv(srv); fwrr_get_srv(srv); srv->npos = grp->curr_pos + (grp->next_weight + grp->curr_weight - grp->curr_pos) / srv->eweight; fwrr_queue_srv(srv); } else { /* The server is either active or in the next queue. If it's * still in the active queue and it has not consumed all of its * places, let's adjust its next position. */ fwrr_get_srv(srv); if (srv->eweight > 0) { int prev_next = srv->npos; int step = grp->next_weight / srv->eweight; srv->npos = srv->lpos + step; srv->rweight = 0; if (srv->npos > prev_next) srv->npos = prev_next; if (srv->npos < grp->curr_pos + 2) srv->npos = grp->curr_pos + step; } else { /* push it into the next tree */ srv->npos = grp->curr_pos + grp->curr_weight; } fwrr_dequeue_srv(srv); fwrr_queue_srv(srv); } update_backend_weight(p); srv->prev_state = srv->state; srv->prev_eweight = srv->eweight; }