From: Dmitry Kurochkin Date: Mon, 27 Jun 2011 20:58:29 +0000 (+0400) Subject: Separate shared page limits for different purposes. X-Git-Tag: take07~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=551f8a18645c80204c93af6f3d4c341cbaaa05e3;p=thirdparty%2Fsquid.git Separate shared page limits for different purposes. The patch implements separate level counters for shared pages with different purpose (i.e. memory cache and IPC I/O). A new purpose parameter was added to Ipc::Mem::GetPage() function to update the level and check the limits. After the change, memory cache and IPC I/O limits are separate and do not affect each other, i.e. full memory cache does not eat all IPC I/O pages. --- diff --git a/src/DiskIO/IpcIo/IpcIoFile.cc b/src/DiskIO/IpcIo/IpcIoFile.cc index a9a2bd497c..757571d19e 100644 --- a/src/DiskIO/IpcIo/IpcIoFile.cc +++ b/src/DiskIO/IpcIo/IpcIoFile.cc @@ -309,9 +309,9 @@ IpcIoFile::push(IpcIoPendingRequest *const pending) ipcIo.len = pending->readRequest->len; } else { // pending->writeRequest Must(pending->writeRequest->len <= Ipc::Mem::PageSize()); - if (!Ipc::Mem::GetPage(ipcIo.page)) { + if (!Ipc::Mem::GetPage(Ipc::Mem::PageId::ioPage, ipcIo.page)) { ipcIo.len = 0; - throw TexcHere("run out of shared memory pages"); + throw TexcHere("run out of shared memory pages for IPC I/O"); } ipcIo.command = IpcIo::cmdWrite; ipcIo.offset = pending->writeRequest->offset; @@ -551,9 +551,9 @@ static int TheFile = -1; ///< db file descriptor static void diskerRead(IpcIoMsg &ipcIo) { - if (!Ipc::Mem::GetPage(ipcIo.page)) { + if (!Ipc::Mem::GetPage(Ipc::Mem::PageId::ioPage, ipcIo.page)) { ipcIo.len = 0; - debugs(47,5, HERE << "run out of shared memory pages"); + debugs(47,5, HERE << "run out of shared memory pages for IPC I/O"); return; } diff --git a/src/MemStore.cc b/src/MemStore.cc index a59e4a0b25..e7621689d4 100644 --- a/src/MemStore.cc +++ b/src/MemStore.cc @@ -290,8 +290,7 @@ bool MemStore::copyToShm(StoreEntry &e, MemStoreMap::Extras &extras) { Ipc::Mem::PageId page; - if (Ipc::Mem::CachePageLevel() > Ipc::Mem::CachePageLimit() || - !Ipc::Mem::GetPage(page)) { + if (!Ipc::Mem::GetPage(Ipc::Mem::PageId::cachePage, page)) { debugs(20, 5, HERE << "No mem-cache page for " << e); return false; // GetPage is responsible for any cleanup on failures } diff --git a/src/fs/rock/RockSwapDir.cc b/src/fs/rock/RockSwapDir.cc index 9e74a296fe..f95f864b47 100644 --- a/src/fs/rock/RockSwapDir.cc +++ b/src/fs/rock/RockSwapDir.cc @@ -354,8 +354,10 @@ Rock::SwapDir::canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) if (!map) return false; - if (Ipc::Mem::IoPageLevel() > Ipc::Mem::IoPageLimit()) { - debugs(47, 5, HERE << "too few shared pages for IPC IO left"); + // Do not start I/O transaction is there are less than 10% free pages left. + // TODO: reserve page instead + if (Ipc::Mem::PageLevel(Ipc::Mem::PageId::ioPage) >= 0.9 * Ipc::Mem::PageLimit(Ipc::Mem::PageId::ioPage)) { + debugs(47, 5, HERE << "too few shared pages for IPC I/O left"); return false; } @@ -445,8 +447,10 @@ Rock::SwapDir::openStoreIO(StoreEntry &e, StoreIOState::STFNCB *cbFile, StoreIOS return NULL; } - if (Ipc::Mem::IoPageLevel() > Ipc::Mem::IoPageLimit()) { - debugs(47, 5, HERE << "too few shared pages for IPC IO left"); + // Do not start I/O transaction is there are less than 10% free pages left. + // TODO: reserve page instead + if (Ipc::Mem::PageLevel(Ipc::Mem::PageId::ioPage) >= 0.9 * Ipc::Mem::PageLimit(Ipc::Mem::PageId::ioPage)) { + debugs(47, 5, HERE << "too few shared pages for IPC I/O left"); return NULL; } diff --git a/src/ipc/mem/Page.h b/src/ipc/mem/Page.h index b8edf95b9e..b2f69df911 100644 --- a/src/ipc/mem/Page.h +++ b/src/ipc/mem/Page.h @@ -17,13 +17,16 @@ namespace Mem { /// Shared memory page identifier, address, or handler class PageId { public: - PageId(): pool(0), number(0) {} + PageId(): pool(0), number(0), purpose(maxPurpose) {} operator bool() const { return pool && number; } uint32_t pool; ///< page pool ID within Squid // uint32_t segment; ///< memory segment ID within the pool; unused for now uint32_t number; ///< page number within the segment + + enum Purpose { cachePage, ioPage, maxPurpose }; + Purpose purpose; ///< page purpose }; /// writes page address (e.g., "sh_page5.3"), for debugging diff --git a/src/ipc/mem/PagePool.cc b/src/ipc/mem/PagePool.cc index 843b28f1ae..36b5231775 100644 --- a/src/ipc/mem/PagePool.cc +++ b/src/ipc/mem/PagePool.cc @@ -23,11 +23,43 @@ Ipc::Mem::PagePool::Init(const char *const id, const unsigned int capacity, cons } Ipc::Mem::PagePool::PagePool(const char *const id): - pageIndex(shm_old(PageStack)(id)) + pageIndex(shm_old(PageStack)(id)), + theLevels(reinterpret_cast( + reinterpret_cast(pageIndex.getRaw()) + + pageIndex->stackSize())), + theBuf(reinterpret_cast(theLevels + PageId::maxPurpose)) { - const size_t pagesDataOffset = - pageIndex->sharedMemorySize() - capacity() * pageSize(); - theBuf = reinterpret_cast(pageIndex.getRaw()) + pagesDataOffset; +} + +size_t +Ipc::Mem::PagePool::level(const int purpose) const +{ + Must(0 <= purpose && purpose < PageId::maxPurpose); + return theLevels[purpose]; +} + +bool +Ipc::Mem::PagePool::get(const PageId::Purpose purpose, PageId &page) +{ + Must(0 <= purpose && purpose < PageId::maxPurpose); + if (pageIndex->pop(page)) { + page.purpose = purpose; + ++theLevels[purpose]; + return true; + } + return false; +} + +void +Ipc::Mem::PagePool::put(PageId &page) +{ + if (!page) + return; + + Must(0 <= page.purpose && page.purpose < PageId::maxPurpose); + --theLevels[page.purpose]; + page.purpose = PageId::maxPurpose; + return pageIndex->push(page); } char * diff --git a/src/ipc/mem/PagePool.h b/src/ipc/mem/PagePool.h index f2b0ef827b..44a59740ad 100644 --- a/src/ipc/mem/PagePool.h +++ b/src/ipc/mem/PagePool.h @@ -6,6 +6,7 @@ #ifndef SQUID_IPC_MEM_PAGE_POOL_H #define SQUID_IPC_MEM_PAGE_POOL_H +#include "ipc/mem/Page.h" #include "ipc/mem/PageStack.h" #include "ipc/mem/Pointer.h" @@ -28,17 +29,23 @@ public: size_t pageSize() const { return pageIndex->pageSize(); } /// lower bound for the number of free pages unsigned int size() const { return pageIndex->size(); } + /// approximate number of shared memory pages used now + size_t level() const { return capacity() - size(); } + /// approximate number of shared memory pages used now for given purpose + size_t level(const int purpose) const; /// sets page ID and returns true unless no free pages are found - bool get(PageId &page) { return pageIndex->pop(page); } + bool get(const PageId::Purpose purpose, PageId &page); /// makes identified page available as a free page to future get() callers - void put(PageId &page) { return pageIndex->push(page); } + void put(PageId &page); /// converts page handler into a temporary writeable shared memory pointer char *pagePointer(const PageId &page); private: Ipc::Mem::Pointer pageIndex; ///< free pages index - char *theBuf; ///< pages storage + /// number of shared memory pages used now for each purpose + AtomicWord *const theLevels; + char *const theBuf; ///< pages storage }; } // namespace Mem diff --git a/src/ipc/mem/PageStack.cc b/src/ipc/mem/PageStack.cc index 6061e3fbca..07789d55ad 100644 --- a/src/ipc/mem/PageStack.cc +++ b/src/ipc/mem/PageStack.cc @@ -113,5 +113,19 @@ Ipc::Mem::PageStack::sharedMemorySize() const size_t Ipc::Mem::PageStack::SharedMemorySize(const uint32_t, const unsigned int capacity, const size_t pageSize) { - return sizeof(PageStack) + capacity * (sizeof(Item) + pageSize); + const size_t levelsSize = PageId::maxPurpose * sizeof(AtomicWord); + const size_t pagesDataSize = capacity * pageSize; + return StackSize(capacity) + pagesDataSize + levelsSize; +} + +size_t +Ipc::Mem::PageStack::StackSize(const unsigned int capacity) +{ + return sizeof(PageStack) + capacity * sizeof(Item); +} + +size_t +Ipc::Mem::PageStack::stackSize() const +{ + return StackSize(theCapacity); } diff --git a/src/ipc/mem/PageStack.h b/src/ipc/mem/PageStack.h index 8c571702c9..d64cbd723d 100644 --- a/src/ipc/mem/PageStack.h +++ b/src/ipc/mem/PageStack.h @@ -39,6 +39,11 @@ public: static size_t SharedMemorySize(const uint32_t aPoolId, const unsigned int capacity, const size_t pageSize); size_t sharedMemorySize() const; + /// shared memory size required only by PageStack, excluding + /// shared counters and page data + static size_t StackSize(const unsigned int capacity); + size_t stackSize() const; + private: /// stack index and size type (may temporary go negative) typedef int Offset; diff --git a/src/ipc/mem/Pages.cc b/src/ipc/mem/Pages.cc index 312c186e34..cdd13af518 100644 --- a/src/ipc/mem/Pages.cc +++ b/src/ipc/mem/Pages.cc @@ -27,9 +27,10 @@ Ipc::Mem::PageSize() { } bool -Ipc::Mem::GetPage(PageId &page) +Ipc::Mem::GetPage(const PageId::Purpose purpose, PageId &page) { - return ThePagePool ? ThePagePool->get(page) : false; + return ThePagePool && PagesAvailable(purpose) > 0 ? + ThePagePool->get(purpose, page) : false; } void @@ -49,44 +50,41 @@ Ipc::Mem::PagePointer(const PageId &page) size_t Ipc::Mem::PageLimit() { - return ThePagePool ? ThePagePool->capacity() : 0; + size_t limit = 0; + for (int i = 0; i < PageId::maxPurpose; ++i) + limit += PageLimit(i); + return limit; } size_t -Ipc::Mem::CachePageLimit() +Ipc::Mem::PageLimit(const int purpose) { - // TODO: adjust cache_mem description to say that in SMP mode, - // in-transit objects are not allocated using cache_mem. Eventually, - // they should not use cache_mem even if shared memory is not used: - // in-transit objects have nothing to do with caching. - return Config.memMaxSize > 0 ? Config.memMaxSize / PageSize() : 0; -} - -size_t -Ipc::Mem::IoPageLimit() -{ - // XXX: this should be independent from memory cache pages - return CachePageLimit(); + switch (purpose) { + case PageId::cachePage: + // TODO: adjust cache_mem description to say that in SMP mode, + // in-transit objects are not allocated using cache_mem. Eventually, + // they should not use cache_mem even if shared memory is not used: + // in-transit objects have nothing to do with caching. + return Config.memMaxSize > 0 ? Config.memMaxSize / PageSize() : 0; + case PageId::ioPage: + // XXX: this should be independent from memory cache pages + return PageLimit(PageId::cachePage) * 0.5; + default: + Must(false); + } + return 0; } size_t Ipc::Mem::PageLevel() { - return ThePagePool ? ThePagePool->capacity() - ThePagePool->size() : 0; -} - -size_t -Ipc::Mem::CachePageLevel() -{ - // TODO: make a separate counter for shared memory pages for memory cache - return PageLevel(); + return ThePagePool ? ThePagePool->level() : 0; } size_t -Ipc::Mem::IoPageLevel() +Ipc::Mem::PageLevel(const int purpose) { - // TODO: make a separate counter for shared memory pages for IPC I/O - return PageLevel(); + return ThePagePool ? ThePagePool->level(purpose) : 0; } /// initializes shared memory pages @@ -115,7 +113,7 @@ void SharedMemPagesRr::run(const RunnerRegistry &) if (Config.memMaxSize <= 0) return; - if (Ipc::Mem::CachePageLimit() <= 0) { + if (Ipc::Mem::PageLimit() <= 0) { if (IamMasterProcess()) { debugs(54, DBG_IMPORTANT, "WARNING: mem-cache size is too small (" << (Config.memMaxSize / 1024.0) << " KB), should be >= " << @@ -126,9 +124,7 @@ void SharedMemPagesRr::run(const RunnerRegistry &) if (IamMasterProcess()) { Must(!owner); - // reserve 10% for IPC I/O - const size_t capacity = Ipc::Mem::CachePageLimit() * 1.1; - owner = Ipc::Mem::PagePool::Init(PagePoolId, capacity, Ipc::Mem::PageSize()); + owner = Ipc::Mem::PagePool::Init(PagePoolId, Ipc::Mem::PageLimit(), Ipc::Mem::PageSize()); } Must(!ThePagePool); diff --git a/src/ipc/mem/Pages.h b/src/ipc/mem/Pages.h index a8e06f0b8b..e447c51f20 100644 --- a/src/ipc/mem/Pages.h +++ b/src/ipc/mem/Pages.h @@ -6,16 +6,16 @@ #ifndef SQUID_IPC_MEM_PAGES_H #define SQUID_IPC_MEM_PAGES_H +#include "ipc/mem/Page.h" + namespace Ipc { namespace Mem { -class PageId; - /* Single page manipulation */ /// sets page ID and returns true unless no free pages are found -bool GetPage(PageId &page); +bool GetPage(const PageId::Purpose purpose, PageId &page); /// makes identified page available as a free page to future GetPage() callers void PutPage(PageId &page); @@ -29,32 +29,22 @@ char *PagePointer(const PageId &page); /// the total number of shared memory pages that can be in use at any time size_t PageLimit(); -/// the total number of shared memory pages for memory cache that can be in -/// use at any time -size_t CachePageLimit(); - -/// the total number of shared memory pages for IPC I/O that can be in -/// use at any time -size_t IoPageLimit(); +/// the total number of shared memory pages that can be in use at any +/// time for given purpose +size_t PageLimit(const int purpose); /// approximate total number of shared memory pages used now size_t PageLevel(); -/// approximate total number of shared memory pages for memory cache used now -size_t CachePageLevel(); - -/// approximate total number of shared memory pages for IPC I/O used now -size_t IoPageLevel(); +/// approximate total number of shared memory pages used now for given purpose +size_t PageLevel(const int purpose); /// approximate total number of shared memory pages we can allocate now inline size_t PagesAvailable() { return PageLimit() - PageLevel(); } -/// approximate total number of shared memory pages for memory cache we can -/// allocate now -inline size_t CachePagesAvailable() { return CachePageLimit() - CachePageLevel(); } - -/// approximate total number of shared memory pages for IPC I/O we can allocate now -inline size_t IoPagesAvailable() { return IoPageLimit() - IoPageLevel(); } +/// approximate total number of shared memory pages we can allocate +/// now for given purpose +inline size_t PagesAvailable(const int purpose) { return PageLimit(purpose) - PageLevel(purpose); } /// returns page size in bytes; all pages are assumed to be the same size size_t PageSize();