]>
Commit | Line | Data |
---|---|---|
30a4f2a8 | 1 | /* |
77b1029d | 2 | * Copyright (C) 1996-2020 The Squid Software Foundation and contributors |
26ac0430 | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
019dd986 | 7 | */ |
ed43818f | 8 | |
bbc27441 AJ |
9 | /* DEBUG: section 19 Store Memory Primitives */ |
10 | ||
582c2af2 FC |
11 | #include "squid.h" |
12 | #include "Generic.h" | |
a0c227a9 | 13 | #include "HttpReply.h" |
528b2c61 | 14 | #include "mem_node.h" |
15 | #include "MemObject.h" | |
582c2af2 FC |
16 | #include "profiler/Profiler.h" |
17 | #include "stmem.h" | |
528b2c61 | 18 | |
12d74f96 | 19 | /* |
20 | * NodeGet() is called to get the data buffer to pass to storeIOWrite(). | |
21 | * By setting the write_pending flag here we are assuming that there | |
22 | * will be no other users of NodeGet(). The storeIOWrite() callback | |
23 | * is memNodeWriteComplete(), which, for whatever reason, lives in | |
24 | * mem_node.cc. | |
25 | */ | |
d06925a4 | 26 | char * |
27 | mem_hdr::NodeGet(mem_node * aNode) | |
28 | { | |
12d74f96 | 29 | assert(!aNode->write_pending); |
3dd52a0b | 30 | aNode->write_pending = true; |
d06925a4 | 31 | return aNode->data; |
32 | } | |
33 | ||
47f6e231 | 34 | int64_t |
528b2c61 | 35 | mem_hdr::lowestOffset () const |
36 | { | |
42a503bd | 37 | const SplayNode<mem_node *> *theStart = nodes.start(); |
38 | ||
39 | if (theStart) | |
40 | return theStart->data->nodeBuffer.offset; | |
62e76326 | 41 | |
528b2c61 | 42 | return 0; |
43 | } | |
44 | ||
47f6e231 | 45 | int64_t |
528b2c61 | 46 | mem_hdr::endOffset () const |
47 | { | |
47f6e231 | 48 | int64_t result = 0; |
b8bad68c | 49 | const SplayNode<mem_node *> *theEnd = nodes.finish(); |
62e76326 | 50 | |
42a503bd | 51 | if (theEnd) |
52 | result = theEnd->data->dataRange().end; | |
62e76326 | 53 | |
528b2c61 | 54 | assert (result == inmem_hi); |
62e76326 | 55 | |
528b2c61 | 56 | return result; |
57 | } | |
090089c4 | 58 | |
d89d1fb6 | 59 | void |
528b2c61 | 60 | mem_hdr::freeContent() |
61 | { | |
552ec743 | 62 | nodes.destroy(); |
528b2c61 | 63 | inmem_hi = 0; |
e2851fe7 | 64 | debugs(19, 9, HERE << this << " hi: " << inmem_hi); |
528b2c61 | 65 | } |
66 | ||
12d74f96 | 67 | bool |
42a503bd | 68 | mem_hdr::unlink(mem_node *aNode) |
528b2c61 | 69 | { |
12d74f96 | 70 | if (aNode->write_pending) { |
fa84c01d | 71 | debugs(0, DBG_CRITICAL, "cannot unlink mem_node " << aNode << " while write_pending"); |
12d74f96 | 72 | return false; |
73 | } | |
d06925a4 | 74 | |
97754f5a | 75 | debugs(19, 8, this << " removing " << aNode); |
12d74f96 | 76 | nodes.remove (aNode, NodeCompare); |
77 | delete aNode; | |
78 | return true; | |
090089c4 | 79 | } |
80 | ||
47f6e231 | 81 | int64_t |
82 | mem_hdr::freeDataUpto(int64_t target_offset) | |
528b2c61 | 83 | { |
97754f5a | 84 | debugs(19, 8, this << " up to " << target_offset); |
528b2c61 | 85 | /* keep the last one to avoid change to other part of code */ |
810635e3 | 86 | SplayNode<mem_node*> const * theStart; |
42a503bd | 87 | |
12d74f96 | 88 | while ((theStart = nodes.start())) { |
89 | if (theStart == nodes.finish()) | |
90 | break; | |
91 | ||
47f6e231 | 92 | if (theStart->data->end() > target_offset ) |
12d74f96 | 93 | break; |
94 | ||
95 | if (!unlink(theStart->data)) | |
96 | break; | |
42a503bd | 97 | } |
62e76326 | 98 | |
528b2c61 | 99 | assert (lowestOffset () <= target_offset); |
62e76326 | 100 | |
528b2c61 | 101 | return lowestOffset (); |
102 | } | |
103 | ||
62e76326 | 104 | int |
528b2c61 | 105 | mem_hdr::appendToNode(mem_node *aNode, const char *data, int maxLength) |
106 | { | |
9e167fa2 | 107 | size_t result = writeAvailable (aNode, aNode->nodeBuffer.offset + aNode->nodeBuffer.length,maxLength, data); |
528b2c61 | 108 | return result; |
109 | } | |
110 | ||
111 | size_t | |
47f6e231 | 112 | mem_hdr::writeAvailable(mem_node *aNode, int64_t location, size_t amount, char const *source) |
528b2c61 | 113 | { |
114 | /* if we attempt to overwrite existing data or leave a gap within a node */ | |
ed013b6c | 115 | assert (location == aNode->nodeBuffer.offset + (int64_t)aNode->nodeBuffer.length); |
528b2c61 | 116 | /* And we are not at the end of the node */ |
117 | assert (aNode->canAccept (location)); | |
62e76326 | 118 | |
528b2c61 | 119 | /* these two can go I think */ |
ed013b6c | 120 | assert (location - aNode->nodeBuffer.offset == (int64_t)aNode->nodeBuffer.length); |
d85c3078 | 121 | size_t copyLen = min(amount, aNode->space()); |
528b2c61 | 122 | |
41d00cd3 | 123 | memcpy(aNode->nodeBuffer.data + aNode->nodeBuffer.length, source, copyLen); |
528b2c61 | 124 | |
e2851fe7 | 125 | debugs(19, 9, HERE << this << " hi: " << inmem_hi); |
47f6e231 | 126 | if (inmem_hi <= location) |
62e76326 | 127 | inmem_hi = location + copyLen; |
128 | ||
528b2c61 | 129 | /* Adjust the ptr and len according to what was deposited in the page */ |
130 | aNode->nodeBuffer.length += copyLen; | |
62e76326 | 131 | |
e2851fe7 AR |
132 | debugs(19, 9, HERE << this << " hi: " << inmem_hi); |
133 | debugs(19, 9, HERE << this << " hi: " << endOffset()); | |
528b2c61 | 134 | return copyLen; |
135 | } | |
136 | ||
137 | void | |
138 | mem_hdr::appendNode (mem_node *aNode) | |
139 | { | |
42a503bd | 140 | nodes.insert (aNode, NodeCompare); |
090089c4 | 141 | } |
142 | ||
e6e5d90d | 143 | void |
528b2c61 | 144 | mem_hdr::makeAppendSpace() |
145 | { | |
42a503bd | 146 | if (!nodes.size()) { |
147 | appendNode (new mem_node (0)); | |
62e76326 | 148 | return; |
090089c4 | 149 | } |
62e76326 | 150 | |
b8bad68c | 151 | if (!nodes.finish()->data->space()) |
62e76326 | 152 | appendNode (new mem_node (endOffset())); |
153 | ||
b8bad68c | 154 | assert (nodes.finish()->data->space()); |
528b2c61 | 155 | } |
156 | ||
157 | void | |
158 | mem_hdr::internalAppend(const char *data, int len) | |
159 | { | |
e2851fe7 | 160 | debugs(19, 6, "memInternalAppend: " << this << " len " << len); |
62e76326 | 161 | |
090089c4 | 162 | while (len > 0) { |
62e76326 | 163 | makeAppendSpace(); |
b8bad68c | 164 | int copied = appendToNode (nodes.finish()->data, data, len); |
62e76326 | 165 | assert (copied); |
166 | ||
167 | len -= copied; | |
168 | data += copied; | |
090089c4 | 169 | } |
090089c4 | 170 | } |
171 | ||
528b2c61 | 172 | /* returns a mem_node that contains location.. |
173 | * If no node contains the start, it returns NULL. | |
174 | */ | |
175 | mem_node * | |
47f6e231 | 176 | mem_hdr::getBlockContainingLocation (int64_t location) const |
528b2c61 | 177 | { |
a5c1e941 | 178 | // Optimize: do not create a whole mem_node just to store location |
42a503bd | 179 | mem_node target (location); |
180 | target.nodeBuffer.length = 1; | |
181 | mem_node *const *result = nodes.find (&target, NodeCompare); | |
62e76326 | 182 | |
42a503bd | 183 | if (result) |
184 | return *result; | |
62e76326 | 185 | |
42a503bd | 186 | return NULL; |
528b2c61 | 187 | } |
188 | ||
189 | size_t | |
47f6e231 | 190 | mem_hdr::copyAvailable(mem_node *aNode, int64_t location, size_t amount, char *target) const |
528b2c61 | 191 | { |
47f6e231 | 192 | if (aNode->nodeBuffer.offset > location) |
62e76326 | 193 | return 0; |
194 | ||
47f6e231 | 195 | assert (aNode->nodeBuffer.offset <= location); |
62e76326 | 196 | |
528b2c61 | 197 | assert (aNode->end() > location); |
62e76326 | 198 | |
528b2c61 | 199 | size_t copyOffset = location - aNode->nodeBuffer.offset; |
62e76326 | 200 | |
d85c3078 | 201 | size_t copyLen = min(amount, aNode->nodeBuffer.length - copyOffset); |
528b2c61 | 202 | |
41d00cd3 | 203 | memcpy(target, aNode->nodeBuffer.data + copyOffset, copyLen); |
62e76326 | 204 | |
528b2c61 | 205 | return copyLen; |
206 | } | |
207 | ||
b8bad68c | 208 | void |
209 | mem_hdr::debugDump() const | |
210 | { | |
db7778f1 | 211 | debugs (19, 0, "mem_hdr::debugDump: lowest offset: " << lowestOffset() << " highest offset + 1: " << endOffset() << "."); |
b8bad68c | 212 | std::ostringstream result; |
213 | PointerPrinter<mem_node *> foo(result, " - "); | |
97754f5a | 214 | getNodes().visit(foo); |
db7778f1 | 215 | debugs (19, 0, "mem_hdr::debugDump: Current available data is: " << result.str() << "."); |
b8bad68c | 216 | } |
217 | ||
62e76326 | 218 | /* FIXME: how do we deal with sparse results - |
528b2c61 | 219 | * where we have (say) |
26ac0430 | 220 | * 0-500 and 1000-1500, but are asked for |
528b2c61 | 221 | * 0-2000 |
222 | * Partial answer: | |
223 | * we supply 0-500 and stop. | |
224 | */ | |
02be0294 | 225 | ssize_t |
90703668 | 226 | mem_hdr::copy(StoreIOBuffer const &target) const |
090089c4 | 227 | { |
528b2c61 | 228 | |
47f6e231 | 229 | assert(target.range().end > target.range().start); |
e2851fe7 | 230 | debugs(19, 6, "memCopy: " << this << " " << target.range()); |
62e76326 | 231 | |
b8bad68c | 232 | /* we shouldn't ever ask for absent offsets */ |
233 | ||
42a503bd | 234 | if (nodes.size() == 0) { |
e0236918 | 235 | debugs(19, DBG_IMPORTANT, "mem_hdr::copy: No data to read"); |
b8bad68c | 236 | debugDump(); |
42a503bd | 237 | assert (0); |
62e76326 | 238 | return 0; |
42a503bd | 239 | } |
62e76326 | 240 | |
528b2c61 | 241 | /* RC: the next assert is nearly useless */ |
90703668 | 242 | assert(target.length > 0); |
528b2c61 | 243 | |
090089c4 | 244 | /* Seek our way into store */ |
47f6e231 | 245 | mem_node *p = getBlockContainingLocation(target.offset); |
62e76326 | 246 | |
528b2c61 | 247 | if (!p) { |
e0236918 | 248 | debugs(19, DBG_IMPORTANT, "memCopy: could not find start of " << target.range() << |
e4049756 | 249 | " in memory."); |
b8bad68c | 250 | debugDump(); |
24fe24be | 251 | fatal_dump("Squid has attempted to read data from memory that is not present. This is an indication of of (pre-3.0) code that hasn't been updated to deal with sparse objects in memory. Squid should coredump.allowing to review the cause. Immediately preceding this message is a dump of the available data in the format [start,end). The [ means from the value, the ) means up to the value. I.e. [1,5) means that there are 4 bytes of data, at offsets 1,2,3,4.\n"); |
62e76326 | 252 | return 0; |
090089c4 | 253 | } |
62e76326 | 254 | |
90703668 | 255 | size_t bytes_to_go = target.length; |
256 | char *ptr_to_buf = target.data; | |
47f6e231 | 257 | int64_t location = target.offset; |
62e76326 | 258 | |
090089c4 | 259 | /* Start copying begining with this block until |
260 | * we're satiated */ | |
528b2c61 | 261 | |
090089c4 | 262 | while (p && bytes_to_go > 0) { |
62e76326 | 263 | size_t bytes_to_copy = copyAvailable (p, |
264 | location, bytes_to_go, ptr_to_buf); | |
265 | ||
266 | /* hit a sparse patch */ | |
267 | ||
268 | if (bytes_to_copy == 0) | |
90703668 | 269 | return target.length - bytes_to_go; |
62e76326 | 270 | |
271 | location += bytes_to_copy; | |
272 | ||
273 | ptr_to_buf += bytes_to_copy; | |
274 | ||
275 | bytes_to_go -= bytes_to_copy; | |
276 | ||
42a503bd | 277 | p = getBlockContainingLocation(location); |
090089c4 | 278 | } |
62e76326 | 279 | |
90703668 | 280 | return target.length - bytes_to_go; |
090089c4 | 281 | } |
528b2c61 | 282 | |
283 | bool | |
47f6e231 | 284 | mem_hdr::hasContigousContentRange(Range<int64_t> const & range) const |
528b2c61 | 285 | { |
47f6e231 | 286 | int64_t currentStart = range.start; |
62e76326 | 287 | |
528b2c61 | 288 | while (mem_node *curr = getBlockContainingLocation(currentStart)) { |
62e76326 | 289 | currentStart = curr->end(); |
290 | ||
4c50505b | 291 | if (currentStart >= range.end) |
62e76326 | 292 | return true; |
528b2c61 | 293 | } |
62e76326 | 294 | |
9cc44692 | 295 | return !range.size(); // empty range is contigous |
528b2c61 | 296 | } |
297 | ||
298 | bool | |
299 | mem_hdr::unionNotEmpty(StoreIOBuffer const &candidate) | |
300 | { | |
528b2c61 | 301 | assert (candidate.offset >= 0); |
42a503bd | 302 | mem_node target(candidate.offset); |
303 | target.nodeBuffer.length = candidate.length; | |
304 | return nodes.find (&target, NodeCompare); | |
528b2c61 | 305 | } |
306 | ||
307 | mem_node * | |
47f6e231 | 308 | mem_hdr::nodeToRecieve(int64_t offset) |
528b2c61 | 309 | { |
310 | /* case 1: Nothing in memory */ | |
62e76326 | 311 | |
42a503bd | 312 | if (!nodes.size()) { |
62e76326 | 313 | appendNode (new mem_node(offset)); |
42a503bd | 314 | return nodes.start()->data; |
528b2c61 | 315 | } |
316 | ||
42a503bd | 317 | mem_node *candidate = NULL; |
528b2c61 | 318 | /* case 2: location fits within an extant node */ |
62e76326 | 319 | |
42a503bd | 320 | if (offset > 0) { |
321 | mem_node search (offset - 1); | |
322 | search.nodeBuffer.length = 1; | |
323 | mem_node *const *leadup = nodes.find (&search, NodeCompare); | |
324 | ||
325 | if (leadup) | |
326 | candidate = *leadup; | |
528b2c61 | 327 | } |
328 | ||
42a503bd | 329 | if (candidate && candidate->canAccept(offset)) |
62e76326 | 330 | return candidate; |
528b2c61 | 331 | |
332 | /* candidate can't accept, so we need a new node */ | |
333 | candidate = new mem_node(offset); | |
62e76326 | 334 | |
528b2c61 | 335 | appendNode (candidate); |
62e76326 | 336 | |
528b2c61 | 337 | /* simpler to write than a indented if */ |
338 | return candidate; | |
339 | } | |
340 | ||
528b2c61 | 341 | bool |
342 | mem_hdr::write (StoreIOBuffer const &writeBuffer) | |
343 | { | |
1d5161bd | 344 | PROF_start(mem_hdr_write); |
e2851fe7 | 345 | debugs(19, 6, "mem_hdr::write: " << this << " " << writeBuffer.range() << " object end " << endOffset()); |
528b2c61 | 346 | |
347 | if (unionNotEmpty(writeBuffer)) { | |
fa84c01d | 348 | debugs(19, DBG_CRITICAL, "mem_hdr::write: writeBuffer: " << writeBuffer.range()); |
db7778f1 | 349 | debugDump(); |
fbdf945d | 350 | fatal_dump("Attempt to overwrite already in-memory data. Preceding this there should be a mem_hdr::write output that lists the attempted write, and the currently present data. Please get a 'backtrace full' from this error - using the generated core, and file a bug report with the squid developers including the last 10 lines of cache.log and the backtrace.\n"); |
1d5161bd | 351 | PROF_stop(mem_hdr_write); |
62e76326 | 352 | return false; |
528b2c61 | 353 | } |
354 | ||
355 | assert (writeBuffer.offset >= 0); | |
356 | ||
357 | mem_node *target; | |
47f6e231 | 358 | int64_t currentOffset = writeBuffer.offset; |
528b2c61 | 359 | char *currentSource = writeBuffer.data; |
360 | size_t len = writeBuffer.length; | |
62e76326 | 361 | |
528b2c61 | 362 | while (len && (target = nodeToRecieve(currentOffset))) { |
62e76326 | 363 | size_t wrote = writeAvailable(target, currentOffset, len, currentSource); |
364 | assert (wrote); | |
365 | len -= wrote; | |
366 | currentOffset += wrote; | |
367 | currentSource += wrote; | |
528b2c61 | 368 | } |
369 | ||
1d5161bd | 370 | PROF_stop(mem_hdr_write); |
528b2c61 | 371 | return true; |
372 | } | |
4c50505b | 373 | |
42a503bd | 374 | mem_hdr::mem_hdr() : inmem_hi(0) |
e2851fe7 AR |
375 | { |
376 | debugs(19, 9, HERE << this << " hi: " << inmem_hi); | |
377 | } | |
4c50505b | 378 | |
379 | mem_hdr::~mem_hdr() | |
380 | { | |
381 | freeContent(); | |
382 | } | |
42a503bd | 383 | |
384 | /* splay of mem nodes: | |
385 | * conditions: | |
386 | * a = b if a.intersection(b).size > 0; | |
387 | * a < b if a < b | |
388 | */ | |
389 | int | |
390 | mem_hdr::NodeCompare(mem_node * const &left, mem_node * const &right) | |
391 | { | |
392 | // possibly Range can help us at some point. | |
393 | ||
394 | if (left->dataRange().intersection(right->dataRange()).size() > 0) | |
395 | return 0; | |
396 | ||
397 | return *left < *right ? -1 : 1; | |
398 | } | |
399 | ||
400 | void | |
401 | mem_hdr::dump() const | |
402 | { | |
e0236918 FC |
403 | debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.start() " << nodes.start()); |
404 | debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.finish() " << nodes.finish()); | |
42a503bd | 405 | } |
406 | ||
407 | size_t | |
408 | mem_hdr::size() const | |
409 | { | |
410 | return nodes.size(); | |
411 | } | |
412 | ||
413 | mem_node const * | |
414 | mem_hdr::start() const | |
415 | { | |
416 | const SplayNode<mem_node *> * result = nodes.start(); | |
417 | ||
418 | if (result) | |
419 | return result->data; | |
420 | ||
421 | return NULL; | |
422 | } | |
b8bad68c | 423 | |
424 | const Splay<mem_node *> & | |
425 | mem_hdr::getNodes() const | |
426 | { | |
427 | return nodes; | |
428 | } | |
f53969cc | 429 |