/* Check for pending connections at the backend, and assign some of them to * the server coming up. The server's weight is checked before being assigned * connections it may not be able to handle. The total number of transferred * connections is returned. */ int pendconn_grab_from_px(struct server *s) { struct pendconn *p; int maxconn, xferred = 0; if (!srv_currently_usable(s)) return 0; /* if this is a backup server and there are active servers or at * least another backup server was elected, then this one must * not dequeue requests from the proxy. */ if ((s->flags & SRV_F_BACKUP) && (s->proxy->srv_act || ((s != s->proxy->lbprm.fbck) && !(s->proxy->options & PR_O_USE_ALL_BK)))) return 0; HA_SPIN_LOCK(PROXY_LOCK, &s->proxy->lock); maxconn = srv_dynamic_maxconn(s); while ((p = pendconn_first(&s->proxy->pendconns))) { if (s->maxconn && s->served + xferred >= maxconn) break; __pendconn_unlink(p); p->target = s; task_wakeup(p->strm->task, TASK_WOKEN_RES); xferred++; } HA_SPIN_UNLOCK(PROXY_LOCK, &s->proxy->lock); return xferred; }
/* Manages a server's connection queue. This function will try to dequeue as * many pending streams as possible, and wake them up. */ void process_srv_queue(struct server *s) { struct proxy *p = s->proxy; int maxconn; HA_SPIN_LOCK(PROXY_LOCK, &p->lock); HA_SPIN_LOCK(SERVER_LOCK, &s->lock); maxconn = srv_dynamic_maxconn(s); while (s->served < maxconn) { int ret = pendconn_process_next_strm(s, p); if (!ret) break; } HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock); HA_SPIN_UNLOCK(PROXY_LOCK, &p->lock); }
/* * Manages a server's connection queue. This function will try to dequeue as * many pending sessions as possible, and wake them up. */ void process_srv_queue(struct server *s) { struct proxy *p = s->proxy; int maxconn; /* First, check if we can handle some connections queued at the proxy. We * will take as many as we can handle. */ maxconn = srv_dynamic_maxconn(s); while (s->served < maxconn) { struct session *sess = pendconn_get_next_sess(s, p); if (sess == NULL) break; task_wakeup(sess->task, TASK_WOKEN_RES); } }
/* Return next server from the FS tree in backend <p>. If the tree is empty, * return NULL. Saturated servers are skipped. */ struct server *fas_get_next_server(struct proxy *p, struct server *srvtoavoid) { struct server *srv, *avoided; struct eb32_node *node; srv = avoided = NULL; if (p->srv_act) node = eb32_first(&p->lbprm.fas.act); else if (p->lbprm.fbck) return p->lbprm.fbck; else if (p->srv_bck) node = eb32_first(&p->lbprm.fas.bck); else return NULL; while (node) { /* OK, we have a server. However, it may be saturated, in which * case we don't want to reconsider it for now, so we'll simply * skip it. Same if it's the server we try to avoid, in which * case we simply remember it for later use if needed. */ struct server *s; s = eb32_entry(node, struct server, lb_node); if (!s->maxconn || (!s->nbpend && s->served < srv_dynamic_maxconn(s))) { if (s != srvtoavoid) { srv = s; break; } avoided = s; } node = eb32_next(node); } if (!srv) srv = avoided; return srv; }
/* * This function tries to find a running server with free connection slots for * the proxy <px> following the round-robin method. * If any server is found, it will be returned and px->lbprm.map.rr_idx will be updated * to point to the next server. If no valid server is found, NULL is returned. */ struct server *map_get_server_rr(struct proxy *px, struct server *srvtoavoid) { int newidx, avoididx; struct server *srv, *avoided; if (px->lbprm.tot_weight == 0) return NULL; if (px->lbprm.map.state & LB_MAP_RECALC) recalc_server_map(px); if (px->lbprm.map.rr_idx < 0 || px->lbprm.map.rr_idx >= px->lbprm.tot_weight) px->lbprm.map.rr_idx = 0; newidx = px->lbprm.map.rr_idx; avoided = NULL; avoididx = 0; /* shut a gcc warning */ do { srv = px->lbprm.map.srv[newidx++]; if (!srv->maxconn || (!srv->nbpend && srv->served < srv_dynamic_maxconn(srv))) { /* make sure it is not the server we are try to exclude... */ if (srv != srvtoavoid) { px->lbprm.map.rr_idx = newidx; return srv; } avoided = srv; /* ...but remember that is was selected yet avoided */ avoididx = newidx; } if (newidx == px->lbprm.tot_weight) newidx = 0; } while (newidx != px->lbprm.map.rr_idx); if (avoided) px->lbprm.map.rr_idx = avoididx; /* return NULL or srvtoavoid if found */ return avoided; }
/* This function assigns a server to session <s> if required, and can add the * connection to either the assigned server's queue or to the proxy's queue. * If ->srv_conn is set, the session is first released from the server. * It may also be called with SN_DIRECT and/or SN_ASSIGNED though. It will * be called before any connection and after any retry or redispatch occurs. * * It is not allowed to call this function with a session in a queue. * * Returns : * * SRV_STATUS_OK if everything is OK. * SRV_STATUS_NOSRV if no server is available. objt_server(s->target) = NULL. * SRV_STATUS_QUEUED if the connection has been queued. * SRV_STATUS_FULL if the server(s) is/are saturated and the * connection could not be queued at the server's, * which may be NULL if we queue on the backend. * SRV_STATUS_INTERNAL for other unrecoverable errors. * */ int assign_server_and_queue(struct session *s) { struct pendconn *p; struct server *srv; int err; if (s->pend_pos) return SRV_STATUS_INTERNAL; err = SRV_STATUS_OK; if (!(s->flags & SN_ASSIGNED)) { struct server *prev_srv = objt_server(s->target); err = assign_server(s); if (prev_srv) { /* This session was previously assigned to a server. We have to * update the session's and the server's stats : * - if the server changed : * - set TX_CK_DOWN if txn.flags was TX_CK_VALID * - set SN_REDISP if it was successfully redispatched * - increment srv->redispatches and be->redispatches * - if the server remained the same : update retries. */ if (prev_srv != objt_server(s->target)) { if ((s->txn.flags & TX_CK_MASK) == TX_CK_VALID) { s->txn.flags &= ~TX_CK_MASK; s->txn.flags |= TX_CK_DOWN; } s->flags |= SN_REDISP; prev_srv->counters.redispatches++; s->be->be_counters.redispatches++; } else { prev_srv->counters.retries++; s->be->be_counters.retries++; } } } switch (err) { case SRV_STATUS_OK: /* we have SN_ASSIGNED set */ srv = objt_server(s->target); if (!srv) return SRV_STATUS_OK; /* dispatch or proxy mode */ /* If we already have a connection slot, no need to check any queue */ if (s->srv_conn == srv) return SRV_STATUS_OK; /* OK, this session already has an assigned server, but no * connection slot yet. Either it is a redispatch, or it was * assigned from persistence information (direct mode). */ if ((s->flags & SN_REDIRECTABLE) && srv->rdr_len) { /* server scheduled for redirection, and already assigned. We * don't want to go further nor check the queue. */ sess_change_server(s, srv); /* not really needed in fact */ return SRV_STATUS_OK; } /* We might have to queue this session if the assigned server is full. * We know we have to queue it into the server's queue, so if a maxqueue * is set on the server, we must also check that the server's queue is * not full, in which case we have to return FULL. */ if (srv->maxconn && (srv->nbpend || srv->served >= srv_dynamic_maxconn(srv))) { if (srv->maxqueue > 0 && srv->nbpend >= srv->maxqueue) return SRV_STATUS_FULL; p = pendconn_add(s); if (p) return SRV_STATUS_QUEUED; else return SRV_STATUS_INTERNAL; } /* OK, we can use this server. Let's reserve our place */ sess_change_server(s, srv); return SRV_STATUS_OK; case SRV_STATUS_FULL: /* queue this session into the proxy's queue */ p = pendconn_add(s); if (p) return SRV_STATUS_QUEUED; else return SRV_STATUS_INTERNAL; case SRV_STATUS_NOSRV: return err; case SRV_STATUS_INTERNAL: return err; default: return SRV_STATUS_INTERNAL; } }
/* Return next server from the current tree in backend <p>, or a server from * the init tree if appropriate. If both trees are empty, return NULL. * Saturated servers are skipped and requeued. */ struct server *fwrr_get_next_server(struct proxy *p, struct server *srvtoavoid) { struct server *srv, *full, *avoided; struct fwrr_group *grp; int switched; if (p->srv_act) grp = &p->lbprm.fwrr.act; else if (p->lbprm.fbck) return p->lbprm.fbck; else if (p->srv_bck) grp = &p->lbprm.fwrr.bck; else return NULL; switched = 0; avoided = NULL; full = NULL; /* NULL-terminated list of saturated servers */ while (1) { /* if we see an empty group, let's first try to collect weights * which might have recently changed. */ if (!grp->curr_weight) grp->curr_pos = grp->curr_weight = grp->next_weight; /* get first server from the "current" tree. When the end of * the tree is reached, we may have to switch, but only once. */ while (1) { srv = fwrr_get_server_from_group(grp); if (srv) break; if (switched) { if (avoided) { srv = avoided; break; } goto requeue_servers; } switched = 1; fwrr_switch_trees(grp); } /* OK, we have a server. However, it may be saturated, in which * case we don't want to reconsider it for now. We'll update * its position and dequeue it anyway, so that we can move it * to a better place afterwards. */ fwrr_update_position(grp, srv); fwrr_dequeue_srv(srv); grp->curr_pos++; if (!srv->maxconn || (!srv->nbpend && srv->served < srv_dynamic_maxconn(srv))) { /* make sure it is not the server we are trying to exclude... */ if (srv != srvtoavoid || avoided) break; avoided = srv; /* ...but remember that is was selected yet avoided */ } /* the server is saturated or avoided, let's chain it for later reinsertion */ srv->next_full = full; full = srv; } /* OK, we got the best server, let's update it */ fwrr_queue_srv(srv); requeue_servers: /* Requeue all extracted servers. If full==srv then it was * avoided (unsucessfully) and chained, omit it now. */ if (unlikely(full != NULL)) { if (switched) { /* the tree has switched, requeue all extracted servers * into "init", because their place was lost, and only * their weight matters. */ do { if (likely(full != srv)) fwrr_queue_by_weight(grp->init, full); full = full->next_full; } while (full); } else { /* requeue all extracted servers just as if they were consumed * so that they regain their expected place. */ do { if (likely(full != srv)) fwrr_queue_srv(full); full = full->next_full; } while (full); } } return srv; }