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;
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;
}
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
}
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;
}
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;
}
/// 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
}
Ipc::Mem::PagePool::PagePool(const char *const id):
- pageIndex(shm_old(PageStack)(id))
+ pageIndex(shm_old(PageStack)(id)),
+ theLevels(reinterpret_cast<AtomicWord *>(
+ reinterpret_cast<char *>(pageIndex.getRaw()) +
+ pageIndex->stackSize())),
+ theBuf(reinterpret_cast<char *>(theLevels + PageId::maxPurpose))
{
- const size_t pagesDataOffset =
- pageIndex->sharedMemorySize() - capacity() * pageSize();
- theBuf = reinterpret_cast<char *>(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 *
#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"
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<PageStack> 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
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);
}
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;
}
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
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
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 >= " <<
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);
#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);
/// 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();