/* * bwstat.c * * Copyright (c) 2003 Marius Aamodt Eriksen * All rights reserved. * * $Id: bwstat.c,v 1.15 2003/07/29 05:58:58 marius Exp $ */ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #ifdef HAVE_SYS_TIME_H #include #endif /* HAVE_SYS_TIME_H */ #include #include #ifdef HAVE_STDINT_H #include #endif /* HAVE_STDINT_H */ #if defined(HAVE_TIME_H) && defined(TIME_WITH_SYS_TIME) #include #endif /* defined(HAVE_TIME_H) && defined(TIME_WITH_SYS_TIME) */ #include #include "util.h" #include "bwstat.h" static TAILQ_HEAD(bwstathead, bwstat) statq; static uint winsz; static double difftv(struct timeval *, struct timeval *); static void _bwstat_update(struct bwstat_data *, size_t); #define INITTV(tv, curtv) do { \ if (!timerisset(&(tv))) \ (tv) = (curtv); \ } while (0) /* * XXX should winsz be per-update? */ int bwstat_init(uint xwinsz) { struct bwstat *bs; winsz = xwinsz; TAILQ_INIT(&statq); /* First entry is the totals. */ if ((bs = bwstat_new()) == NULL) return (-1); return (0); } struct bwstat * bwstat_new(void) { struct bwstat *bs; if ((bs = calloc(1, sizeof(*bs))) == NULL) return (NULL); bs->pts = 1; TAILQ_INSERT_TAIL(&statq, bs, next); return (bs); } struct bwstat * bwstat_free(struct bwstat *bs) { TAILQ_REMOVE(&statq, bs, next); return (NULL); } struct bwstat * bwstat_gettot(void) { return (TAILQ_FIRST(&statq)); } void bwstat_update(struct bwstat *bs, size_t len, short which) { struct bwstat *bstot = TAILQ_FIRST(&statq); if (bs != NULL) _bwstat_update(&bs->data[which], len); _bwstat_update(&bstot->data[which], len); } static void _bwstat_update(struct bwstat_data *bsd, size_t len) { struct timeval curtv; double elap, elapwin; gettimeofday(&curtv, NULL); INITTV(bsd->tv, curtv); INITTV(bsd->wintv, curtv); elap = difftv(&curtv, &bsd->tv); elapwin = difftv(&curtv, &bsd->wintv); /* XXX Window follows. */ if (bsd->winbytes == 0 && bsd->winrate > 0) bsd->winbytes = (1.0 * bsd->winrate) * elapwin; bsd->bytes += len; bsd->winbytes += len; if (elap == 0.0 || elapwin == 0.0) return; bsd->rate = (1.0 * bsd->bytes) / elap; bsd->winrate = (1.0 * bsd->winbytes) / elapwin; /* * Reset window; XXX make this a sliding window wrt a * timeline; keep average, but reduce #bytes? */ if (bsd->winbytes >= winsz) { gettimeofday(&bsd->wintv, NULL); bsd->winbytes = 0; } } /* * Return required delay for bs for direction which. */ struct timeval * bwstat_getdelay(struct bwstat *bs, size_t *len, uint lim, short which) { uint rate = 0, npts = 0, ent; int ncli = 0, pool = 0, xent; double delay; static struct timeval tv; struct bwstathead poolq; struct bwstat *xbs, *bstot = TAILQ_FIRST(&statq); //uint initent; size_t xlen = *len; if (*len == 0) return (NULL); memset(&tv, 0, sizeof(tv)); TAILQ_INIT(&poolq); rate = bstot->data[which].winrate; if (rate <= lim) return (NULL); xbs = bstot; while ((xbs = TAILQ_NEXT(xbs, next)) != NULL) { ncli++; npts += xbs->pts; TAILQ_INSERT_TAIL(&poolq, xbs, qnext); } if (ncli == 0) return (NULL); /* Entitlement per point */ //initent = ent = lim / npts; ent = lim / npts; if (ent == 0) ; /* * XXX we have surpassed our * granularity. */ /* * Sprinkle some bandwidth: increase the value of a point * until everyone is satisfied. */ do { /* Take from the poor ... */ TAILQ_FOREACH(xbs, &poolq, qnext) if (xbs->data[which].winrate < ent * xbs->pts) { pool += ent * xbs->pts - xbs->data[which].winrate; ncli--; npts -= xbs->pts; TAILQ_REMOVE(&poolq, xbs, qnext); } /* And give to the rich ... */ if (ncli > 0) { xent = pool / npts; if (xent <= 0) break; TAILQ_FOREACH(xbs, &poolq, qnext) if (xbs->data[which].winrate > ent * xbs->pts) pool -= xent * xbs->pts; ent += xent; } } while (pool > 0 && ncli > 0); /* * This is the case of a client that is not using its limit. * We reset ent in this case. The rest will adjust itself * over time. */ if (ent * bs->pts > lim) ent = lim / bs->pts; if (bs->data[which].winrate > ent * bs->pts) delay = (1.0 * *len) / (1.0 * ent * bs->pts); else delay = 0.0; /* if (delay > bs->tsmooth) { */ if ((*len = ent * bs->pts * bs->tsmooth) == 0) { *len = bs->lsmooth; delay = (1.0 * *len) / (1.0 * ent * bs->pts); } else delay = bs->tsmooth; /* } */ if (*len > xlen) { *len = xlen; delay = (1.0 * *len) / (1.0 * ent * bs->pts); } /* XXX */ if (delay < 0.0) return (NULL); tv.tv_sec = delay; tv.tv_usec = (delay - tv.tv_sec) * 1000000L; return (&tv); } static double difftv(struct timeval *tv0, struct timeval *tv1) { struct timeval diff_tv; timersub(tv0, tv1, &diff_tv); return (diff_tv.tv_sec + (diff_tv.tv_usec / 1000000.0)); }