6b9acd18a0
- fix for bug #772483 running out of memory, mem_node growing out of bounds
450 lines
17 KiB
Diff
450 lines
17 KiB
Diff
diff -ru squid-3.2.0.14/src/MemObject.cc squid-3.2.0.14-mem_node/src/MemObject.cc
|
|
--- squid-3.2.0.14/src/MemObject.cc 2011-12-12 12:08:18.000000000 +0100
|
|
+++ squid-3.2.0.14-mem_node/src/MemObject.cc 2012-01-17 20:11:54.756609310 +0100
|
|
@@ -492,3 +492,9 @@
|
|
}
|
|
|
|
#endif
|
|
+
|
|
+int64_t
|
|
+MemObject::availableForSwapOut() const
|
|
+{
|
|
+ return endOffset() - swapout.queue_offset;
|
|
+}
|
|
diff -ru squid-3.2.0.14/src/MemObject.h squid-3.2.0.14-mem_node/src/MemObject.h
|
|
--- squid-3.2.0.14/src/MemObject.h 2011-12-12 12:08:18.000000000 +0100
|
|
+++ squid-3.2.0.14-mem_node/src/MemObject.h 2012-01-17 20:11:54.757609437 +0100
|
|
@@ -81,6 +81,7 @@
|
|
*/
|
|
int64_t objectBytesOnDisk() const;
|
|
int64_t policyLowestOffsetToKeep(bool swap) const;
|
|
+ int64_t availableForSwapOut() const; ///< buffered bytes we have not swapped out yet
|
|
void trimSwappable();
|
|
void trimUnSwappable();
|
|
bool isContiguous() const;
|
|
diff -ru squid-3.2.0.14/src/store.cc squid-3.2.0.14-mem_node/src/store.cc
|
|
--- squid-3.2.0.14/src/store.cc 2011-12-12 12:08:18.000000000 +0100
|
|
+++ squid-3.2.0.14-mem_node/src/store.cc 2012-01-17 20:11:54.761609945 +0100
|
|
@@ -1890,95 +1890,8 @@
|
|
return result;
|
|
}
|
|
|
|
-bool
|
|
-StoreEntry::swapoutPossible()
|
|
-{
|
|
- if (!Config.cacheSwap.n_configured)
|
|
- return false;
|
|
-
|
|
- /* should we swap something out to disk? */
|
|
- debugs(20, 7, "storeSwapOut: " << url());
|
|
- debugs(20, 7, "storeSwapOut: store_status = " << storeStatusStr[store_status]);
|
|
-
|
|
- assert(mem_obj);
|
|
- MemObject::SwapOut::Decision &decision = mem_obj->swapout.decision;
|
|
-
|
|
- // if we decided that swapout is not possible, do not repeat same checks
|
|
- if (decision == MemObject::SwapOut::swImpossible) {
|
|
- debugs(20, 3, "storeSwapOut: already rejected");
|
|
- return false;
|
|
- }
|
|
-
|
|
- // this flag may change so we must check it even if we already said "yes"
|
|
- if (EBIT_TEST(flags, ENTRY_ABORTED)) {
|
|
- assert(EBIT_TEST(flags, RELEASE_REQUEST));
|
|
- // StoreEntry::abort() already closed the swap out file, if any
|
|
- decision = MemObject::SwapOut::swImpossible;
|
|
- return false;
|
|
- }
|
|
-
|
|
- // if we decided that swapout is possible, do not repeat same checks
|
|
- if (decision == MemObject::SwapOut::swPossible) {
|
|
- debugs(20, 3, "storeSwapOut: already allowed");
|
|
- return true;
|
|
- }
|
|
-
|
|
- // if we are swapping out already, do not repeat same checks
|
|
- if (swap_status != SWAPOUT_NONE) {
|
|
- debugs(20, 3, "storeSwapOut: already started");
|
|
- decision = MemObject::SwapOut::swPossible;
|
|
- return true;
|
|
- }
|
|
-
|
|
- if (!checkCachable()) {
|
|
- debugs(20, 3, "storeSwapOut: not cachable");
|
|
- decision = MemObject::SwapOut::swImpossible;
|
|
- return false;
|
|
- }
|
|
-
|
|
- if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
|
|
- debugs(20, 3, "storeSwapOut: " << url() << " SPECIAL");
|
|
- decision = MemObject::SwapOut::swImpossible;
|
|
- return false;
|
|
- }
|
|
-
|
|
- // check cache_dir max-size limit if all cache_dirs have it
|
|
- if (store_maxobjsize >= 0) {
|
|
- // TODO: add estimated store metadata size to be conservative
|
|
-
|
|
- // use guaranteed maximum if it is known
|
|
- const int64_t expectedEnd = mem_obj->expectedReplySize();
|
|
- debugs(20, 7, "storeSwapOut: expectedEnd = " << expectedEnd);
|
|
- if (expectedEnd > store_maxobjsize) {
|
|
- debugs(20, 3, "storeSwapOut: will not fit: " << expectedEnd <<
|
|
- " > " << store_maxobjsize);
|
|
- decision = MemObject::SwapOut::swImpossible;
|
|
- return false; // known to outgrow the limit eventually
|
|
- }
|
|
-
|
|
- // use current minimum (always known)
|
|
- const int64_t currentEnd = mem_obj->endOffset();
|
|
- if (currentEnd > store_maxobjsize) {
|
|
- debugs(20, 3, "storeSwapOut: does not fit: " << currentEnd <<
|
|
- " > " << store_maxobjsize);
|
|
- decision = MemObject::SwapOut::swImpossible;
|
|
- return false; // already does not fit and may only get bigger
|
|
- }
|
|
-
|
|
- // prevent default swPossible answer for yet unknown length
|
|
- if (expectedEnd < 0) {
|
|
- debugs(20, 3, "storeSwapOut: wait for more info: " <<
|
|
- store_maxobjsize);
|
|
- return false; // may fit later, but will be rejected now
|
|
- }
|
|
- }
|
|
-
|
|
- decision = MemObject::SwapOut::swPossible;
|
|
- return true;
|
|
-}
|
|
-
|
|
void
|
|
-StoreEntry::trimMemory()
|
|
+StoreEntry::trimMemory(const bool preserveSwappable)
|
|
{
|
|
/*
|
|
* DPW 2007-05-09
|
|
@@ -1988,7 +1901,7 @@
|
|
if (mem_status == IN_MEMORY)
|
|
return;
|
|
|
|
- if (!swapOutAble()) {
|
|
+ if (!preserveSwappable) {
|
|
if (mem_obj->policyLowestOffsetToKeep(0) == 0) {
|
|
/* Nothing to do */
|
|
return;
|
|
Endast i squid-3.2.0.14-mem_node/src: store.cc.orig
|
|
diff -ru squid-3.2.0.14/src/store_client.cc squid-3.2.0.14-mem_node/src/store_client.cc
|
|
--- squid-3.2.0.14/src/store_client.cc 2011-12-12 12:08:18.000000000 +0100
|
|
+++ squid-3.2.0.14-mem_node/src/store_client.cc 2012-01-17 20:11:54.768610834 +0100
|
|
@@ -194,7 +194,7 @@
|
|
if (getType() == STORE_DISK_CLIENT)
|
|
/* assert we'll be able to get the data we want */
|
|
/* maybe we should open swapin_sio here */
|
|
- assert(entry->swap_filen > -1 || entry->swapOutAble());
|
|
+ assert(entry->swap_filen > -1 || entry->swappingOut());
|
|
|
|
#if STORE_CLIENT_LIST_DEBUG
|
|
|
|
Endast i squid-3.2.0.14-mem_node/src: store_client.cc.orig
|
|
diff -ru squid-3.2.0.14/src/Store.h squid-3.2.0.14-mem_node/src/Store.h
|
|
--- squid-3.2.0.14/src/Store.h 2011-12-12 12:08:18.000000000 +0100
|
|
+++ squid-3.2.0.14-mem_node/src/Store.h 2012-01-17 20:11:56.770865312 +0100
|
|
@@ -92,8 +92,9 @@
|
|
virtual char const *getSerialisedMetaData();
|
|
void replaceHttpReply(HttpReply *, bool andStartWriting = true);
|
|
void startWriting(); ///< pack and write reply headers and, maybe, body
|
|
- virtual bool swapoutPossible();
|
|
- virtual void trimMemory();
|
|
+ /// whether we may start writing to disk (now or in the future)
|
|
+ virtual bool mayStartSwapOut();
|
|
+ virtual void trimMemory(const bool preserveSwappable);
|
|
void abort();
|
|
void unlink();
|
|
void makePublic();
|
|
@@ -108,7 +109,8 @@
|
|
void purgeMem();
|
|
void cacheInMemory(); ///< start or continue storing in memory cache
|
|
void swapOut();
|
|
- bool swapOutAble() const;
|
|
+ /// whether we are in the process of writing this entry to disk
|
|
+ bool swappingOut() const { return swap_status == SWAPOUT_WRITING; }
|
|
void swapOutFileClose(int how);
|
|
const char *url() const;
|
|
int checkCachable();
|
|
@@ -247,9 +249,9 @@
|
|
store_client_t storeClientType() const {return STORE_MEM_CLIENT;}
|
|
|
|
char const *getSerialisedMetaData();
|
|
- bool swapoutPossible() {return false;}
|
|
+ bool mayStartSwapout() {return false;}
|
|
|
|
- void trimMemory() {}
|
|
+ void trimMemory(const bool preserveSwappable) {}
|
|
|
|
|
|
static NullStoreEntry _instance;
|
|
diff -ru squid-3.2.0.14/src/store_swapout.cc squid-3.2.0.14-mem_node/src/store_swapout.cc
|
|
--- squid-3.2.0.14/src/store_swapout.cc 2011-12-12 12:08:18.000000000 +0100
|
|
+++ squid-3.2.0.14-mem_node/src/store_swapout.cc 2012-01-17 20:11:54.771611216 +0100
|
|
@@ -187,8 +187,20 @@
|
|
if (!mem_obj)
|
|
return;
|
|
|
|
- if (!swapoutPossible())
|
|
+ // this flag may change so we must check even if we are swappingOut
|
|
+ if (EBIT_TEST(flags, ENTRY_ABORTED)) {
|
|
+ assert(EBIT_TEST(flags, RELEASE_REQUEST));
|
|
+ // StoreEntry::abort() already closed the swap out file, if any
|
|
+ // no trimming: data producer must stop production if ENTRY_ABORTED
|
|
return;
|
|
+ }
|
|
+
|
|
+ const bool weAreOrMayBeSwappingOut = swappingOut() || mayStartSwapOut();
|
|
+
|
|
+ trimMemory(weAreOrMayBeSwappingOut);
|
|
+
|
|
+ if (!weAreOrMayBeSwappingOut)
|
|
+ return; // nothing else to do
|
|
|
|
// Aborted entries have STORE_OK, but swapoutPossible rejects them. Thus,
|
|
// store_status == STORE_OK below means we got everything we wanted.
|
|
@@ -200,39 +212,10 @@
|
|
if (mem_obj->swapout.sio != NULL)
|
|
debugs(20, 7, "storeSwapOut: storeOffset() = " << mem_obj->swapout.sio->offset() );
|
|
|
|
- // buffered bytes we have not swapped out yet
|
|
- int64_t swapout_maxsize = mem_obj->endOffset() - mem_obj->swapout.queue_offset;
|
|
-
|
|
- assert(swapout_maxsize >= 0);
|
|
-
|
|
int64_t const lowest_offset = mem_obj->lowestMemReaderOffset();
|
|
|
|
debugs(20, 7, HERE << "storeSwapOut: lowest_offset = " << lowest_offset);
|
|
|
|
- // Check to see whether we're going to defer the swapout based upon size
|
|
- if (store_status != STORE_OK) {
|
|
- const int64_t expectedSize = mem_obj->expectedReplySize();
|
|
- const int64_t maxKnownSize = expectedSize < 0 ?
|
|
- swapout_maxsize : expectedSize;
|
|
- debugs(20, 7, HERE << "storeSwapOut: maxKnownSize= " << maxKnownSize);
|
|
-
|
|
- if (maxKnownSize < store_maxobjsize) {
|
|
- /*
|
|
- * NOTE: the store_maxobjsize here is the max of optional
|
|
- * max-size values from 'cache_dir' lines. It is not the
|
|
- * same as 'maximum_object_size'. By default, store_maxobjsize
|
|
- * will be set to -1. However, I am worried that this
|
|
- * deferance may consume a lot of memory in some cases.
|
|
- * Should we add an option to limit this memory consumption?
|
|
- */
|
|
- debugs(20, 5, "storeSwapOut: Deferring swapout start for " <<
|
|
- (store_maxobjsize - maxKnownSize) << " bytes");
|
|
- return;
|
|
- }
|
|
- }
|
|
-
|
|
-// TODO: it is better to trim as soon as we swap something out, not before
|
|
- trimMemory();
|
|
#if SIZEOF_OFF_T <= 4
|
|
|
|
if (mem_obj->endOffset() > 0x7FFF0000) {
|
|
@@ -245,9 +228,9 @@
|
|
if (swap_status == SWAPOUT_WRITING)
|
|
assert(mem_obj->inmem_lo <= mem_obj->objectBytesOnDisk() );
|
|
|
|
- if (!swapOutAble())
|
|
- return;
|
|
-
|
|
+ // buffered bytes we have not swapped out yet
|
|
+ const int64_t swapout_maxsize = mem_obj->availableForSwapOut();
|
|
+ assert(swapout_maxsize >= 0);
|
|
debugs(20, 7, "storeSwapOut: swapout_size = " << swapout_maxsize);
|
|
|
|
if (swapout_maxsize == 0) { // swapped everything we got
|
|
@@ -373,19 +356,106 @@
|
|
e->unlock();
|
|
}
|
|
|
|
-/*
|
|
- * Is this entry a candidate for writing to disk?
|
|
- */
|
|
bool
|
|
-StoreEntry::swapOutAble() const
|
|
+StoreEntry::mayStartSwapOut()
|
|
{
|
|
dlink_node *node;
|
|
|
|
- if (mem_obj->swapout.sio != NULL)
|
|
+ // must be checked in the caller
|
|
+ assert(!EBIT_TEST(flags, ENTRY_ABORTED));
|
|
+
|
|
+ if (!Config.cacheSwap.n_configured)
|
|
+ return false;
|
|
+
|
|
+ assert(mem_obj);
|
|
+ MemObject::SwapOut::Decision &decision = mem_obj->swapout.decision;
|
|
+
|
|
+ // if we decided that swapout is not possible, do not repeat same checks
|
|
+ if (decision == MemObject::SwapOut::swImpossible) {
|
|
+ debugs(20, 3, HERE << " already rejected");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // if we decided that swapout is possible, do not repeat same checks
|
|
+ if (decision == MemObject::SwapOut::swPossible) {
|
|
+ debugs(20, 3, HERE << "already allowed");
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ // if we are swapping out already, do not repeat same checks
|
|
+ if (swap_status != SWAPOUT_NONE) {
|
|
+ debugs(20, 3, HERE << " already started");
|
|
+ decision = MemObject::SwapOut::swPossible;
|
|
return true;
|
|
+ }
|
|
|
|
- if (mem_obj->inmem_lo > 0)
|
|
+ if (!checkCachable()) {
|
|
+ debugs(20, 3, HERE << "not cachable");
|
|
+ decision = MemObject::SwapOut::swImpossible;
|
|
return false;
|
|
+ }
|
|
+
|
|
+ if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
|
|
+ debugs(20, 3, HERE << url() << " SPECIAL");
|
|
+ decision = MemObject::SwapOut::swImpossible;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // check cache_dir max-size limit if all cache_dirs have it
|
|
+ if (store_maxobjsize >= 0) {
|
|
+ // TODO: add estimated store metadata size to be conservative
|
|
+
|
|
+ // use guaranteed maximum if it is known
|
|
+ const int64_t expectedEnd = mem_obj->expectedReplySize();
|
|
+ debugs(20, 7, HERE << "expectedEnd = " << expectedEnd);
|
|
+ if (expectedEnd > store_maxobjsize) {
|
|
+ debugs(20, 3, HERE << "will not fit: " << expectedEnd <<
|
|
+ " > " << store_maxobjsize);
|
|
+ decision = MemObject::SwapOut::swImpossible;
|
|
+ return false; // known to outgrow the limit eventually
|
|
+ }
|
|
+
|
|
+ // use current minimum (always known)
|
|
+ const int64_t currentEnd = mem_obj->endOffset();
|
|
+ if (currentEnd > store_maxobjsize) {
|
|
+ debugs(20, 3, HERE << "does not fit: " << currentEnd <<
|
|
+ " > " << store_maxobjsize);
|
|
+ decision = MemObject::SwapOut::swImpossible;
|
|
+ return false; // already does not fit and may only get bigger
|
|
+ }
|
|
+
|
|
+ // prevent default swPossible answer for yet unknown length
|
|
+ if (expectedEnd < 0) {
|
|
+ debugs(20, 3, HERE << "wait for more info: " <<
|
|
+ store_maxobjsize);
|
|
+ return false; // may fit later, but will be rejected now
|
|
+ }
|
|
+
|
|
+ if (store_status != STORE_OK) {
|
|
+ const int64_t maxKnownSize = expectedEnd < 0 ?
|
|
+ mem_obj->availableForSwapOut() : expectedEnd;
|
|
+ debugs(20, 7, HERE << "maxKnownSize= " << maxKnownSize);
|
|
+ if (maxKnownSize < store_maxobjsize) {
|
|
+ /*
|
|
+ * NOTE: the store_maxobjsize here is the max of optional
|
|
+ * max-size values from 'cache_dir' lines. It is not the
|
|
+ * same as 'maximum_object_size'. By default, store_maxobjsize
|
|
+ * will be set to -1. However, I am worried that this
|
|
+ * deferance may consume a lot of memory in some cases.
|
|
+ * Should we add an option to limit this memory consumption?
|
|
+ */
|
|
+ debugs(20, 5, HERE << "Deferring swapout start for " <<
|
|
+ (store_maxobjsize - maxKnownSize) << " bytes");
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (mem_obj->inmem_lo > 0) {
|
|
+ debugs(20, 3, "storeSwapOut: (inmem_lo > 0) imem_lo:" << mem_obj->inmem_lo);
|
|
+ decision = MemObject::SwapOut::swImpossible;
|
|
+ return false;
|
|
+ }
|
|
|
|
/*
|
|
* If there are DISK clients, we must write to disk
|
|
@@ -394,21 +464,29 @@
|
|
* therefore this should be an assert?
|
|
* RBC 20030708: We can use disk to avoid mem races, so this shouldn't be
|
|
* an assert.
|
|
+ *
|
|
+ * XXX: Not clear what "mem races" the above refers to, especially when
|
|
+ * dealing with non-cachable objects that cannot have multiple clients.
|
|
+ *
|
|
+ * XXX: If STORE_DISK_CLIENT needs SwapOut::swPossible, we have to check
|
|
+ * for that flag earlier, but forcing swapping may contradict max-size or
|
|
+ * other swapability restrictions. Change storeClientType() and/or its
|
|
+ * callers to take swap-in availability into account.
|
|
*/
|
|
for (node = mem_obj->clients.head; node; node = node->next) {
|
|
- if (((store_client *) node->data)->getType() == STORE_DISK_CLIENT)
|
|
+ if (((store_client *) node->data)->getType() == STORE_DISK_CLIENT) {
|
|
+ debugs(20, 3, HERE << "DISK client found");
|
|
+ decision = MemObject::SwapOut::swPossible;
|
|
return true;
|
|
+ }
|
|
}
|
|
|
|
- /* Don't pollute the disk with icons and other special entries */
|
|
- if (EBIT_TEST(flags, ENTRY_SPECIAL))
|
|
- return false;
|
|
-
|
|
- if (!EBIT_TEST(flags, ENTRY_CACHABLE))
|
|
- return false;
|
|
-
|
|
- if (!mem_obj->isContiguous())
|
|
+ if (!mem_obj->isContiguous()) {
|
|
+ debugs(20, 3, "storeSwapOut: not Contiguous");
|
|
+ decision = MemObject::SwapOut::swImpossible;
|
|
return false;
|
|
+ }
|
|
|
|
+ decision = MemObject::SwapOut::swPossible;
|
|
return true;
|
|
}
|
|
Endast i squid-3.2.0.14-mem_node/src: store_swapout.cc.orig
|
|
diff -ru squid-3.2.0.14/src/tests/stub_MemObject.cc squid-3.2.0.14-mem_node/src/tests/stub_MemObject.cc
|
|
--- squid-3.2.0.14/src/tests/stub_MemObject.cc 2011-12-12 12:08:18.000000000 +0100
|
|
+++ squid-3.2.0.14-mem_node/src/tests/stub_MemObject.cc 2012-01-17 20:11:54.772611344 +0100
|
|
@@ -47,6 +47,7 @@
|
|
{
|
|
return data_hdr.endOffset();
|
|
}
|
|
+int64_t MemObject::availableForSwapOut() const STUB_RETVAL(0)
|
|
|
|
void
|
|
MemObject::trimSwappable()
|
|
Endast i squid-3.2.0.14-mem_node/src/tests: stub_MemObject.cc.orig
|
|
diff -ru squid-3.2.0.14/src/tests/stub_store.cc squid-3.2.0.14-mem_node/src/tests/stub_store.cc
|
|
--- squid-3.2.0.14/src/tests/stub_store.cc 2011-12-12 12:08:18.000000000 +0100
|
|
+++ squid-3.2.0.14-mem_node/src/tests/stub_store.cc 2012-01-17 20:11:54.773611472 +0100
|
|
@@ -26,8 +26,8 @@
|
|
store_client_t StoreEntry::storeClientType() const STUB_RETVAL(STORE_NON_CLIENT)
|
|
char const *StoreEntry::getSerialisedMetaData() STUB_RETVAL(NULL)
|
|
void StoreEntry::replaceHttpReply(HttpReply *, bool andStartWriting) STUB
|
|
-bool StoreEntry::swapoutPossible() STUB_RETVAL(false)
|
|
-void StoreEntry::trimMemory() STUB
|
|
+bool StoreEntry::mayStartSwapOut() STUB_RETVAL(false)
|
|
+void StoreEntry::trimMemory(const bool preserveSwappable) STUB
|
|
void StoreEntry::abort() STUB
|
|
void StoreEntry::unlink() STUB
|
|
void StoreEntry::makePublic() STUB
|
|
@@ -41,7 +41,6 @@
|
|
void StoreEntry::invokeHandlers() STUB
|
|
void StoreEntry::purgeMem() STUB
|
|
void StoreEntry::swapOut() STUB
|
|
-bool StoreEntry::swapOutAble() const STUB_RETVAL(false)
|
|
void StoreEntry::swapOutFileClose(int how) STUB
|
|
const char *StoreEntry::url() const STUB_RETVAL(NULL)
|
|
int StoreEntry::checkCachable() STUB_RETVAL(0)
|