]> git.ipfire.org Git - thirdparty/squid.git/blame - src/peer_select.cc
fix WARNINGs
[thirdparty/squid.git] / src / peer_select.cc
CommitLineData
641941c0 1
062e2281 2/*
0c511722 3 * $Id: peer_select.cc,v 1.52 1998/04/22 16:24:14 rousskov Exp $
062e2281 4 *
5 * DEBUG: section 44 Peer Selection Algorithm
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
31
32#include "squid.h"
33
429fdbec 34const char *hier_strings[] =
35{
36 "NONE",
37 "DIRECT",
38 "SIBLING_HIT",
39 "PARENT_HIT",
40 "DEFAULT_PARENT",
41 "SINGLE_PARENT",
42 "FIRST_UP_PARENT",
43 "NO_PARENT_DIRECT",
44 "FIRST_PARENT_MISS",
45 "CLOSEST_PARENT_MISS",
46 "CLOSEST_DIRECT",
47 "NO_DIRECT_FAIL",
48 "SOURCE_FASTEST",
429fdbec 49 "ROUNDROBIN_PARENT",
b123df92 50#if SQUID_PEER_DIGEST
c127134a 51 "CACHE_DIGEST_HIT",
285f75ea 52 "NO_CACHE_DIGEST_DIRECT",
c127134a 53#endif
429fdbec 54 "INVALID CODE"
55};
56
85034133 57static struct {
75e88d56 58 int timeouts;
85034133 59} PeerStats;
062e2281 60
f9e5a344 61static char *DirectStr[] =
62{
63 "DIRECT_NO",
64 "DIRECT_MAYBE",
65 "DIRECT_YES"
7b665aeb 66};
75e88d56 67
f5b8bbc4 68static void peerSelectFoo(ps_state *);
de7ba689 69static void peerSelectCheckAS(ps_state *);
f5b8bbc4 70static void peerPingTimeout(void *data);
71static void peerSelectCallbackFail(ps_state * psstate);
b3264694 72static IRCB peerHandleIcpReply;
de7ba689 73static IPH peerSelectCheckASDone;
f5b8bbc4 74static void peerSelectStateFree(ps_state * psstate);
75static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *);
76static int peerCheckNetdbDirect(ps_state * psstate);
348b2031 77
78static void
79peerSelectStateFree(ps_state * psstate)
80{
81 if (psstate->acl_checklist) {
51fdcbd5 82 debug(44, 1) ("calling aclChecklistFree() from peerSelectStateFree\n");
348b2031 83 aclChecklistFree(psstate->acl_checklist);
84 }
85 requestUnlink(psstate->request);
8407afee 86 psstate->request = NULL;
87 cbdataFree(psstate);
348b2031 88}
062e2281 89
90int
91peerSelectIcpPing(request_t * request, int direct, StoreEntry * entry)
92{
7b665aeb 93 int n;
93775f90 94 if (entry == NULL)
95 return 0;
9fb13bb6 96 debug(44, 3) ("peerSelectIcpPing: %s\n", storeUrl(entry));
062e2281 97 if (entry->ping_status != PING_NONE)
98 return 0;
9bd6a36d 99 assert(direct != DIRECT_YES);
79a15e0a 100 if (!EBIT_TEST(entry->flag, HIERARCHICAL) && direct != DIRECT_NO)
062e2281 101 return 0;
79a15e0a 102 if (EBIT_TEST(entry->flag, KEY_PRIVATE) && !neighbors_do_private_keys)
062e2281 103 if (direct != DIRECT_NO)
104 return 0;
7b665aeb 105 n = neighborsCount(request);
f9e5a344 106 debug(44, 3) ("peerSelectIcpPing: counted %d neighbors\n", n);
7b665aeb 107 return n;
062e2281 108}
109
110
111peer *
112peerGetSomeParent(request_t * request, hier_code * code)
113{
114 peer *p;
de7ba689 115 debug(44, 3) ("peerGetSomeParent: called.\n");
062e2281 116 if ((p = getDefaultParent(request))) {
429fdbec 117 *code = DEFAULT_PARENT;
062e2281 118 return p;
119 }
062e2281 120 if ((p = getRoundRobinParent(request))) {
429fdbec 121 *code = ROUNDROBIN_PARENT;
062e2281 122 return p;
123 }
124 if ((p = getFirstUpParent(request))) {
429fdbec 125 *code = FIRSTUP_PARENT;
062e2281 126 return p;
127 }
128 return NULL;
129}
130
131void
93775f90 132peerSelect(request_t * request,
b6c0e933 133 StoreEntry * entry,
582b6456 134 PSC * callback,
135 PSC * fail_callback,
b6c0e933 136 void *callback_data)
75e88d56 137{
b6c0e933 138 ps_state *psstate = xcalloc(1, sizeof(ps_state));
86b389fc 139 if (entry)
9fb13bb6 140 debug(44, 3) ("peerSelect: %s\n", storeUrl(entry));
86b389fc 141 else
f9e5a344 142 debug(44, 3) ("peerSelect: %s\n", RequestMethodStr[request->method]);
3f6c0fb2 143 cbdataAdd(psstate, MEM_NONE);
b6c0e933 144 psstate->request = requestLink(request);
145 psstate->entry = entry;
146 psstate->callback = callback;
147 psstate->fail_callback = fail_callback;
148 psstate->callback_data = callback_data;
39edba21 149#if SQUID_PEER_DIGEST
150 request->hier.peer_select_start = current_time;
151#endif
395b813e 152 cbdataLock(callback_data);
153 peerSelectCheckAS(psstate);
de7ba689 154}
155
156static void
157peerSelectCheckASDone(const ipcache_addrs * ia, void *data)
158{
159 ps_state *psstate = data;
b6c0e933 160 peerSelectFoo(psstate);
75e88d56 161}
162
de7ba689 163static void
164peerSelectCheckAS(ps_state * psstate)
165{
166 request_t *request = psstate->request;
167
168/* XXXX Just a quick hack to get the destination address to the
169 * ipcache, because peerSelectIcpPing requires non-blocking ACL
170 * check.
171 * We should handle AS acl's differently than cache_host ones. */
172
173 ipcache_nbgethostbyname(request->host,
174 peerSelectCheckASDone,
175 psstate);
176 return;
177}
178
179
75e88d56 180static void
181peerCheckNeverDirectDone(int answer, void *data)
182{
b6c0e933 183 ps_state *psstate = data;
348b2031 184 psstate->acl_checklist = NULL;
a3d5953d 185 debug(44, 3) ("peerCheckNeverDirectDone: %d\n", answer);
b6c0e933 186 psstate->never_direct = answer ? 1 : -1;
187 peerSelectFoo(psstate);
75e88d56 188}
189
190static void
191peerCheckAlwaysDirectDone(int answer, void *data)
192{
b6c0e933 193 ps_state *psstate = data;
348b2031 194 psstate->acl_checklist = NULL;
a3d5953d 195 debug(44, 3) ("peerCheckAlwaysDirectDone: %d\n", answer);
7b665aeb 196 psstate->always_direct = answer ? 1 : -1;
b6c0e933 197 peerSelectFoo(psstate);
75e88d56 198}
199
93775f90 200static void
b6c0e933 201peerSelectCallback(ps_state * psstate, peer * p)
93775f90 202{
b6c0e933 203 StoreEntry *entry = psstate->entry;
8407afee 204 void *data = psstate->callback_data;
b6c0e933 205 if (entry) {
9fb13bb6 206 debug(44, 3) ("peerSelectCallback: %s\n", storeUrl(entry));
b6c0e933 207 if (entry->ping_status == PING_WAITING)
208 eventDelete(peerPingTimeout, psstate);
209 entry->ping_status = PING_DONE;
210 }
ee1679df 211 psstate->icp.stop = current_time;
8407afee 212 if (cbdataValid(data))
365e5b34 213 psstate->callback(p, data);
8407afee 214 cbdataUnlock(data);
348b2031 215 peerSelectStateFree(psstate);
93775f90 216}
217
218static void
b6c0e933 219peerSelectCallbackFail(ps_state * psstate)
93775f90 220{
b6c0e933 221 request_t *request = psstate->request;
8407afee 222 void *data = psstate->callback_data;
9fb13bb6 223 const char *url = psstate->entry ? storeUrl(psstate->entry) : urlCanonical(request, NULL);
a3d5953d 224 debug(44, 1) ("Failed to select source for '%s'\n", url);
225 debug(44, 1) (" always_direct = %d\n", psstate->always_direct);
226 debug(44, 1) (" never_direct = %d\n", psstate->never_direct);
227 debug(44, 1) (" timeout = %d\n", psstate->icp.timeout);
8407afee 228 if (cbdataValid(data))
365e5b34 229 psstate->fail_callback(NULL, data);
8407afee 230 cbdataUnlock(data);
348b2031 231 peerSelectStateFree(psstate);
063347b4 232 /* XXX When this happens, the client request just hangs */
93775f90 233}
234
b3264694 235static int
236peerCheckNetdbDirect(ps_state * psstate)
237{
238 peer *p = psstate->closest_parent_miss;
239 int myrtt;
240 int myhops;
241 if (p == NULL)
242 return 0;
243 myrtt = netdbHostRtt(psstate->request->host);
437e2060 244 debug(44, 3) ("peerCheckNetdbDirect: MY RTT = %d\n", myrtt);
245 debug(44, 3) ("peerCheckNetdbDirect: closest_parent_miss RTT = %d\n",
b3264694 246 psstate->icp.p_rtt);
247 if (myrtt && myrtt < psstate->icp.p_rtt)
248 return 1;
249 myhops = netdbHostHops(psstate->request->host);
437e2060 250 debug(44, 3) ("peerCheckNetdbDirect: MY hops = %d\n", myhops);
251 debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_hops = %d\n",
b3264694 252 Config.minDirectHops);
253 if (myhops && myhops <= Config.minDirectHops)
254 return 1;
255 return 0;
256}
257
93775f90 258static void
b6c0e933 259peerSelectFoo(ps_state * psstate)
062e2281 260{
261 peer *p;
262 hier_code code;
b6c0e933 263 StoreEntry *entry = psstate->entry;
264 request_t *request = psstate->request;
75e88d56 265 int direct;
7b665aeb 266 debug(44, 3) ("peerSelectFoo: '%s %s'\n",
b6c0e933 267 RequestMethodStr[request->method],
268 request->host);
eeeb6107 269 if (psstate->never_direct == 0 && Config.accessList.NeverDirect) {
348b2031 270 psstate->acl_checklist = aclChecklistCreate(
271 Config.accessList.NeverDirect,
75e88d56 272 request,
273 request->client_addr,
274 NULL, /* user agent */
348b2031 275 NULL); /* ident */
276 aclNBCheck(psstate->acl_checklist,
75e88d56 277 peerCheckNeverDirectDone,
b6c0e933 278 psstate);
75e88d56 279 return;
b6c0e933 280 } else if (psstate->never_direct > 0) {
75e88d56 281 direct = DIRECT_NO;
eeeb6107 282 } else if (psstate->always_direct == 0 && Config.accessList.AlwaysDirect) {
348b2031 283 psstate->acl_checklist = aclChecklistCreate(
284 Config.accessList.AlwaysDirect,
75e88d56 285 request,
286 request->client_addr,
287 NULL, /* user agent */
348b2031 288 NULL); /* ident */
289 aclNBCheck(psstate->acl_checklist,
75e88d56 290 peerCheckAlwaysDirectDone,
b6c0e933 291 psstate);
75e88d56 292 return;
b6c0e933 293 } else if (psstate->always_direct > 0) {
75e88d56 294 direct = DIRECT_YES;
295 } else {
296 direct = DIRECT_MAYBE;
297 }
7b665aeb 298 debug(44, 3) ("peerSelectFoo: direct = %s\n", DirectStr[direct]);
062e2281 299 if (direct == DIRECT_YES) {
7b665aeb 300 debug(44, 3) ("peerSelectFoo: DIRECT\n");
a4394ebd 301 hierarchyNote(&request->hier, DIRECT, &psstate->icp, request->host);
b6c0e933 302 peerSelectCallback(psstate, NULL);
062e2281 303 return;
304 }
a369131d 305 psstate->single_parent = getSingleParent(request);
a369131d 306 if (psstate->single_parent != NULL) {
307 debug(44, 3) ("peerSelect: found single parent, skipping ICP query\n");
308 } else if (peerSelectIcpPing(request, direct, entry)) {
9bd6a36d 309 assert(entry->ping_status == PING_NONE);
b123df92 310#if SQUID_PEER_DIGEST
26b164ac 311 /* which algorithm to use? */
312 if (squid_random() & 1) {
0c511722 313 debug(44, 2) ("peerSelect: Using Cache Digest\n");
b123df92 314 request->hier.alg = PEER_SA_DIGEST;
26b164ac 315 if (1 /* global_digested_peer_count */)
316 p = neighborsDigestSelect(request, entry);
2920225f 317 /* update counters */
318 statHistCount(&Counter.cd.peer_choice_count, request->hier.n_choices);
319 statHistCount(&Counter.cd.peer_ichoice_count, request->hier.n_ichoices);
320 code = DIRECT;
26b164ac 321 switch (request->hier.cd_lookup) {
322 case LOOKUP_HIT:
b123df92 323 assert(p);
c127134a 324 code = CACHE_DIGEST_HIT;
0c511722 325 debug(44, 2) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
c127134a 326 hierarchyNote(&request->hier, code, &psstate->icp, p->host);
26b164ac 327 peerSelectCallback(psstate, p); /* @?@: p used to be NULL */
328 return;
329 case LOOKUP_MISS:
285f75ea 330 code = NO_CACHE_DIGEST_DIRECT;
26b164ac 331 /* fall through */
332 case LOOKUP_NONE:
333 /* go direct */
0c511722 334 debug(44, 2) ("peerSelect: %s/%s\n", hier_strings[code], request->host);
26b164ac 335 hierarchyNote(&request->hier, code, &psstate->icp, request->host);
c127134a 336 peerSelectCallback(psstate, NULL);
b123df92 337 return;
26b164ac 338 default:
339 assert(0); /* invalid lookup code */
c127134a 340 }
26b164ac 341 assert(0); /* never reached */
c127134a 342 } else {
b123df92 343 request->hier.alg = PEER_SA_ICP;
c127134a 344#endif
345 debug(44, 3) ("peerSelect: Doing ICP pings\n");
346 psstate->icp.start = current_time;
347 psstate->icp.n_sent = neighborsUdpPing(request,
348 entry,
349 peerHandleIcpReply,
b6c0e933 350 psstate,
c127134a 351 &psstate->icp.n_replies_expected);
352 if (psstate->icp.n_sent == 0)
353 debug(44, 0) ("WARNING: neighborsUdpPing returned 0\n");
354 if (psstate->icp.n_replies_expected > 0) {
355 entry->ping_status = PING_WAITING;
356 eventAdd("peerPingTimeout",
357 peerPingTimeout,
358 psstate,
359 Config.neighborTimeout);
360 return;
361 }
b123df92 362#if SQUID_PEER_DIGEST
062e2281 363 }
c127134a 364#endif
062e2281 365 }
de7ba689 366 debug(44, 3) ("peerSelectFoo: After peerSelectIcpPing.\n");
b3264694 367 if (peerCheckNetdbDirect(psstate)) {
368 code = CLOSEST_DIRECT;
369 debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], request->host);
370 hierarchyNote(&request->hier, code, &psstate->icp, request->host);
371 peerSelectCallback(psstate, NULL);
a200bbd2 372 } else if ((p = psstate->closest_parent_miss) != NULL) {
b3264694 373 code = CLOSEST_PARENT_MISS;
374 debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
375 hierarchyNote(&request->hier, code, &psstate->icp, p->host);
376 peerSelectCallback(psstate, p);
a200bbd2 377 } else if ((p = psstate->first_parent_miss) != NULL) {
429fdbec 378 code = FIRST_PARENT_MISS;
a3d5953d 379 debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
a4394ebd 380 hierarchyNote(&request->hier, code, &psstate->icp, p->host);
b6c0e933 381 peerSelectCallback(psstate, p);
a369131d 382 } else if ((p = psstate->single_parent)) {
383 code = SINGLE_PARENT;
384 debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
385 hierarchyNote(&request->hier, code, &psstate->icp, p->host);
386 peerSelectCallback(psstate, p);
93775f90 387 } else if (direct != DIRECT_NO) {
429fdbec 388 code = DIRECT;
a3d5953d 389 debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], request->host);
a4394ebd 390 hierarchyNote(&request->hier, code, &psstate->icp, request->host);
b6c0e933 391 peerSelectCallback(psstate, NULL);
93775f90 392 } else if ((p = peerGetSomeParent(request, &code))) {
a3d5953d 393 debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
a4394ebd 394 hierarchyNote(&request->hier, code, &psstate->icp, p->host);
b6c0e933 395 peerSelectCallback(psstate, p);
93775f90 396 } else {
429fdbec 397 code = NO_DIRECT_FAIL;
a4394ebd 398 hierarchyNote(&request->hier, code, &psstate->icp, NULL);
b6c0e933 399 peerSelectCallbackFail(psstate);
062e2281 400 }
401}
402
d9586c3c 403static void
93775f90 404peerPingTimeout(void *data)
062e2281 405{
b6c0e933 406 ps_state *psstate = data;
407 StoreEntry *entry = psstate->entry;
86b389fc 408 if (entry)
9fb13bb6 409 debug(44, 3) ("peerPingTimeout: '%s'\n", storeUrl(entry));
93775f90 410 entry->ping_status = PING_TIMEOUT;
75e88d56 411 PeerStats.timeouts++;
b6c0e933 412 psstate->icp.timeout = 1;
413 peerSelectFoo(psstate);
85034133 414}
415
416void
417peerSelectInit(void)
418{
75e88d56 419 memset(&PeerStats, '\0', sizeof(PeerStats));
9bdbfe33 420 assert(sizeof(hier_strings) == (HIER_MAX + 1) * sizeof(char *));
062e2281 421}
93775f90 422
b3264694 423static void
424peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
425{
426 int rtt;
427 int hops;
428 if (Config.onoff.query_icmp) {
79a15e0a 429 if (header->flags & ICP_FLAG_SRC_RTT) {
b3264694 430 rtt = header->pad & 0xFFFF;
431 hops = (header->pad >> 16) & 0xFFFF;
432 if (rtt > 0 && rtt < 0xFFFF)
433 netdbUpdatePeer(ps->request, p, rtt, hops);
434 if (rtt && (ps->icp.p_rtt == 0 || rtt < ps->icp.p_rtt)) {
435 ps->closest_parent_miss = p;
436 ps->icp.p_rtt = rtt;
437 }
438 }
439 }
440 /* if closest-only is set, the don't allow FIRST_PARENT_MISS */
79a15e0a 441 if (EBIT_TEST(p->options, NEIGHBOR_CLOSEST_ONLY))
b3264694 442 return;
443 /* set FIRST_MISS if thre is no CLOSEST parent */
444 if (ps->closest_parent_miss != NULL)
445 return;
446 rtt = tvSubMsec(ps->icp.start, current_time) / p->weight;
447 if (ps->icp.w_rtt == 0 || rtt < ps->icp.w_rtt) {
448 ps->first_parent_miss = p;
449 ps->icp.w_rtt = rtt;
450 }
451}
93775f90 452
b6c0e933 453static void
b3264694 454peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
93775f90 455{
b6c0e933 456 ps_state *psstate = data;
b3264694 457 icp_opcode op = header->opcode;
b6c0e933 458 request_t *request = psstate->request;
f9e5a344 459 debug(44, 3) ("peerHandleIcpReply: %s %s\n",
27cd7235 460 icp_opcode_str[op],
9fb13bb6 461 storeUrl(psstate->entry));
26b164ac 462#if SQUID_PEER_DIGEST
463 /* do cd lookup to count false misses */
3ab66981 464 if (p && request)
465 peerNoteDigestLookup(request, p,
466 peerDigestLookup(p, request, psstate->entry));
26b164ac 467#endif
b6c0e933 468 psstate->icp.n_recv++;
27cd7235 469 if (op == ICP_MISS || op == ICP_DECHO) {
b3264694 470 if (type == PEER_PARENT)
471 peerIcpParentMiss(p, header, psstate);
a7c05555 472 } else if (op == ICP_HIT) {
a4394ebd 473 hierarchyNote(&request->hier,
429fdbec 474 type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT,
b6c0e933 475 &psstate->icp,
93775f90 476 p->host);
b6c0e933 477 peerSelectCallback(psstate, p);
93775f90 478 return;
27cd7235 479 } else if (op == ICP_SECHO) {
a4394ebd 480 hierarchyNote(&request->hier,
429fdbec 481 SOURCE_FASTEST,
b6c0e933 482 &psstate->icp,
483 request->host);
4f92c80c 484 peerSelectCallback(psstate, NULL);
93775f90 485 return;
486 }
b6c0e933 487 if (psstate->icp.n_recv < psstate->icp.n_replies_expected)
93775f90 488 return;
b6c0e933 489 peerSelectFoo(psstate);
93775f90 490}