]>
Commit | Line | Data |
---|---|---|
641941c0 | 1 | |
062e2281 | 2 | /* |
4a7a3d56 | 3 | * $Id: peer_select.cc,v 1.147 2007/04/30 16:56:09 wessels Exp $ |
062e2281 | 4 | * |
5 | * DEBUG: section 44 Peer Selection Algorithm | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
062e2281 | 10 | * |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
062e2281 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
062e2281 | 34 | */ |
35 | ||
36 | #include "squid.h" | |
a553a5a3 | 37 | #include "event.h" |
b24880fe | 38 | #include "PeerSelectState.h" |
e6ccf245 | 39 | #include "Store.h" |
40 | #include "ICP.h" | |
528b2c61 | 41 | #include "HttpRequest.h" |
8000a965 | 42 | #include "ACLChecklist.h" |
924f73bc | 43 | #include "htcp.h" |
b6b6f466 | 44 | #include "forward.h" |
062e2281 | 45 | |
429fdbec | 46 | const char *hier_strings[] = |
62e76326 | 47 | { |
48 | "NONE", | |
49 | "DIRECT", | |
50 | "SIBLING_HIT", | |
51 | "PARENT_HIT", | |
52 | "DEFAULT_PARENT", | |
53 | "SINGLE_PARENT", | |
54 | "FIRST_UP_PARENT", | |
55 | "FIRST_PARENT_MISS", | |
56 | "CLOSEST_PARENT_MISS", | |
57 | "CLOSEST_PARENT", | |
58 | "CLOSEST_DIRECT", | |
59 | "NO_DIRECT_FAIL", | |
60 | "SOURCE_FASTEST", | |
61 | "ROUNDROBIN_PARENT", | |
6cfa8966 | 62 | #if USE_CACHE_DIGESTS |
62e76326 | 63 | "CD_PARENT_HIT", |
64 | "CD_SIBLING_HIT", | |
afd88fbe | 65 | #endif |
66 | #if USE_CARP | |
62e76326 | 67 | "CARP", |
c127134a | 68 | #endif |
62e76326 | 69 | "ANY_PARENT", |
70 | "INVALID CODE" | |
71 | }; | |
429fdbec | 72 | |
62e76326 | 73 | static struct |
74 | { | |
75e88d56 | 75 | int timeouts; |
62e76326 | 76 | } |
77 | ||
78 | PeerStats; | |
062e2281 | 79 | |
a2c963ae | 80 | static const char *DirectStr[] = |
62e76326 | 81 | { |
82 | "DIRECT_UNKNOWN", | |
83 | "DIRECT_NO", | |
84 | "DIRECT_MAYBE", | |
85 | "DIRECT_YES" | |
86 | }; | |
75e88d56 | 87 | |
f5b8bbc4 | 88 | static void peerSelectFoo(ps_state *); |
89 | static void peerPingTimeout(void *data); | |
db1cd23c | 90 | static void peerSelectCallback(ps_state * psstate); |
86aebcda | 91 | static IRCB peerHandlePingReply; |
f5b8bbc4 | 92 | static void peerSelectStateFree(ps_state * psstate); |
93 | static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *); | |
44e237d0 | 94 | #if USE_HTCP |
95 | static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *); | |
96 | static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *); | |
97 | #endif | |
f5b8bbc4 | 98 | static int peerCheckNetdbDirect(ps_state * psstate); |
db1cd23c | 99 | static void peerGetSomeNeighbor(ps_state *); |
100 | static void peerGetSomeNeighborReplies(ps_state *); | |
101 | static void peerGetSomeDirect(ps_state *); | |
102 | static void peerGetSomeParent(ps_state *); | |
168dfda9 | 103 | static void peerGetAllParents(ps_state *); |
db1cd23c | 104 | static void peerAddFwdServer(FwdServer **, peer *, hier_code); |
348b2031 | 105 | |
aa839030 | 106 | CBDATA_CLASS_INIT(ps_state); |
107 | ||
348b2031 | 108 | static void |
109 | peerSelectStateFree(ps_state * psstate) | |
110 | { | |
111 | if (psstate->acl_checklist) { | |
bf8fe701 | 112 | debugs(44, 1, "calling aclChecklistFree() from peerSelectStateFree"); |
62e76326 | 113 | delete (psstate->acl_checklist); |
348b2031 | 114 | } |
62e76326 | 115 | |
6dd9f4bd | 116 | HTTPMSGUNLOCK(psstate->request); |
62e76326 | 117 | |
73a201f8 | 118 | if (psstate->entry) { |
62e76326 | 119 | assert(psstate->entry->ping_status != PING_WAITING); |
97b5e68f | 120 | psstate->entry->unlock(); |
62e76326 | 121 | psstate->entry = NULL; |
73a201f8 | 122 | } |
62e76326 | 123 | |
8407afee | 124 | cbdataFree(psstate); |
348b2031 | 125 | } |
062e2281 | 126 | |
2d72d4fd | 127 | static int |
190154cf | 128 | peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry) |
062e2281 | 129 | { |
7b665aeb | 130 | int n; |
db1cd23c | 131 | assert(entry); |
132 | assert(entry->ping_status == PING_NONE); | |
9bd6a36d | 133 | assert(direct != DIRECT_YES); |
bf8fe701 | 134 | debugs(44, 3, "peerSelectIcpPing: " << entry->url() ); |
62e76326 | 135 | |
92695e5e | 136 | if (!request->flags.hierarchical && direct != DIRECT_NO) |
62e76326 | 137 | return 0; |
138 | ||
d46a87a8 | 139 | if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys) |
62e76326 | 140 | if (direct != DIRECT_NO) |
141 | return 0; | |
142 | ||
7b665aeb | 143 | n = neighborsCount(request); |
62e76326 | 144 | |
bf8fe701 | 145 | debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors"); |
62e76326 | 146 | |
7b665aeb | 147 | return n; |
062e2281 | 148 | } |
149 | ||
150 | ||
062e2281 | 151 | void |
190154cf | 152 | peerSelect(HttpRequest * request, |
62e76326 | 153 | StoreEntry * entry, |
154 | PSC * callback, | |
155 | void *callback_data) | |
75e88d56 | 156 | { |
28c60158 | 157 | ps_state *psstate; |
62e76326 | 158 | |
86b389fc | 159 | if (entry) |
bf8fe701 | 160 | debugs(44, 3, "peerSelect: " << entry->url() ); |
86b389fc | 161 | else |
bf8fe701 | 162 | debugs(44, 3, "peerSelect: " << RequestMethodStr[request->method]); |
62e76326 | 163 | |
b24880fe | 164 | psstate = new ps_state; |
62e76326 | 165 | |
6dd9f4bd | 166 | psstate->request = HTTPMSGLOCK(request); |
62e76326 | 167 | |
b6c0e933 | 168 | psstate->entry = entry; |
62e76326 | 169 | |
b6c0e933 | 170 | psstate->callback = callback; |
62e76326 | 171 | |
fa80a8ef | 172 | psstate->callback_data = cbdataReference(callback_data); |
62e76326 | 173 | |
db1cd23c | 174 | psstate->direct = DIRECT_UNKNOWN; |
62e76326 | 175 | |
6cfa8966 | 176 | #if USE_CACHE_DIGESTS |
62e76326 | 177 | |
39edba21 | 178 | request->hier.peer_select_start = current_time; |
62e76326 | 179 | |
39edba21 | 180 | #endif |
62e76326 | 181 | |
2395cb21 | 182 | if (psstate->entry) |
34266cde | 183 | psstate->entry->lock() |
184 | ||
185 | ; | |
62e76326 | 186 | |
b6c0e933 | 187 | peerSelectFoo(psstate); |
75e88d56 | 188 | } |
189 | ||
190 | static void | |
191 | peerCheckNeverDirectDone(int answer, void *data) | |
192 | { | |
e6ccf245 | 193 | ps_state *psstate = (ps_state *) data; |
348b2031 | 194 | psstate->acl_checklist = NULL; |
bf8fe701 | 195 | debugs(44, 3, "peerCheckNeverDirectDone: " << answer); |
b6c0e933 | 196 | psstate->never_direct = answer ? 1 : -1; |
197 | peerSelectFoo(psstate); | |
75e88d56 | 198 | } |
199 | ||
200 | static void | |
201 | peerCheckAlwaysDirectDone(int answer, void *data) | |
202 | { | |
e6ccf245 | 203 | ps_state *psstate = (ps_state *)data; |
348b2031 | 204 | psstate->acl_checklist = NULL; |
bf8fe701 | 205 | debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer); |
7b665aeb | 206 | psstate->always_direct = answer ? 1 : -1; |
b6c0e933 | 207 | peerSelectFoo(psstate); |
75e88d56 | 208 | } |
209 | ||
93775f90 | 210 | static void |
db1cd23c | 211 | peerSelectCallback(ps_state * psstate) |
93775f90 | 212 | { |
b6c0e933 | 213 | StoreEntry *entry = psstate->entry; |
db1cd23c | 214 | FwdServer *fs = psstate->servers; |
fa80a8ef | 215 | PSC *callback; |
216 | void *cbdata; | |
62e76326 | 217 | |
b6c0e933 | 218 | if (entry) { |
bf8fe701 | 219 | debugs(44, 3, "peerSelectCallback: " << entry->url() ); |
62e76326 | 220 | |
221 | if (entry->ping_status == PING_WAITING) | |
222 | eventDelete(peerPingTimeout, psstate); | |
223 | ||
224 | entry->ping_status = PING_DONE; | |
b6c0e933 | 225 | } |
62e76326 | 226 | |
db1cd23c | 227 | if (fs == NULL) { |
bf8fe701 | 228 | debugs(44, 1, "Failed to select source for '" << entry->url() << "'" ); |
229 | debugs(44, 1, " always_direct = " << psstate->always_direct ); | |
230 | debugs(44, 1, " never_direct = " << psstate->never_direct ); | |
231 | debugs(44, 1, " timedout = " << psstate->ping.timedout ); | |
db1cd23c | 232 | } |
62e76326 | 233 | |
44e237d0 | 234 | psstate->ping.stop = current_time; |
0bdf2621 | 235 | psstate->request->hier.ping = psstate->ping; |
fa80a8ef | 236 | callback = psstate->callback; |
237 | psstate->callback = NULL; | |
62e76326 | 238 | |
fa80a8ef | 239 | if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) { |
62e76326 | 240 | psstate->servers = NULL; |
241 | callback(fs, cbdata); | |
db1cd23c | 242 | } |
62e76326 | 243 | |
348b2031 | 244 | peerSelectStateFree(psstate); |
93775f90 | 245 | } |
246 | ||
b3264694 | 247 | static int |
248 | peerCheckNetdbDirect(ps_state * psstate) | |
249 | { | |
5f84d830 | 250 | peer *p; |
b3264694 | 251 | int myrtt; |
252 | int myhops; | |
62e76326 | 253 | |
0886a797 | 254 | if (psstate->direct == DIRECT_NO) |
62e76326 | 255 | return 0; |
256 | ||
b3264694 | 257 | myrtt = netdbHostRtt(psstate->request->host); |
62e76326 | 258 | |
bf8fe701 | 259 | debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec"); |
260 | debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec"); | |
62e76326 | 261 | |
62e76326 | 262 | |
5f84d830 | 263 | if (myrtt && myrtt <= Config.minDirectRtt) |
62e76326 | 264 | return 1; |
265 | ||
b3264694 | 266 | myhops = netdbHostHops(psstate->request->host); |
62e76326 | 267 | |
bf8fe701 | 268 | debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops); |
269 | debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops); | |
62e76326 | 270 | |
62e76326 | 271 | |
b3264694 | 272 | if (myhops && myhops <= Config.minDirectHops) |
62e76326 | 273 | return 1; |
274 | ||
5f84d830 | 275 | p = whichPeer(&psstate->closest_parent_miss); |
62e76326 | 276 | |
5f84d830 | 277 | if (p == NULL) |
62e76326 | 278 | return 0; |
279 | ||
bf8fe701 | 280 | debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec"); |
62e76326 | 281 | |
5f84d830 | 282 | if (myrtt && myrtt <= psstate->ping.p_rtt) |
62e76326 | 283 | return 1; |
284 | ||
b3264694 | 285 | return 0; |
286 | } | |
287 | ||
93775f90 | 288 | static void |
511f47bb | 289 | peerSelectFoo(ps_state * ps) |
062e2281 | 290 | { |
511f47bb | 291 | StoreEntry *entry = ps->entry; |
190154cf | 292 | HttpRequest *request = ps->request; |
bf8fe701 | 293 | debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr[request->method] << " " << request->host << "'"); |
62e76326 | 294 | |
511f47bb | 295 | if (ps->direct == DIRECT_UNKNOWN) { |
62e76326 | 296 | if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) { |
297 | ps->acl_checklist = aclChecklistCreate( | |
298 | Config.accessList.AlwaysDirect, | |
299 | request, | |
300 | NULL); /* ident */ | |
301 | ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone, | |
302 | ps); | |
303 | return; | |
304 | } else if (ps->always_direct > 0) { | |
305 | ps->direct = DIRECT_YES; | |
306 | } else if (ps->never_direct == 0 && Config.accessList.NeverDirect) { | |
307 | ps->acl_checklist = aclChecklistCreate( | |
308 | Config.accessList.NeverDirect, | |
309 | request, | |
310 | NULL); /* ident */ | |
311 | ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone, | |
312 | ps); | |
313 | return; | |
314 | } else if (ps->never_direct > 0) { | |
315 | ps->direct = DIRECT_NO; | |
316 | } else if (request->flags.accelerated) { | |
317 | ps->direct = DIRECT_NO; | |
318 | } else if (request->flags.loopdetect) { | |
319 | ps->direct = DIRECT_YES; | |
320 | } else if (peerCheckNetdbDirect(ps)) { | |
321 | ps->direct = DIRECT_YES; | |
322 | } else { | |
323 | ps->direct = DIRECT_MAYBE; | |
324 | } | |
325 | ||
bf8fe701 | 326 | debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]); |
db1cd23c | 327 | } |
62e76326 | 328 | |
df503d6c | 329 | if (entry == NULL) { |
62e76326 | 330 | (void) 0; |
df503d6c | 331 | } else if (entry->ping_status == PING_NONE) { |
62e76326 | 332 | peerGetSomeNeighbor(ps); |
333 | ||
334 | if (entry->ping_status == PING_WAITING) | |
335 | return; | |
db1cd23c | 336 | } else if (entry->ping_status == PING_WAITING) { |
62e76326 | 337 | peerGetSomeNeighborReplies(ps); |
338 | entry->ping_status = PING_DONE; | |
db1cd23c | 339 | } |
62e76326 | 340 | |
168dfda9 | 341 | switch (ps->direct) { |
62e76326 | 342 | |
168dfda9 | 343 | case DIRECT_YES: |
62e76326 | 344 | peerGetSomeDirect(ps); |
345 | break; | |
346 | ||
168dfda9 | 347 | case DIRECT_NO: |
62e76326 | 348 | peerGetSomeParent(ps); |
349 | peerGetAllParents(ps); | |
350 | break; | |
351 | ||
168dfda9 | 352 | default: |
62e76326 | 353 | |
354 | if (Config.onoff.prefer_direct) | |
355 | peerGetSomeDirect(ps); | |
356 | ||
357 | if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) | |
358 | peerGetSomeParent(ps); | |
359 | ||
360 | if (!Config.onoff.prefer_direct) | |
361 | peerGetSomeDirect(ps); | |
362 | ||
363 | break; | |
168dfda9 | 364 | } |
62e76326 | 365 | |
511f47bb | 366 | peerSelectCallback(ps); |
db1cd23c | 367 | } |
368 | ||
369 | /* | |
370 | * peerGetSomeNeighbor | |
371 | * | |
372 | * Selects a neighbor (parent or sibling) based on one of the | |
373 | * following methods: | |
374 | * Cache Digests | |
375 | * CARP | |
376 | * Netdb RTT estimates | |
377 | * ICP/HTCP queries | |
378 | */ | |
379 | static void | |
380 | peerGetSomeNeighbor(ps_state * ps) | |
381 | { | |
382 | StoreEntry *entry = ps->entry; | |
190154cf | 383 | HttpRequest *request = ps->request; |
db1cd23c | 384 | peer *p; |
385 | hier_code code = HIER_NONE; | |
386 | assert(entry->ping_status == PING_NONE); | |
62e76326 | 387 | |
db1cd23c | 388 | if (ps->direct == DIRECT_YES) { |
62e76326 | 389 | entry->ping_status = PING_DONE; |
390 | return; | |
124511e5 | 391 | } |
62e76326 | 392 | |
6cfa8966 | 393 | #if USE_CACHE_DIGESTS |
f66a9ef4 | 394 | if ((p = neighborsDigestSelect(request))) { |
62e76326 | 395 | if (neighborType(p, request) == PEER_PARENT) |
396 | code = CD_PARENT_HIT; | |
397 | else | |
398 | code = CD_SIBLING_HIT; | |
db1cd23c | 399 | } else |
c127134a | 400 | #endif |
62e76326 | 401 | if ((p = netdbClosestParent(request))) { |
402 | code = CLOSEST_PARENT; | |
403 | } else if (peerSelectIcpPing(request, ps->direct, entry)) { | |
bf8fe701 | 404 | debugs(44, 3, "peerSelect: Doing ICP pings"); |
62e76326 | 405 | ps->ping.start = current_time; |
406 | ps->ping.n_sent = neighborsUdpPing(request, | |
407 | entry, | |
408 | peerHandlePingReply, | |
409 | ps, | |
410 | &ps->ping.n_replies_expected, | |
411 | &ps->ping.timeout); | |
412 | ||
413 | if (ps->ping.n_sent == 0) | |
bf8fe701 | 414 | debugs(44, 0, "WARNING: neighborsUdpPing returned 0"); |
415 | debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected << | |
416 | " ICP replies expected, RTT " << ps->ping.timeout << | |
417 | " msec"); | |
62e76326 | 418 | |
62e76326 | 419 | |
420 | if (ps->ping.n_replies_expected > 0) { | |
421 | entry->ping_status = PING_WAITING; | |
422 | eventAdd("peerPingTimeout", | |
423 | peerPingTimeout, | |
424 | ps, | |
425 | 0.001 * ps->ping.timeout, | |
426 | 0); | |
427 | return; | |
428 | } | |
429 | } | |
430 | ||
db1cd23c | 431 | if (code != HIER_NONE) { |
62e76326 | 432 | assert(p); |
bf8fe701 | 433 | debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host); |
62e76326 | 434 | peerAddFwdServer(&ps->servers, p, code); |
db1cd23c | 435 | } |
62e76326 | 436 | |
db1cd23c | 437 | entry->ping_status = PING_DONE; |
438 | } | |
439 | ||
440 | /* | |
441 | * peerGetSomeNeighborReplies | |
442 | * | |
443 | * Selects a neighbor (parent or sibling) based on ICP/HTCP replies. | |
444 | */ | |
445 | static void | |
446 | peerGetSomeNeighborReplies(ps_state * ps) | |
447 | { | |
190154cf | 448 | HttpRequest *request = ps->request; |
db1cd23c | 449 | peer *p = NULL; |
450 | hier_code code = HIER_NONE; | |
4c5a1592 | 451 | assert(ps->entry->ping_status == PING_WAITING); |
db1cd23c | 452 | assert(ps->direct != DIRECT_YES); |
62e76326 | 453 | |
db1cd23c | 454 | if (peerCheckNetdbDirect(ps)) { |
62e76326 | 455 | code = CLOSEST_DIRECT; |
bf8fe701 | 456 | debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << request->host); |
62e76326 | 457 | peerAddFwdServer(&ps->servers, NULL, code); |
458 | return; | |
db1cd23c | 459 | } |
62e76326 | 460 | |
db1cd23c | 461 | if ((p = ps->hit)) { |
62e76326 | 462 | code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT; |
db1cd23c | 463 | } else |
464 | #if ALLOW_SOURCE_PING | |
62e76326 | 465 | if ((p = ps->secho)) { |
466 | code = SOURCE_FASTEST; | |
467 | } else | |
db1cd23c | 468 | #endif |
62e76326 | 469 | if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr) { |
470 | p = whichPeer(&ps->closest_parent_miss); | |
471 | code = CLOSEST_PARENT_MISS; | |
472 | } else if (ps->first_parent_miss.sin_addr.s_addr != any_addr.s_addr) { | |
473 | p = whichPeer(&ps->first_parent_miss); | |
474 | code = FIRST_PARENT_MISS; | |
475 | } | |
476 | ||
db1cd23c | 477 | if (p && code != HIER_NONE) { |
bf8fe701 | 478 | debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host); |
62e76326 | 479 | peerAddFwdServer(&ps->servers, p, code); |
db1cd23c | 480 | } |
481 | } | |
482 | ||
483 | ||
484 | /* | |
485 | * peerGetSomeDirect | |
486 | * | |
487 | * Simply adds a 'direct' entry to the FwdServers list if this | |
488 | * request can be forwarded directly to the origin server | |
489 | */ | |
490 | static void | |
491 | peerGetSomeDirect(ps_state * ps) | |
492 | { | |
493 | if (ps->direct == DIRECT_NO) | |
62e76326 | 494 | return; |
495 | ||
db80e881 | 496 | /* WAIS is not implemented natively */ |
db1cd23c | 497 | if (ps->request->protocol == PROTO_WAIS) |
db80e881 | 498 | return; |
499 | ||
500 | peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT); | |
db1cd23c | 501 | } |
502 | ||
503 | static void | |
504 | peerGetSomeParent(ps_state * ps) | |
505 | { | |
506 | peer *p; | |
190154cf | 507 | HttpRequest *request = ps->request; |
db1cd23c | 508 | hier_code code = HIER_NONE; |
bf8fe701 | 509 | debugs(44, 3, "peerGetSomeParent: " << RequestMethodStr[request->method] << " " << request->host); |
62e76326 | 510 | |
6b8e7481 | 511 | if (ps->direct == DIRECT_YES) |
62e76326 | 512 | return; |
513 | ||
db1cd23c | 514 | if ((p = getDefaultParent(request))) { |
62e76326 | 515 | code = DEFAULT_PARENT; |
b3995439 | 516 | #if USE_CARP |
62e76326 | 517 | |
b3995439 | 518 | } else if ((p = carpSelectParent(request))) { |
62e76326 | 519 | code = CARP; |
b3995439 | 520 | #endif |
62e76326 | 521 | |
db1cd23c | 522 | } else if ((p = getRoundRobinParent(request))) { |
62e76326 | 523 | code = ROUNDROBIN_PARENT; |
d1b63fc8 | 524 | } else if ((p = getWeightedRoundRobinParent(request))) { |
62e76326 | 525 | code = ROUNDROBIN_PARENT; |
db1cd23c | 526 | } else if ((p = getFirstUpParent(request))) { |
62e76326 | 527 | code = FIRSTUP_PARENT; |
db1cd23c | 528 | } else if ((p = getAnyParent(request))) { |
62e76326 | 529 | code = ANY_OLD_PARENT; |
db1cd23c | 530 | } |
62e76326 | 531 | |
db1cd23c | 532 | if (code != HIER_NONE) { |
bf8fe701 | 533 | debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host); |
62e76326 | 534 | peerAddFwdServer(&ps->servers, p, code); |
062e2281 | 535 | } |
536 | } | |
537 | ||
168dfda9 | 538 | /* Adds alive parents. Used as a last resort for never_direct. |
539 | */ | |
540 | static void | |
541 | peerGetAllParents(ps_state * ps) | |
542 | { | |
543 | peer *p; | |
190154cf | 544 | HttpRequest *request = ps->request; |
168dfda9 | 545 | /* Add all alive parents */ |
62e76326 | 546 | |
168dfda9 | 547 | for (p = Config.peers; p; p = p->next) { |
62e76326 | 548 | /* XXX: neighbors.c lacks a public interface for enumerating |
549 | * parents to a request so we have to dig some here.. | |
550 | */ | |
551 | ||
552 | if (neighborType(p, request) != PEER_PARENT) | |
553 | continue; | |
554 | ||
555 | if (!peerHTTPOkay(p, request)) | |
556 | continue; | |
557 | ||
bf8fe701 | 558 | debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host); |
62e76326 | 559 | |
560 | peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT); | |
168dfda9 | 561 | } |
62e76326 | 562 | |
168dfda9 | 563 | /* XXX: should add dead parents here, but it is currently |
564 | * not possible to find out which parents are dead or which | |
565 | * simply are not configured to handle the request. | |
566 | */ | |
567 | /* Add default parent as a last resort */ | |
568 | if ((p = getDefaultParent(request))) { | |
62e76326 | 569 | peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT); |
168dfda9 | 570 | } |
571 | } | |
572 | ||
d9586c3c | 573 | static void |
93775f90 | 574 | peerPingTimeout(void *data) |
062e2281 | 575 | { |
e6ccf245 | 576 | ps_state *psstate = (ps_state *)data; |
b6c0e933 | 577 | StoreEntry *entry = psstate->entry; |
62e76326 | 578 | |
fefb0227 | 579 | if (entry) |
bf8fe701 | 580 | debugs(44, 3, "peerPingTimeout: '" << entry->url() << "'" ); |
62e76326 | 581 | |
fa80a8ef | 582 | if (!cbdataReferenceValid(psstate->callback_data)) { |
62e76326 | 583 | /* request aborted */ |
584 | entry->ping_status = PING_DONE; | |
585 | cbdataReferenceDone(psstate->callback_data); | |
586 | peerSelectStateFree(psstate); | |
587 | return; | |
73a201f8 | 588 | } |
62e76326 | 589 | |
75e88d56 | 590 | PeerStats.timeouts++; |
44e237d0 | 591 | psstate->ping.timedout = 1; |
b6c0e933 | 592 | peerSelectFoo(psstate); |
85034133 | 593 | } |
594 | ||
595 | void | |
596 | peerSelectInit(void) | |
597 | { | |
75e88d56 | 598 | memset(&PeerStats, '\0', sizeof(PeerStats)); |
9bdbfe33 | 599 | assert(sizeof(hier_strings) == (HIER_MAX + 1) * sizeof(char *)); |
062e2281 | 600 | } |
93775f90 | 601 | |
b3264694 | 602 | static void |
603 | peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps) | |
604 | { | |
605 | int rtt; | |
606 | int hops; | |
62e76326 | 607 | |
b3264694 | 608 | if (Config.onoff.query_icmp) { |
62e76326 | 609 | if (header->flags & ICP_FLAG_SRC_RTT) { |
610 | rtt = header->pad & 0xFFFF; | |
611 | hops = (header->pad >> 16) & 0xFFFF; | |
612 | ||
613 | if (rtt > 0 && rtt < 0xFFFF) | |
614 | netdbUpdatePeer(ps->request, p, rtt, hops); | |
615 | ||
616 | if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) { | |
617 | ps->closest_parent_miss = p->in_addr; | |
618 | ps->ping.p_rtt = rtt; | |
619 | } | |
620 | } | |
b3264694 | 621 | } |
62e76326 | 622 | |
18ec72b2 | 623 | /* if closest-only is set, then don't allow FIRST_PARENT_MISS */ |
cd196bc8 | 624 | if (p->options.closest_only) |
62e76326 | 625 | return; |
626 | ||
85223cd7 | 627 | /* set FIRST_MISS if there is no CLOSEST parent */ |
628 | if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr) | |
62e76326 | 629 | return; |
630 | ||
d1b63fc8 | 631 | rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight; |
62e76326 | 632 | |
d1b63fc8 | 633 | if (rtt < 1) |
62e76326 | 634 | rtt = 1; |
635 | ||
0f97864c | 636 | if (ps->first_parent_miss.sin_addr.s_addr == any_addr.s_addr || |
62e76326 | 637 | rtt < ps->ping.w_rtt) { |
638 | ps->first_parent_miss = p->in_addr; | |
639 | ps->ping.w_rtt = rtt; | |
b3264694 | 640 | } |
641 | } | |
93775f90 | 642 | |
b6c0e933 | 643 | static void |
b3264694 | 644 | peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data) |
93775f90 | 645 | { |
e6ccf245 | 646 | ps_state *psstate = (ps_state *)data; |
647 | icp_opcode op = header->getOpCode(); | |
bf8fe701 | 648 | debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->entry->url() ); |
69c95dd3 | 649 | #if USE_CACHE_DIGESTS && 0 |
26b164ac | 650 | /* do cd lookup to count false misses */ |
62e76326 | 651 | |
3ab66981 | 652 | if (p && request) |
62e76326 | 653 | peerNoteDigestLookup(request, p, |
654 | peerDigestLookup(p, request, psstate->entry)); | |
655 | ||
26b164ac | 656 | #endif |
62e76326 | 657 | |
44e237d0 | 658 | psstate->ping.n_recv++; |
62e76326 | 659 | |
27cd7235 | 660 | if (op == ICP_MISS || op == ICP_DECHO) { |
62e76326 | 661 | if (type == PEER_PARENT) |
662 | peerIcpParentMiss(p, header, psstate); | |
a7c05555 | 663 | } else if (op == ICP_HIT) { |
62e76326 | 664 | psstate->hit = p; |
665 | psstate->hit_type = type; | |
666 | peerSelectFoo(psstate); | |
667 | return; | |
db1cd23c | 668 | } |
62e76326 | 669 | |
db1cd23c | 670 | #if ALLOW_SOURCE_PING |
671 | else if (op == ICP_SECHO) { | |
62e76326 | 672 | psstate->secho = p; |
673 | peerSelectFoo(psstate); | |
674 | return; | |
93775f90 | 675 | } |
62e76326 | 676 | |
db1cd23c | 677 | #endif |
44e237d0 | 678 | if (psstate->ping.n_recv < psstate->ping.n_replies_expected) |
62e76326 | 679 | return; |
680 | ||
b6c0e933 | 681 | peerSelectFoo(psstate); |
93775f90 | 682 | } |
86aebcda | 683 | |
684 | #if USE_HTCP | |
685 | static void | |
44e237d0 | 686 | peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data) |
86aebcda | 687 | { |
e6ccf245 | 688 | ps_state *psstate = (ps_state *)data; |
bf8fe701 | 689 | debugs(44, 3, "peerHandleHtcpReply: " << |
690 | (htcp->hit ? "HIT" : "MISS") << " " << | |
691 | psstate->entry->url() ); | |
44e237d0 | 692 | psstate->ping.n_recv++; |
62e76326 | 693 | |
44e237d0 | 694 | if (htcp->hit) { |
62e76326 | 695 | psstate->hit = p; |
696 | psstate->hit_type = type; | |
697 | peerSelectFoo(psstate); | |
698 | return; | |
44e237d0 | 699 | } |
62e76326 | 700 | |
44e237d0 | 701 | if (type == PEER_PARENT) |
62e76326 | 702 | peerHtcpParentMiss(p, htcp, psstate); |
703 | ||
44e237d0 | 704 | if (psstate->ping.n_recv < psstate->ping.n_replies_expected) |
62e76326 | 705 | return; |
706 | ||
44e237d0 | 707 | peerSelectFoo(psstate); |
708 | } | |
709 | ||
710 | static void | |
711 | peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps) | |
712 | { | |
713 | int rtt; | |
714 | int hops; | |
62e76326 | 715 | |
44e237d0 | 716 | if (Config.onoff.query_icmp) { |
62e76326 | 717 | if (htcp->cto.rtt > 0) { |
718 | rtt = (int) htcp->cto.rtt * 1000; | |
719 | hops = (int) htcp->cto.hops * 1000; | |
720 | netdbUpdatePeer(ps->request, p, rtt, hops); | |
721 | ||
722 | if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) { | |
723 | ps->closest_parent_miss = p->in_addr; | |
724 | ps->ping.p_rtt = rtt; | |
725 | } | |
726 | } | |
44e237d0 | 727 | } |
62e76326 | 728 | |
44e237d0 | 729 | /* if closest-only is set, then don't allow FIRST_PARENT_MISS */ |
cd196bc8 | 730 | if (p->options.closest_only) |
62e76326 | 731 | return; |
732 | ||
44e237d0 | 733 | /* set FIRST_MISS if there is no CLOSEST parent */ |
734 | if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr) | |
62e76326 | 735 | return; |
736 | ||
d1b63fc8 | 737 | rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight; |
62e76326 | 738 | |
d1b63fc8 | 739 | if (rtt < 1) |
62e76326 | 740 | rtt = 1; |
741 | ||
508b3383 | 742 | if (ps->first_parent_miss.sin_addr.s_addr == any_addr.s_addr || |
62e76326 | 743 | rtt < ps->ping.w_rtt) { |
744 | ps->first_parent_miss = p->in_addr; | |
745 | ps->ping.w_rtt = rtt; | |
44e237d0 | 746 | } |
86aebcda | 747 | } |
62e76326 | 748 | |
86aebcda | 749 | #endif |
750 | ||
751 | static void | |
9ef28b60 | 752 | peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data) |
86aebcda | 753 | { |
754 | if (proto == PROTO_ICP) | |
62e76326 | 755 | peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data); |
756 | ||
86aebcda | 757 | #if USE_HTCP |
62e76326 | 758 | |
86aebcda | 759 | else if (proto == PROTO_HTCP) |
62e76326 | 760 | peerHandleHtcpReply(p, type, (htcpReplyData *)pingdata, data); |
761 | ||
86aebcda | 762 | #endif |
62e76326 | 763 | |
86aebcda | 764 | else |
4a7a3d56 | 765 | debugs(44, 1, "peerHandlePingReply: unknown protocol_t " << proto); |
86aebcda | 766 | } |
db1cd23c | 767 | |
768 | static void | |
b73f343e | 769 | peerAddFwdServer(FwdServer ** FSVR, peer * p, hier_code code) |
db1cd23c | 770 | { |
e6ccf245 | 771 | FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER); |
bf8fe701 | 772 | debugs(44, 5, "peerAddFwdServer: adding " << |
773 | (p ? p->host : "DIRECT") << " " << | |
774 | hier_strings[code] ); | |
29b8d8d6 | 775 | fs->_peer = cbdataReference(p); |
db1cd23c | 776 | fs->code = code; |
62e76326 | 777 | |
b73f343e | 778 | while (*FSVR) |
779 | FSVR = &(*FSVR)->next; | |
62e76326 | 780 | |
b73f343e | 781 | *FSVR = fs; |
db1cd23c | 782 | } |
b24880fe | 783 | |
784 | void * | |
785 | ps_state::operator new(size_t) | |
786 | { | |
aa839030 | 787 | CBDATA_INIT_TYPE(ps_state); |
b24880fe | 788 | return cbdataAlloc(ps_state); |
789 | } | |
790 | ||
791 | ps_state::ps_state() : request (NULL), | |
792 | entry (NULL), | |
793 | always_direct (0), | |
794 | never_direct (0), | |
795 | direct (0), | |
796 | callback (NULL), | |
797 | callback_data (NULL), | |
798 | servers (NULL), | |
799 | hit(NULL), | |
800 | hit_type(PEER_NONE), | |
801 | #if ALLOW_SOURCE_PING | |
802 | ||
803 | secho( NULL), | |
804 | #endif | |
805 | acl_checklist (NULL) | |
806 | { | |
807 | memset(&first_parent_miss, '\0', sizeof(first_parent_miss)); | |
808 | memset(&closest_parent_miss, '\0', sizeof(closest_parent_miss)); | |
809 | } | |
810 | ||
811 | ping_data::ping_data() : | |
812 | n_sent(0), | |
813 | n_recv(0), | |
814 | n_replies_expected(0), | |
815 | timeout(0), | |
816 | timedout(0), | |
817 | w_rtt(0), | |
818 | p_rtt(0) | |
819 | { | |
820 | start.tv_sec = 0; | |
821 | start.tv_usec = 0; | |
822 | stop.tv_sec = 0; | |
823 | stop.tv_usec = 0; | |
824 | } |