/* * $Id: delay_pools.c,v 1.34.2.1 2007/08/31 14:08:52 hno Exp $ * * DEBUG: section 77 Delay Pools * AUTHOR: David Luyer * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from * the Internet community; see the CONTRIBUTORS file for full * details. Many organizations have provided support for Squid's * development; see the SPONSORS file for full details. Squid is * Copyrighted (C) 2001 by the Regents of the University of * California; see the COPYRIGHT file for full details. Squid * incorporates software developed and/or copyrighted by other * sources; see the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "config.h" #if DELAY_POOLS #include "squid.h" struct _class1DelayPool { int class; int aggregate; }; #define IND_MAP_SZ 256 struct _class2DelayPool { int class; int aggregate; /* OK: -1 is terminator. individual[255] is always host 255. */ /* 255 entries + 1 terminator byte */ unsigned char individual_map[IND_MAP_SZ]; unsigned char individual_255_used; /* 256 entries */ int individual[IND_MAP_SZ]; }; #define NET_MAP_SZ 256 #define C3_IND_SZ (NET_MAP_SZ*IND_MAP_SZ) struct _class3DelayPool { int class; int aggregate; /* OK: -1 is terminator. network[255] is always host 255. */ /* 255 entries + 1 terminator byte */ unsigned char network_map[NET_MAP_SZ]; unsigned char network_255_used; /* 256 entries */ int network[256]; /* 256 sets of (255 entries + 1 terminator byte) */ unsigned char individual_map[NET_MAP_SZ][IND_MAP_SZ]; /* Pack this into one bit per net */ unsigned char individual_255_used[32]; /* largest entry = (255<<8)+255 = 65535 */ int individual[C3_IND_SZ]; }; typedef struct _class1DelayPool class1DelayPool; typedef struct _class2DelayPool class2DelayPool; typedef struct _class3DelayPool class3DelayPool; union _delayPool { class1DelayPool *class1; class2DelayPool *class2; class3DelayPool *class3; }; typedef union _delayPool delayPool; static delayPool *delay_data = NULL; static char *delay_no_delay; static time_t delay_pools_last_update = 0; static hash_table *delay_id_ptr_hash = NULL; static long memory_used = 0; static OBJH delayPoolStats; static unsigned int delayIdPtrHash(const void *key, unsigned int n) { /* Hashes actual POINTER VALUE. * Assumes <= 256 hash buckets & even hash size. * Assumes the most variation in pointers to inside * medium size objects occurs in the 2nd and 3rd * least significant bytes. */ const char *ptr = (char *) &key; #if SIZEOF_VOID_P == 4 return (ptr[1] ^ ptr[2]) & (n - 1); #elif SIZEOF_VOID_P == 8 #if WORDS_BIGENDIAN return (ptr[5] ^ ptr[6]) & (n - 1); #else return (ptr[1] ^ ptr[2]) & (n - 1); #endif #else #error What kind of a sick architecture are you on anyway? #endif } static int delayIdPtrHashCmp(const void *a, const void *b) { /* * Compare POINTER VALUE. * Note, we can't subtract void pointers, but we don't need * to anyway. All we need is a test for equality. */ return a != b; } void delayPoolsInit(void) { delay_pools_last_update = getCurrentTime(); delay_no_delay = xcalloc(1, Squid_MaxFD); cachemgrRegister("delay", "Delay Pool Levels", delayPoolStats, 0, 1); } void delayInitDelayData(unsigned short pools) { if (!pools) return; delay_data = xcalloc(pools, sizeof(*delay_data)); memory_used += pools * sizeof(*delay_data); delay_id_ptr_hash = hash_create(delayIdPtrHashCmp, 256, delayIdPtrHash); } static void delayIdZero(void *hlink) { hash_link *h = hlink; delay_id *id = (delay_id *) h->key; *id = 0; xfree(h); memory_used -= sizeof(*h); } void delayFreeDelayData(unsigned short pools) { if (!delay_id_ptr_hash) return; safe_free(delay_data); memory_used -= pools * sizeof(*delay_data); hashFreeItems(delay_id_ptr_hash, delayIdZero); hashFreeMemory(delay_id_ptr_hash); delay_id_ptr_hash = NULL; } void delayRegisterDelayIdPtr(delay_id * loc) { hash_link *lnk; if (!delay_id_ptr_hash) return; if (*loc == 0) return; lnk = xmalloc(sizeof(hash_link)); memory_used += sizeof(hash_link); lnk->key = (char *) loc; hash_join(delay_id_ptr_hash, lnk); } void delayUnregisterDelayIdPtr(delay_id * loc) { hash_link *lnk; if (!delay_id_ptr_hash) return; /* * If we went through a reconfigure, then all the delay_id's * got set to zero, and they were removed from our hash * table. */ if (*loc == 0) return; lnk = hash_lookup(delay_id_ptr_hash, loc); assert(lnk); hash_remove_link(delay_id_ptr_hash, lnk); xxfree(lnk); memory_used -= sizeof(*lnk); } void delayCreateDelayPool(unsigned short pool, u_char class) { switch (class) { case 1: delay_data[pool].class1 = xmalloc(sizeof(class1DelayPool)); delay_data[pool].class1->class = 1; memory_used += sizeof(class1DelayPool); break; case 2: delay_data[pool].class2 = xmalloc(sizeof(class2DelayPool)); delay_data[pool].class1->class = 2; memory_used += sizeof(class2DelayPool); break; case 3: delay_data[pool].class3 = xmalloc(sizeof(class3DelayPool)); delay_data[pool].class1->class = 3; memory_used += sizeof(class3DelayPool); break; default: assert(0); } } void delayInitDelayPool(unsigned short pool, u_char class, delaySpecSet * rates) { /* delaySetSpec may be pointer to partial structure so MUST pass by * reference. */ switch (class) { case 1: delay_data[pool].class1->aggregate = (int) (((double) rates->aggregate.max_bytes * Config.Delay.initial) / 100); break; case 2: delay_data[pool].class2->aggregate = (int) (((double) rates->aggregate.max_bytes * Config.Delay.initial) / 100); delay_data[pool].class2->individual_map[0] = 255; delay_data[pool].class2->individual_255_used = 0; break; case 3: delay_data[pool].class3->aggregate = (int) (((double) rates->aggregate.max_bytes * Config.Delay.initial) / 100); delay_data[pool].class3->network_map[0] = 255; delay_data[pool].class3->network_255_used = 0; memset(&delay_data[pool].class3->individual_255_used, '\0', sizeof(delay_data[pool].class3->individual_255_used)); break; default: assert(0); } } void delayFreeDelayPool(unsigned short pool) { /* this is a union - and all free() cares about is the pointer location */ switch (delay_data[pool].class1->class) { case 1: memory_used -= sizeof(class1DelayPool); break; case 2: memory_used -= sizeof(class2DelayPool); break; case 3: memory_used -= sizeof(class3DelayPool); break; default: debug(77, 1) ("delayFreeDelayPool: bad class %d\n", delay_data[pool].class1->class); } safe_free(delay_data[pool].class1); } void delaySetNoDelay(int fd) { delay_no_delay[fd] = 1; } void delayClearNoDelay(int fd) { delay_no_delay[fd] = 0; } int delayIsNoDelay(int fd) { return delay_no_delay[fd]; } static delay_id delayId(unsigned short pool, unsigned short position) { return (pool << 16) | position; } delay_id delayClient(clientHttpRequest * http) { request_t *r; aclCheck_t ch; int i; int j; unsigned int host; unsigned short pool, position; unsigned char class, net; assert(http); r = http->request; memset(&ch, '\0', sizeof(ch)); #if FOLLOW_X_FORWARDED_FOR if (Config.onoff.delay_pool_uses_indirect_client) { ch.src_addr = r->indirect_client_addr; } else #endif /* FOLLOW_X_FORWARDED_FOR */ ch.src_addr = r->client_addr; ch.my_addr = r->my_addr; ch.my_port = r->my_port; ch.conn = http->conn; ch.request = r; if (r->client_addr.s_addr == INADDR_BROADCAST) { debug(77, 2) ("delayClient: WARNING: Called with 'allones' address, ignoring\n"); return delayId(0, 0); } for (pool = 0; pool < Config.Delay.pools; pool++) { if (aclCheckFast(Config.Delay.access[pool], &ch)) break; } if (pool == Config.Delay.pools) return delayId(0, 0); class = Config.Delay.class[pool]; if (class == 0) return delayId(0, 0); if (class == 1) return delayId(pool + 1, 0); if (class == 2) { host = ntohl(ch.src_addr.s_addr) & 0xff; if (host == 255) { if (!delay_data[pool].class2->individual_255_used) { delay_data[pool].class2->individual_255_used = 1; delay_data[pool].class2->individual[IND_MAP_SZ - 1] = (int) (((double) Config.Delay.rates[pool]->individual.max_bytes * Config.Delay.initial) / 100); } return delayId(pool + 1, 255); } for (i = 0; i < IND_MAP_SZ; i++) { if (delay_data[pool].class2->individual_map[i] == host) break; if (delay_data[pool].class2->individual_map[i] == 255) { delay_data[pool].class2->individual_map[i] = host; assert(i < (IND_MAP_SZ - 1)); delay_data[pool].class2->individual_map[i + 1] = 255; delay_data[pool].class2->individual[i] = (int) (((double) Config.Delay.rates[pool]->individual.max_bytes * Config.Delay.initial) / 100); break; } } return delayId(pool + 1, i); } /* class == 3 */ host = ntohl(ch.src_addr.s_addr) & 0xffff; net = host >> 8; host &= 0xff; if (net == 255) { i = 255; if (!delay_data[pool].class3->network_255_used) { delay_data[pool].class3->network_255_used = 1; delay_data[pool].class3->network[255] = (int) (((double) Config.Delay.rates[pool]->network.max_bytes * Config.Delay.initial) / 100); delay_data[pool].class3->individual_map[i][0] = 255; } } else { for (i = 0; i < NET_MAP_SZ; i++) { if (delay_data[pool].class3->network_map[i] == net) break; if (delay_data[pool].class3->network_map[i] == 255) { delay_data[pool].class3->network_map[i] = net; delay_data[pool].class3->individual_map[i][0] = 255; assert(i < (NET_MAP_SZ - 1)); delay_data[pool].class3->network_map[i + 1] = 255; delay_data[pool].class3->network[i] = (int) (((double) Config.Delay.rates[pool]->network.max_bytes * Config.Delay.initial) / 100); break; } } } position = i << 8; if (host == 255) { position |= 255; if (!(delay_data[pool].class3->individual_255_used[i / 8] & (1 << (i % 8)))) { delay_data[pool].class3->individual_255_used[i / 8] |= (1 << (i % 8)); delay_data[pool].class3->individual[position] = (int) (((double) Config.Delay.rates[pool]->individual.max_bytes * Config.Delay.initial) / 100); } return delayId(pool + 1, position); } assert(i < NET_MAP_SZ); for (j = 0; j < IND_MAP_SZ; j++) { if (delay_data[pool].class3->individual_map[i][j] == host) { position |= j; break; } if (delay_data[pool].class3->individual_map[i][j] == 255) { delay_data[pool].class3->individual_map[i][j] = host; assert(j < (IND_MAP_SZ - 1)); delay_data[pool].class3->individual_map[i][j + 1] = 255; position |= j; delay_data[pool].class3->individual[position] = (int) (((double) Config.Delay.rates[pool]->individual.max_bytes * Config.Delay.initial) / 100); break; } } return delayId(pool + 1, position); } static void delayUpdateClass1(class1DelayPool * class1, delaySpecSet * rates, int incr) { /* delaySetSpec may be pointer to partial structure so MUST pass by * reference. */ if (rates->aggregate.restore_bps != -1 && (class1->aggregate += rates->aggregate.restore_bps * incr) > rates->aggregate.max_bytes) class1->aggregate = rates->aggregate.max_bytes; } static void delayUpdateClass2(class2DelayPool * class2, delaySpecSet * rates, int incr) { int restore_bytes; unsigned char i; /* depends on 255 + 1 = 0 */ /* delaySetSpec may be pointer to partial structure so MUST pass by * reference. */ if (rates->aggregate.restore_bps != -1 && (class2->aggregate += rates->aggregate.restore_bps * incr) > rates->aggregate.max_bytes) class2->aggregate = rates->aggregate.max_bytes; if ((restore_bytes = rates->individual.restore_bps) == -1) return; restore_bytes *= incr; /* i < IND_MAP_SZ is enforced by data type (unsigned chars are all < 256). * this loop starts at 0 or 255 and ends at 254 unless terminated earlier * by finding the end of the map. note as above that 255 + 1 = 0. */ for (i = (class2->individual_255_used ? 255 : 0);; i++) { if (i != 255 && class2->individual_map[i] == 255) return; if (class2->individual[i] != rates->individual.max_bytes && (class2->individual[i] += restore_bytes) > rates->individual.max_bytes) class2->individual[i] = rates->individual.max_bytes; if (i == 254) return; } } static void delayUpdateClass3(class3DelayPool * class3, delaySpecSet * rates, int incr) { int individual_restore_bytes, network_restore_bytes; int mpos; unsigned char i, j; /* depends on 255 + 1 = 0 */ /* delaySetSpec may be pointer to partial structure so MUST pass by * reference. */ if (rates->aggregate.restore_bps != -1 && (class3->aggregate += rates->aggregate.restore_bps * incr) > rates->aggregate.max_bytes) class3->aggregate = rates->aggregate.max_bytes; /* the following line deliberately uses &, not &&, in an if statement * to avoid conditional execution */ if (((network_restore_bytes = rates->network.restore_bps) == -1) & ((individual_restore_bytes = rates->individual.restore_bps) == -1)) return; individual_restore_bytes *= incr; network_restore_bytes *= incr; /* i < NET_MAP_SZ is enforced by data type (unsigned chars are all < 256). * this loop starts at 0 or 255 and ends at 254 unless terminated earlier * by finding the end of the map. note as above that 255 + 1 = 0. */ for (i = (class3->network_255_used ? 255 : 0);; ++i) { if (i != 255 && class3->network_map[i] == 255) return; if (individual_restore_bytes != -incr) { mpos = i << 8; /* this is not as simple as the outer loop as mpos doesn't wrap like * i and j do. so the net 255 increment is done as a separate special * case. the alternative would be overlapping a union of two chars on * top of a 16-bit unsigned int, but that wouldn't really be worth the * effort. */ for (j = 0;; ++j, ++mpos) { if (class3->individual_map[i][j] == 255) break; assert(mpos < C3_IND_SZ); if (class3->individual[mpos] != rates->individual.max_bytes && (class3->individual[mpos] += individual_restore_bytes) > rates->individual.max_bytes) class3->individual[mpos] = rates->individual.max_bytes; if (j == 254) break; } if (class3->individual_255_used[i / 8] & (1 << (i % 8))) { mpos |= 255; /* this will set mpos to network 255 */ assert(mpos < C3_IND_SZ); if (class3->individual[mpos] != rates->individual.max_bytes && (class3->individual[mpos] += individual_restore_bytes) > rates->individual.max_bytes) class3->individual[mpos] = rates->individual.max_bytes; } } if (network_restore_bytes != -incr && class3->network[i] != rates->network.max_bytes && (class3->network[i] += network_restore_bytes) > rates->network.max_bytes) class3->network[i] = rates->network.max_bytes; if (i == 254) return; } } void delayPoolsUpdate(void *unused) { int incr = squid_curtime - delay_pools_last_update; unsigned short i; unsigned char class; if (!Config.Delay.pools) return; if (incr < 1) return; delay_pools_last_update = squid_curtime; for (i = 0; i < Config.Delay.pools; i++) { class = Config.Delay.class[i]; if (!class) continue; switch (class) { case 1: delayUpdateClass1(delay_data[i].class1, Config.Delay.rates[i], incr); break; case 2: delayUpdateClass2(delay_data[i].class2, Config.Delay.rates[i], incr); break; case 3: delayUpdateClass3(delay_data[i].class3, Config.Delay.rates[i], incr); break; default: assert(0); } } } /* * this returns the number of bytes the client is permitted. it does not take * into account bytes already buffered - that is up to the caller. */ int delayBytesWanted(delay_id d, int min, int max) { unsigned short position = d & 0xFFFF; unsigned short pool = (d >> 16) - 1; unsigned char class = (pool == 0xFFFF) ? 0 : Config.Delay.class[pool]; int nbytes = max; switch (class) { case 0: break; case 1: if (Config.Delay.rates[pool]->aggregate.restore_bps != -1) nbytes = XMIN(nbytes, delay_data[pool].class1->aggregate); break; case 2: if (Config.Delay.rates[pool]->aggregate.restore_bps != -1) nbytes = XMIN(nbytes, delay_data[pool].class2->aggregate); if (Config.Delay.rates[pool]->individual.restore_bps != -1) nbytes = XMIN(nbytes, delay_data[pool].class2->individual[position]); break; case 3: if (Config.Delay.rates[pool]->aggregate.restore_bps != -1) nbytes = XMIN(nbytes, delay_data[pool].class3->aggregate); if (Config.Delay.rates[pool]->individual.restore_bps != -1) nbytes = XMIN(nbytes, delay_data[pool].class3->individual[position]); if (Config.Delay.rates[pool]->network.restore_bps != -1) nbytes = XMIN(nbytes, delay_data[pool].class3->network[position >> 8]); break; default: fatalf("delayBytesWanted: Invalid class %d\n", class); break; } nbytes = XMAX(min, nbytes); return nbytes; } /* * this records actual bytes received. always recorded, even if the * class is disabled - it's more efficient to just do it than to do all * the checks. */ void delayBytesIn(delay_id d, int qty) { unsigned short position = d & 0xFFFF; unsigned short pool = (d >> 16) - 1; unsigned char class; if (pool == 0xFFFF) return; class = Config.Delay.class[pool]; switch (class) { case 1: delay_data[pool].class1->aggregate -= qty; return; case 2: delay_data[pool].class2->aggregate -= qty; delay_data[pool].class2->individual[position] -= qty; return; case 3: delay_data[pool].class3->aggregate -= qty; delay_data[pool].class3->network[position >> 8] -= qty; delay_data[pool].class3->individual[position] -= qty; return; } fatalf("delayBytesWanted: Invalid class %d\n", class); assert(0); } int delayMostBytesWanted(const MemObject * mem, int max) { int i = 0; int found = 0; store_client *sc; dlink_node *node; for (node = mem->clients.head; node; node = node->next) { sc = (store_client *) node->data; i = delayBytesWanted(sc->delay_id, i, max); found = 1; } return found ? i : max; } delay_id delayMostBytesAllowed(const MemObject * mem, size_t * read_sz) { int j; int jmax = -1; store_client *sc; dlink_node *node; delay_id d = 0; for (node = mem->clients.head; node; node = node->next) { sc = (store_client *) node->data; j = delayBytesWanted(sc->delay_id, 0, INT_MAX); if (j > jmax) { jmax = j; d = sc->delay_id; } } if (jmax >= 0 && jmax < (int) *read_sz) { if (jmax == 0) jmax = 1; if (jmax > 1460) jmax = 1460; *read_sz = (size_t) jmax; } return d; } void delaySetStoreClient(store_client * sc, delay_id delay_id) { assert(sc != NULL); sc->delay_id = delay_id; delayRegisterDelayIdPtr(&sc->delay_id); } static void delayPoolStatsAg(StoreEntry * sentry, delaySpecSet * rate, int ag) { /* note - always pass delaySpecSet's by reference as may be incomplete */ if (rate->aggregate.restore_bps == -1) { storeAppendPrintf(sentry, "\tAggregate:\n\t\tDisabled.\n\n"); return; } storeAppendPrintf(sentry, "\tAggregate:\n"); storeAppendPrintf(sentry, "\t\tMax: %d\n", rate->aggregate.max_bytes); storeAppendPrintf(sentry, "\t\tRestore: %d\n", rate->aggregate.restore_bps); storeAppendPrintf(sentry, "\t\tCurrent: %d\n\n", ag); } static void delayPoolStats1(StoreEntry * sentry, unsigned short pool) { /* must be a reference only - partially malloc()d struct */ delaySpecSet *rate = Config.Delay.rates[pool]; storeAppendPrintf(sentry, "Pool: %d\n\tClass: 1\n\n", pool + 1); delayPoolStatsAg(sentry, rate, delay_data[pool].class1->aggregate); } static void delayPoolStats2(StoreEntry * sentry, unsigned short pool) { /* must be a reference only - partially malloc()d struct */ delaySpecSet *rate = Config.Delay.rates[pool]; class2DelayPool *class2 = delay_data[pool].class2; unsigned char shown = 0; unsigned int i; storeAppendPrintf(sentry, "Pool: %d\n\tClass: 2\n\n", pool + 1); delayPoolStatsAg(sentry, rate, class2->aggregate); if (rate->individual.restore_bps == -1) { storeAppendPrintf(sentry, "\tIndividual:\n\t\tDisabled.\n\n"); return; } storeAppendPrintf(sentry, "\tIndividual:\n"); storeAppendPrintf(sentry, "\t\tMax: %d\n", rate->individual.max_bytes); storeAppendPrintf(sentry, "\t\tRate: %d\n", rate->individual.restore_bps); storeAppendPrintf(sentry, "\t\tCurrent: "); for (i = 0; i < IND_MAP_SZ; i++) { if (class2->individual_map[i] == 255) break; storeAppendPrintf(sentry, "%d:%d ", class2->individual_map[i], class2->individual[i]); shown = 1; } if (class2->individual_255_used) { storeAppendPrintf(sentry, "%d:%d ", 255, class2->individual[255]); shown = 1; } if (!shown) storeAppendPrintf(sentry, "Not used yet."); storeAppendPrintf(sentry, "\n\n"); } static void delayPoolStats3(StoreEntry * sentry, unsigned short pool) { /* fully malloc()d struct in this case only */ delaySpecSet *rate = Config.Delay.rates[pool]; class3DelayPool *class3 = delay_data[pool].class3; unsigned char shown = 0; unsigned int i; unsigned int j; storeAppendPrintf(sentry, "Pool: %d\n\tClass: 3\n\n", pool + 1); delayPoolStatsAg(sentry, rate, class3->aggregate); if (rate->network.restore_bps == -1) { storeAppendPrintf(sentry, "\tNetwork:\n\t\tDisabled."); } else { storeAppendPrintf(sentry, "\tNetwork:\n"); storeAppendPrintf(sentry, "\t\tMax: %d\n", rate->network.max_bytes); storeAppendPrintf(sentry, "\t\tRate: %d\n", rate->network.restore_bps); storeAppendPrintf(sentry, "\t\tCurrent: "); for (i = 0; i < NET_MAP_SZ; i++) { if (class3->network_map[i] == 255) break; storeAppendPrintf(sentry, "%d:%d ", class3->network_map[i], class3->network[i]); shown = 1; } if (class3->network_255_used) { storeAppendPrintf(sentry, "%d:%d ", 255, class3->network[255]); shown = 1; } if (!shown) storeAppendPrintf(sentry, "Not used yet."); } storeAppendPrintf(sentry, "\n\n"); shown = 0; if (rate->individual.restore_bps == -1) { storeAppendPrintf(sentry, "\tIndividual:\n\t\tDisabled.\n\n"); return; } storeAppendPrintf(sentry, "\tIndividual:\n"); storeAppendPrintf(sentry, "\t\tMax: %d\n", rate->individual.max_bytes); storeAppendPrintf(sentry, "\t\tRate: %d\n", rate->individual.restore_bps); for (i = 0; i < NET_MAP_SZ; i++) { if (class3->network_map[i] == 255) break; storeAppendPrintf(sentry, "\t\tCurrent [Network %d]: ", class3->network_map[i]); shown = 1; for (j = 0; j < IND_MAP_SZ; j++) { if (class3->individual_map[i][j] == 255) break; storeAppendPrintf(sentry, "%d:%d ", class3->individual_map[i][j], class3->individual[(i << 8) | j]); } if (class3->individual_255_used[i / 8] & (1 << (i % 8))) { storeAppendPrintf(sentry, "%d:%d ", 255, class3->individual[(i << 8) | 255]); } storeAppendPrintf(sentry, "\n"); } if (class3->network_255_used) { storeAppendPrintf(sentry, "\t\tCurrent [Network 255]: "); shown = 1; for (j = 0; j < IND_MAP_SZ; j++) { if (class3->individual_map[255][j] == 255) break; storeAppendPrintf(sentry, "%d:%d ", class3->individual_map[255][j], class3->individual[(255 << 8) | j]); } if (class3->individual_255_used[255 / 8] & (1 << (255 % 8))) { storeAppendPrintf(sentry, "%d:%d ", 255, class3->individual[(255 << 8) | 255]); } storeAppendPrintf(sentry, "\n"); } if (!shown) storeAppendPrintf(sentry, "\t\tCurrent [All networks]: Not used yet.\n"); storeAppendPrintf(sentry, "\n"); } static void delayPoolStats(StoreEntry * sentry) { unsigned short i; storeAppendPrintf(sentry, "Delay pools configured: %d\n\n", Config.Delay.pools); for (i = 0; i < Config.Delay.pools; i++) { switch (Config.Delay.class[i]) { case 0: storeAppendPrintf(sentry, "Pool: %d\n\tClass: 0\n\n", i + 1); storeAppendPrintf(sentry, "\tMisconfigured pool.\n\n"); break; case 1: delayPoolStats1(sentry, i); break; case 2: delayPoolStats2(sentry, i); break; case 3: delayPoolStats3(sentry, i); break; default: assert(0); } } storeAppendPrintf(sentry, "Memory Used: %d bytes\n", (int) memory_used); } #endif