]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/store_swapout.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / store_swapout.cc
index a2d613bf9eaa20757a7b5022c013411b0effee2d..53121e34b4ca48975864c01bb647940da6719875 100644 (file)
@@ -1,7 +1,5 @@
 
 /*
- * $Id$
- *
  * DEBUG: section 20    Storage Manager Swapout Functions
  * AUTHOR: Duane Wessels
  *
 
 #include "squid.h"
 #include "cbdata.h"
-#include "StoreClient.h"
+#include "globals.h"
 #include "Store.h"
+#include "StoreClient.h"
 /* FIXME: Abstract the use of this more */
 #include "mem_node.h"
 #include "MemObject.h"
-#include "SwapDir.h"
+#include "SquidConfig.h"
+#include "StatCounters.h"
+#include "store_log.h"
 #include "swap_log_op.h"
+#include "SwapDir.h"
 
 static void storeSwapOutStart(StoreEntry * e);
 static StoreIOState::STIOCB storeSwapOutFileClosed;
@@ -177,7 +179,6 @@ doPages(StoreEntry *anEntry)
     } while (true);
 }
 
-
 /* This routine is called every time data is sent to the client side.
  * It's overhead is therefor, significant.
  */
@@ -187,8 +188,20 @@ StoreEntry::swapOut()
     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();
+
+    Store::Root().maybeTrimMemory(*this, weAreOrMayBeSwappingOut);
+
+    if (mem_obj->swapout.decision != MemObject::SwapOut::swPossible)
+        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,43 +213,14 @@ StoreEntry::swapOut()
     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) {
-        debugs(20, 0, "WARNING: preventing off_t overflow for " << url());
+        debugs(20, DBG_CRITICAL, "WARNING: preventing off_t overflow for " << url());
         abort();
         return;
     }
@@ -245,9 +229,9 @@ StoreEntry::swapOut()
     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
@@ -351,10 +335,10 @@ storeSwapOutFileClosed(void *data, int errflag, StoreIOState::Pointer self)
                std::uppercase << e->swap_filen);
         debugs(20, 5, HERE << "swap_file_sz = " <<
                e->objectLen() << " + " << mem->swap_hdr_sz);
-        assert(e->objectLen() >= 0); // we checked that above
+
         e->swap_file_sz = e->objectLen() + mem->swap_hdr_sz;
         e->swap_status = SWAPOUT_DONE;
-        e->store()->updateSize(e->swap_file_sz, 1);
+        e->store()->swappedOut(*e);
 
         // XXX: For some Stores, it is pointless to re-check cachability here
         // and it leads to double counts in store_check_cachable_hist. We need
@@ -365,7 +349,7 @@ storeSwapOutFileClosed(void *data, int errflag, StoreIOState::Pointer self)
             storeDirSwapLog(e, SWAP_LOG_ADD);
         }
 
-        statCounter.swap.outs++;
+        ++statCounter.swap.outs;
     }
 
     debugs(20, 3, "storeSwapOutFileClosed: " << __FILE__ << ":" << __LINE__);
@@ -373,42 +357,104 @@ storeSwapOutFileClosed(void *data, int errflag, StoreIOState::Pointer self)
     e->unlock();
 }
 
-/*
- * Is this entry a candidate for writing to disk?
- */
 bool
-StoreEntry::swapOutAble() const
+StoreEntry::mayStartSwapOut()
 {
-    dlink_node *node;
+    // must be checked in the caller
+    assert(!EBIT_TEST(flags, ENTRY_ABORTED));
+    assert(!swappingOut());
 
-    if (mem_obj->swapout.sio != NULL)
+    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 (mem_obj->inmem_lo > 0)
+    // if we swapped out already, do not start over
+    if (swap_status == SWAPOUT_DONE) {
+        debugs(20, 3,  HERE << "already did");
+        decision = MemObject::SwapOut::swImpossible;
         return false;
+    }
 
-    /*
-     * If there are DISK clients, we must write to disk
-     * even if its not cachable
-     * RBC: Surely we should not create disk client on non cacheable objects?
-     * therefore this should be an assert?
-     * RBC 20030708: We can use disk to avoid mem races, so this shouldn't be
-     * an assert.
-     */
-    for (node = mem_obj->clients.head; node; node = node->next) {
-        if (((store_client *) node->data)->getType() == STORE_DISK_CLIENT)
-            return true;
+    if (!checkCachable()) {
+        debugs(20, 3,  HERE << "not cachable");
+        decision = MemObject::SwapOut::swImpossible;
+        return false;
     }
 
-    /* Don't pollute the disk with icons and other special entries */
-    if (EBIT_TEST(flags, ENTRY_SPECIAL))
+    if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
+        debugs(20, 3,  HERE  << url() << " SPECIAL");
+        decision = MemObject::SwapOut::swImpossible;
         return false;
+    }
 
-    if (!EBIT_TEST(flags, ENTRY_CACHABLE))
+    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 (!mem_obj->isContiguous())
+    if (!mem_obj->isContiguous()) {
+        debugs(20, 3, "storeSwapOut: not Contiguous");
+        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 final default swPossible answer for yet unknown length
+        if (expectedEnd < 0 && store_status != STORE_OK) {
+            const int64_t maxKnownSize = mem_obj->availableForSwapOut();
+            debugs(20, 7, HERE << "maxKnownSize= " << maxKnownSize);
+            /*
+             * NOTE: the store_maxobjsize here is the global maximum
+             * size of object cacheable in any of Squid cache stores
+             * both disk and memory stores.
+             *
+             * 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 true; // may still fit, but no final decision yet
+        }
+    }
 
+    decision = MemObject::SwapOut::swPossible;
     return true;
 }