diff --git a/include/common/hathreads.h b/include/common/hathreads.h index 20d0c0479..9946d5116 100644 --- a/include/common/hathreads.h +++ b/include/common/hathreads.h @@ -150,6 +150,7 @@ enum lock_label { PROXY_LOCK, SERVER_LOCK, UPDATED_SERVERS_LOCK, + LBPRM_LOCK, SIGNALS_LOCK, LOCK_LABELS }; @@ -236,7 +237,7 @@ static inline void show_lock_stats() const char *labels[LOCK_LABELS] = {"THREAD_SYNC", "FDTAB", "FDCACHE", "FD", "POLL", "TASK_RQ", "TASK_WQ", "POOL", "LISTENER", "LISTENER_QUEUE", "PROXY", "SERVER", - "UPDATED_SERVERS", "SIGNALS" }; + "UPDATED_SERVERS", "LBPRM", "SIGNALS" }; int lbl; for (lbl = 0; lbl < LOCK_LABELS; lbl++) { diff --git a/include/proto/lb_map.h b/include/proto/lb_map.h index 061273d3f..cf7349ef4 100644 --- a/include/proto/lb_map.h +++ b/include/proto/lb_map.h @@ -26,8 +26,6 @@ #include #include -void map_set_server_status_down(struct server *srv); -void map_set_server_status_up(struct server *srv); void recalc_server_map(struct proxy *px); void init_server_map(struct proxy *p); struct server *map_get_server_rr(struct proxy *px, struct server *srvtoavoid); diff --git a/include/types/backend.h b/include/types/backend.h index 446ac2af1..68d312538 100644 --- a/include/types/backend.h +++ b/include/types/backend.h @@ -23,6 +23,8 @@ #define _TYPES_BACKEND_H #include +#include + #include #include #include @@ -145,6 +147,9 @@ struct lbprm { struct lb_fwlc fwlc; struct lb_chash chash; struct lb_fas fas; +#ifdef USE_THREAD + HA_SPINLOCK_T lock; +#endif /* Call backs for some actions. Any of them may be NULL (thus should be ignored). */ void (*update_server_eweight)(struct server *); /* to be called after eweight change */ void (*set_server_status_up)(struct server *); /* to be called after status changes to UP */ diff --git a/include/types/lb_map.h b/include/types/lb_map.h index 4c12089d4..38e26af64 100644 --- a/include/types/lb_map.h +++ b/include/types/lb_map.h @@ -25,13 +25,9 @@ #include #include -/* values for map.state */ -#define LB_MAP_RECALC (1 << 0) - struct lb_map { struct server **srv; /* the server map used to apply weights */ int rr_idx; /* next server to be elected in round robin mode */ - int state; /* LB_MAP_RECALC */ }; #endif /* _TYPES_LB_MAP_H */ diff --git a/src/backend.c b/src/backend.c index d17635ff5..23b85ce6b 100644 --- a/src/backend.c +++ b/src/backend.c @@ -99,6 +99,10 @@ static unsigned int gen_hash(const struct proxy* px, const char* key, unsigned l * this. * This functions is designed to be called before server's weight and state * commit so it uses 'next' weight and states values. + * + * threads: this is the caller responsibility to lock data. For now, this + * function is called from lb modules, so it should be ok. But if you need to + * call it from another place, be careful (and update this comment). */ void recount_servers(struct proxy *px) { @@ -129,6 +133,10 @@ void recount_servers(struct proxy *px) /* This function simply updates the backend's tot_weight and tot_used values * after servers weights have been updated. It is designed to be used after * recount_servers() or equivalent. + * + * threads: this is the caller responsibility to lock data. For now, this + * function is called from lb modules, so it should be ok. But if you need to + * call it from another place, be careful (and update this comment). */ void update_backend_weight(struct proxy *px) { @@ -233,7 +241,7 @@ static struct server *get_server_uh(struct proxy *px, char *uri, int uri_len) return map_get_server_hash(px, hash); } -/* +/* * This function tries to find a running server for the proxy following * the URL parameter hash method. It looks for a specific parameter in the * URL and hashes it to compute the server ID. This is useful to optimize @@ -503,7 +511,7 @@ static struct server *get_server_rch(struct stream *s) else return map_get_server_hash(px, hash); } - + /* * This function applies the load-balancing algorithm to the stream, as * defined by the backend it is assigned to. The stream is then marked as @@ -579,6 +587,7 @@ int assign_server(struct stream *s) s->target = &srv->obj_type; } else if (s->be->lbprm.algo & BE_LB_KIND) { + /* we must check if we have at least one server available */ if (!s->be->lbprm.tot_weight) { err = SRV_STATUS_NOSRV; diff --git a/src/cfgparse.c b/src/cfgparse.c index e765fdb1a..dd4900953 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -8434,6 +8434,7 @@ out_uri_auth_compat: } break; } + SPIN_INIT(&curproxy->lbprm.lock); if (curproxy->options & PR_O_LOGASAP) curproxy->to_log &= ~LW_BYTES; diff --git a/src/haproxy.c b/src/haproxy.c index 5710cc26e..30ac15737 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2129,6 +2129,7 @@ void deinit(void) p0 = p; p = p->next; + SPIN_DESTROY(&p0->lbprm.lock); SPIN_DESTROY(&p0->lock); free(p0); }/* end while(p) */ diff --git a/src/lb_chash.c b/src/lb_chash.c index f368b684e..32d7d1d11 100644 --- a/src/lb_chash.c +++ b/src/lb_chash.c @@ -118,7 +118,7 @@ static void chash_set_server_status_down(struct server *srv) struct proxy *p = srv->proxy; if (!srv_lb_status_changed(srv)) - return; + return; if (srv_willbe_usable(srv)) goto out_update_state; @@ -169,7 +169,7 @@ static void chash_set_server_status_up(struct server *srv) struct proxy *p = srv->proxy; if (!srv_lb_status_changed(srv)) - return; + return; if (!srv_willbe_usable(srv)) goto out_update_state; @@ -364,14 +364,19 @@ struct server *chash_get_next_server(struct proxy *p, struct server *srvtoavoid) srv = avoided = NULL; avoided_node = NULL; + SPIN_LOCK(LBPRM_LOCK, &p->lbprm.lock); if (p->srv_act) root = &p->lbprm.chash.act; - else if (p->lbprm.fbck) - return p->lbprm.fbck; + else if (p->lbprm.fbck) { + srv = p->lbprm.fbck; + goto out; + } else if (p->srv_bck) root = &p->lbprm.chash.bck; - else - return NULL; + else { + srv = NULL; + goto out; + } stop = node = p->lbprm.chash.last; do { @@ -415,6 +420,8 @@ struct server *chash_get_next_server(struct proxy *p, struct server *srvtoavoid) p->lbprm.chash.last = avoided_node; } + out: + SPIN_UNLOCK(LBPRM_LOCK, &p->lbprm.lock); return srv; } diff --git a/src/lb_fas.c b/src/lb_fas.c index f8e739b12..db292db8d 100644 --- a/src/lb_fas.c +++ b/src/lb_fas.c @@ -63,8 +63,11 @@ static void fas_srv_reposition(struct server *s) { if (!s->lb_tree) return; + + SPIN_LOCK(LBPRM_LOCK, &s->proxy->lbprm.lock); fas_dequeue_srv(s); fas_queue_srv(s); + SPIN_UNLOCK(LBPRM_LOCK, &s->proxy->lbprm.lock); } /* This function updates the server trees according to server 's new @@ -111,7 +114,7 @@ static void fas_set_server_status_down(struct server *srv) fas_dequeue_srv(srv); fas_remove_from_tree(srv); -out_update_backend: + out_update_backend: /* check/update tot_used, tot_weight */ update_backend_weight(p); out_update_state: @@ -274,14 +277,19 @@ struct server *fas_get_next_server(struct proxy *p, struct server *srvtoavoid) srv = avoided = NULL; + SPIN_LOCK(LBPRM_LOCK, &p->lbprm.lock); if (p->srv_act) node = eb32_first(&p->lbprm.fas.act); - else if (p->lbprm.fbck) - return p->lbprm.fbck; + else if (p->lbprm.fbck) { + srv = p->lbprm.fbck; + goto out; + } else if (p->srv_bck) node = eb32_first(&p->lbprm.fas.bck); - else - return NULL; + else { + srv = NULL; + goto out; + } while (node) { /* OK, we have a server. However, it may be saturated, in which @@ -304,7 +312,8 @@ struct server *fas_get_next_server(struct proxy *p, struct server *srvtoavoid) if (!srv) srv = avoided; - + out: + SPIN_UNLOCK(LBPRM_LOCK, &p->lbprm.lock); return srv; } diff --git a/src/lb_fwlc.c b/src/lb_fwlc.c index 589031236..8bd3ac22c 100644 --- a/src/lb_fwlc.c +++ b/src/lb_fwlc.c @@ -55,8 +55,11 @@ static void fwlc_srv_reposition(struct server *s) { if (!s->lb_tree) return; + + SPIN_LOCK(LBPRM_LOCK, &s->proxy->lbprm.lock); fwlc_dequeue_srv(s); fwlc_queue_srv(s); + SPIN_UNLOCK(LBPRM_LOCK, &s->proxy->lbprm.lock); } /* This function updates the server trees according to server 's new @@ -266,14 +269,19 @@ struct server *fwlc_get_next_server(struct proxy *p, struct server *srvtoavoid) srv = avoided = NULL; + SPIN_LOCK(LBPRM_LOCK, &p->lbprm.lock); if (p->srv_act) node = eb32_first(&p->lbprm.fwlc.act); - else if (p->lbprm.fbck) - return p->lbprm.fbck; + else if (p->lbprm.fbck) { + srv = p->lbprm.fbck; + goto out; + } else if (p->srv_bck) node = eb32_first(&p->lbprm.fwlc.bck); - else - return NULL; + else { + srv = NULL; + goto out; + } while (node) { /* OK, we have a server. However, it may be saturated, in which @@ -296,7 +304,8 @@ struct server *fwlc_get_next_server(struct proxy *p, struct server *srvtoavoid) if (!srv) srv = avoided; - + out: + SPIN_UNLOCK(LBPRM_LOCK, &p->lbprm.lock); return srv; } diff --git a/src/lb_fwrr.c b/src/lb_fwrr.c index e273a279a..fe2777df9 100644 --- a/src/lb_fwrr.c +++ b/src/lb_fwrr.c @@ -315,7 +315,7 @@ static void fwrr_queue_srv(struct server *s) struct fwrr_group *grp; grp = (s->flags & SRV_F_BACKUP) ? &p->lbprm.fwrr.bck : &p->lbprm.fwrr.act; - + /* Delay everything which does not fit into the window and everything * which does not fit into the theorical new window. */ @@ -327,7 +327,7 @@ static void fwrr_queue_srv(struct server *s) s->npos >= grp->curr_weight + grp->next_weight) { /* put into next tree, and readjust npos in case we could * finally take this back to current. */ - s->npos -= grp->curr_weight; + HA_ATOMIC_SUB(&s->npos, grp->curr_weight); fwrr_queue_by_weight(grp->next, s); } else { @@ -359,7 +359,7 @@ static inline void fwrr_get_srv_next(struct server *s) &s->proxy->lbprm.fwrr.bck : &s->proxy->lbprm.fwrr.act; - s->npos += grp->curr_weight; + HA_ATOMIC_ADD(&s->npos, grp->curr_weight); } /* prepares a server when it was marked down */ @@ -414,7 +414,7 @@ static struct server *fwrr_get_server_from_group(struct fwrr_group *grp) node = eb32_first(&grp->curr); s = eb32_entry(node, struct server, lb_node); - + if (!node || s->npos > grp->curr_pos) { /* either we have no server left, or we have a hole */ struct eb32_node *node2; @@ -442,20 +442,20 @@ static inline void fwrr_update_position(struct fwrr_group *grp, struct server *s /* first time ever for this server */ s->lpos = grp->curr_pos; s->npos = grp->curr_pos + grp->next_weight / s->cur_eweight; - s->rweight += grp->next_weight % s->cur_eweight; + HA_ATOMIC_ADD(&s->rweight, (grp->next_weight % s->cur_eweight)); if (s->rweight >= s->cur_eweight) { - s->rweight -= s->cur_eweight; - s->npos++; + HA_ATOMIC_SUB(&s->rweight, s->cur_eweight); + HA_ATOMIC_ADD(&s->npos, 1); } } else { s->lpos = s->npos; - s->npos += grp->next_weight / s->cur_eweight; - s->rweight += grp->next_weight % s->cur_eweight; + HA_ATOMIC_ADD(&s->npos, (grp->next_weight / s->cur_eweight)); + HA_ATOMIC_ADD(&s->rweight, (grp->next_weight % s->cur_eweight)); if (s->rweight >= s->cur_eweight) { - s->rweight -= s->cur_eweight; - s->npos++; + HA_ATOMIC_SUB(&s->rweight, s->cur_eweight); + HA_ATOMIC_ADD(&s->npos, 1); } } } @@ -470,14 +470,19 @@ struct server *fwrr_get_next_server(struct proxy *p, struct server *srvtoavoid) struct fwrr_group *grp; int switched; + SPIN_LOCK(LBPRM_LOCK, &p->lbprm.lock); if (p->srv_act) grp = &p->lbprm.fwrr.act; - else if (p->lbprm.fbck) - return p->lbprm.fbck; + else if (p->lbprm.fbck) { + srv = p->lbprm.fbck; + goto out; + } else if (p->srv_bck) grp = &p->lbprm.fwrr.bck; - else - return NULL; + else { + srv = NULL; + goto out; + } switched = 0; avoided = NULL; @@ -558,6 +563,8 @@ struct server *fwrr_get_next_server(struct proxy *p, struct server *srvtoavoid) } while (full); } } + out: + SPIN_UNLOCK(LBPRM_LOCK, &p->lbprm.lock); return srv; } diff --git a/src/lb_map.c b/src/lb_map.c index fef16ac2e..028e85ba0 100644 --- a/src/lb_map.c +++ b/src/lb_map.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,7 @@ static void map_set_server_status_down(struct server *srv) /* FIXME: could be optimized since we know what changed */ recount_servers(p); update_backend_weight(p); - p->lbprm.map.state |= LB_MAP_RECALC; + recalc_server_map(p); out_update_state: srv_lb_commit_status(srv); } @@ -56,7 +57,7 @@ static void map_set_server_status_up(struct server *srv) /* FIXME: could be optimized since we know what changed */ recount_servers(p); update_backend_weight(p); - p->lbprm.map.state |= LB_MAP_RECALC; + recalc_server_map(p); out_update_state: srv_lb_commit_status(srv); } @@ -73,7 +74,6 @@ void recalc_server_map(struct proxy *px) switch (px->lbprm.tot_used) { case 0: /* no server */ - px->lbprm.map.state &= ~LB_MAP_RECALC; return; default: tot = px->lbprm.tot_weight; @@ -113,7 +113,7 @@ void recalc_server_map(struct proxy *px) break; } - cur->wscore += cur->next_eweight; + HA_ATOMIC_ADD(&cur->wscore, cur->next_eweight); v = (cur->wscore + tot) / tot; /* result between 0 and 3 */ if (best == NULL || v > max) { max = v; @@ -122,9 +122,8 @@ void recalc_server_map(struct proxy *px) } } px->lbprm.map.srv[o] = best; - best->wscore -= tot; + HA_ATOMIC_ADD(&best->wscore, tot); } - px->lbprm.map.state &= ~LB_MAP_RECALC; } /* This function is responsible of building the server MAP for map-based LB @@ -193,7 +192,6 @@ void init_server_map(struct proxy *p) p->lbprm.map.srv = 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); @@ -210,11 +208,11 @@ 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); + SPIN_LOCK(LBPRM_LOCK, &px->lbprm.lock); + if (px->lbprm.tot_weight == 0) { + avoided = NULL; + goto out; + } if (px->lbprm.map.rr_idx < 0 || px->lbprm.map.rr_idx >= px->lbprm.tot_weight) px->lbprm.map.rr_idx = 0; @@ -241,6 +239,8 @@ struct server *map_get_server_rr(struct proxy *px, struct server *srvtoavoid) if (avoided) px->lbprm.map.rr_idx = avoididx; + out: + SPIN_UNLOCK(LBPRM_LOCK, &px->lbprm.lock); /* return NULL or srvtoavoid if found */ return avoided; } @@ -255,10 +255,6 @@ struct server *map_get_server_hash(struct proxy *px, unsigned int hash) { if (px->lbprm.tot_weight == 0) return NULL; - - if (px->lbprm.map.state & LB_MAP_RECALC) - recalc_server_map(px); - return px->lbprm.map.srv[hash % px->lbprm.tot_weight]; }