kernel/vm: Per-KDiskDevice modified queues and page writers.
This solves an old TODO, that the page writer could potentially cause deadlocks when writing pages of different devices back at once. It's also necessary after the previous change, as otherwise simultaneous writes to disks with different write speeds would cause the quota computations to be fluctuating and inconsistent. Change-Id: I1c485f66625ea9013f17ab4fee007d7d58afd2ea Reviewed-on: https://review.haiku-os.org/c/haiku/+/8619 Reviewed-by: waddlesplash <[email protected]> Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
de0a0420a8
commit
3bfaa91290
@@ -13,6 +13,9 @@
|
||||
#include "KPartition.h"
|
||||
|
||||
|
||||
class ModifiedPageQueue;
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
namespace DiskDevice {
|
||||
|
||||
@@ -65,6 +68,8 @@ public:
|
||||
// File descriptor: valid only for kernel threads.
|
||||
int FD() const;
|
||||
|
||||
ModifiedPageQueue* ModifiedQueue();
|
||||
|
||||
// access to C style device data
|
||||
disk_device_data *DeviceData();
|
||||
const disk_device_data *DeviceData() const;
|
||||
@@ -85,6 +90,7 @@ private:
|
||||
void _UpdateDeviceFlags();
|
||||
|
||||
disk_device_data fDeviceData;
|
||||
ModifiedPageQueue* fModifiedQueue;
|
||||
rw_lock fLocker;
|
||||
int fFD;
|
||||
status_t fMediaStatus;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
struct kernel_args;
|
||||
struct ObjectCache;
|
||||
class ModifiedPageQueue;
|
||||
|
||||
|
||||
enum {
|
||||
@@ -166,6 +167,7 @@ public:
|
||||
generic_size_t numBytes, uint32 flags,
|
||||
AsyncIOCallback* callback);
|
||||
virtual bool CanWritePage(off_t offset);
|
||||
virtual ModifiedPageQueue* ModifiedQueue();
|
||||
status_t WriteModified();
|
||||
|
||||
virtual int32 MaxPagesPerWrite() const
|
||||
@@ -262,7 +264,7 @@ public:
|
||||
int32 numGuardPages, bool swappable,
|
||||
int priority);
|
||||
static status_t CreateVnodeCache(VMCache*& cache,
|
||||
struct vnode* vnode);
|
||||
struct vnode* vnode, ModifiedPageQueue* queue);
|
||||
static status_t CreateDeviceCache(VMCache*& cache,
|
||||
addr_t baseAddress);
|
||||
static status_t CreateNullCache(int priority, VMCache*& cache);
|
||||
|
||||
@@ -113,7 +113,8 @@ area_id vm_clone_area(team_id team, const char *name, void **address,
|
||||
area_id sourceArea, bool kernel);
|
||||
status_t vm_change_clones_to_null_areas(area_id area);
|
||||
status_t vm_delete_area(team_id teamID, area_id areaID, bool kernel);
|
||||
status_t vm_create_vnode_cache(struct vnode *vnode, struct VMCache **_cache);
|
||||
status_t vm_create_vnode_cache(struct vnode *vnode, struct ModifiedPageQueue* queue,
|
||||
struct VMCache **_cache);
|
||||
status_t vm_set_area_memory_type(area_id id, phys_addr_t physicalBase,
|
||||
uint32 type);
|
||||
status_t vm_set_area_protection(area_id areaID,
|
||||
|
||||
+11
-7
@@ -542,14 +542,15 @@ write_to_cache(file_cache_ref* ref, void* cookie, off_t offset,
|
||||
// TODO: the pages we allocate here should have been reserved upfront
|
||||
// in cache_io()
|
||||
vm_page* page = pages[pageIndex++] = vm_page_allocate_page(
|
||||
reservation,
|
||||
(writeThrough ? PAGE_STATE_CACHED : PAGE_STATE_MODIFIED)
|
||||
| VM_PAGE_ALLOC_BUSY);
|
||||
reservation, PAGE_STATE_CACHED | VM_PAGE_ALLOC_BUSY);
|
||||
page->busy_io = true;
|
||||
|
||||
page->modified = !writeThrough;
|
||||
|
||||
ref->cache->InsertPage(page, offset + pos);
|
||||
|
||||
page->modified = !writeThrough;
|
||||
if (!writeThrough)
|
||||
vm_page_set_state(page, PAGE_STATE_MODIFIED);
|
||||
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
|
||||
add_to_iovec(vecs, vecCount, MAX_IO_VECS,
|
||||
@@ -794,8 +795,11 @@ do_cache_io(void* _cacheRef, void* cookie, off_t offset, addr_t buffer,
|
||||
AutoLocker<VMCache> locker(cache);
|
||||
|
||||
ModifiedPageQueue* modifiedQueue = NULL;
|
||||
if (doWrite)
|
||||
modifiedQueue = vm_page_get_modified_queue();
|
||||
if (doWrite) {
|
||||
modifiedQueue = cache->ModifiedQueue();
|
||||
if (modifiedQueue == NULL)
|
||||
modifiedQueue = vm_page_default_modified_queue();
|
||||
}
|
||||
|
||||
size_t bytesLeft = size, lastLeft = size;
|
||||
int32 lastPageOffset = pageOffset;
|
||||
|
||||
+23
-1
@@ -15,10 +15,24 @@
|
||||
#include <vm/vm.h>
|
||||
|
||||
#include "IORequest.h"
|
||||
#include "../vm/ModifiedPageQueue.h"
|
||||
|
||||
|
||||
VMVnodeCache::VMVnodeCache()
|
||||
{
|
||||
// empty, but needed due to the BReference<> member
|
||||
}
|
||||
|
||||
|
||||
VMVnodeCache::~VMVnodeCache()
|
||||
{
|
||||
fModifiedPageQueue.Unset();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMVnodeCache::Init(struct vnode* vnode, uint32 allocationFlags)
|
||||
VMVnodeCache::Init(struct vnode* vnode, ModifiedPageQueue* modifiedQueue,
|
||||
uint32 allocationFlags)
|
||||
{
|
||||
status_t error = VMCache::Init("VMVnodeCache", CACHE_TYPE_VNODE, allocationFlags);
|
||||
if (error != B_OK)
|
||||
@@ -26,6 +40,7 @@ VMVnodeCache::Init(struct vnode* vnode, uint32 allocationFlags)
|
||||
|
||||
fVnode = vnode;
|
||||
fFileCacheRef = NULL;
|
||||
fModifiedPageQueue.SetTo(modifiedQueue, false);
|
||||
fVnodeDeleted = false;
|
||||
|
||||
vfs_vnode_to_node_ref(fVnode, &fDevice, &fInode);
|
||||
@@ -130,6 +145,13 @@ VMVnodeCache::CanWritePage(off_t offset)
|
||||
}
|
||||
|
||||
|
||||
ModifiedPageQueue*
|
||||
VMVnodeCache::ModifiedQueue()
|
||||
{
|
||||
return fModifiedPageQueue.Get();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMVnodeCache::AcquireUnreferencedStoreRef()
|
||||
{
|
||||
|
||||
+6
@@ -8,6 +8,7 @@
|
||||
|
||||
|
||||
#include <vm/VMCache.h>
|
||||
#include <Referenceable.h>
|
||||
|
||||
|
||||
struct file_cache_ref;
|
||||
@@ -16,7 +17,10 @@ struct file_cache_ref;
|
||||
class VMVnodeCache final : public VMCache {
|
||||
public:
|
||||
status_t Init(struct vnode* vnode,
|
||||
ModifiedPageQueue* modifiedQueue,
|
||||
uint32 allocationFlags);
|
||||
VMVnodeCache();
|
||||
virtual ~VMVnodeCache();
|
||||
|
||||
virtual status_t Commit(off_t size, int priority);
|
||||
virtual bool StoreHasPage(off_t offset);
|
||||
@@ -32,6 +36,7 @@ public:
|
||||
generic_size_t numBytes, uint32 flags,
|
||||
AsyncIOCallback* callback);
|
||||
virtual bool CanWritePage(off_t offset);
|
||||
virtual ModifiedPageQueue* ModifiedQueue();
|
||||
|
||||
virtual status_t Fault(struct VMAddressSpace* aspace,
|
||||
off_t offset);
|
||||
@@ -62,6 +67,7 @@ private:
|
||||
file_cache_ref* fFileCacheRef;
|
||||
ino_t fInode;
|
||||
dev_t fDevice;
|
||||
BReference<ModifiedPageQueue> fModifiedPageQueue;
|
||||
volatile bool fVnodeDeleted;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
|
||||
#include "../vm/ModifiedPageQueue.h"
|
||||
#include "ddm_userland_interface.h"
|
||||
#include "KDiskDeviceUtils.h"
|
||||
#include "KPath.h"
|
||||
@@ -31,6 +32,7 @@ KDiskDevice::KDiskDevice(partition_id id)
|
||||
:
|
||||
KPartition(id),
|
||||
fDeviceData(),
|
||||
fModifiedQueue(NULL),
|
||||
fFD(-1),
|
||||
fMediaStatus(B_ERROR)
|
||||
{
|
||||
@@ -80,6 +82,12 @@ KDiskDevice::SetTo(const char* path)
|
||||
_ResetGeometry();
|
||||
}
|
||||
|
||||
fModifiedQueue = new ModifiedPageQueue;
|
||||
fModifiedQueue->Init();
|
||||
error = fModifiedQueue->StartWriter(path);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
_UpdateDeviceFlags();
|
||||
_InitPartitionData();
|
||||
return B_OK;
|
||||
@@ -93,6 +101,10 @@ KDiskDevice::Unset()
|
||||
close(fFD);
|
||||
fFD = -1;
|
||||
}
|
||||
if (fModifiedQueue != NULL) {
|
||||
fModifiedQueue->ReleaseReference();
|
||||
fModifiedQueue = NULL;
|
||||
}
|
||||
fMediaStatus = B_ERROR;
|
||||
fDeviceData.id = -1;
|
||||
fDeviceData.flags = 0;
|
||||
@@ -280,6 +292,13 @@ KDiskDevice::FD() const
|
||||
}
|
||||
|
||||
|
||||
ModifiedPageQueue*
|
||||
KDiskDevice::ModifiedQueue()
|
||||
{
|
||||
return fModifiedQueue;
|
||||
}
|
||||
|
||||
|
||||
disk_device_data*
|
||||
KDiskDevice::DeviceData()
|
||||
{
|
||||
|
||||
@@ -4769,10 +4769,14 @@ vfs_get_vnode_cache(struct vnode* vnode, VMCache** _cache, bool allocate)
|
||||
bool wasBusy = vnode->IsBusy();
|
||||
vnode->SetBusy(true);
|
||||
|
||||
ModifiedPageQueue* queue = NULL;
|
||||
if (vnode->mount->partition != NULL)
|
||||
queue = vnode->mount->partition->Device()->ModifiedQueue();
|
||||
|
||||
vnode->Unlock();
|
||||
rw_lock_read_unlock(&sVnodeLock);
|
||||
|
||||
status = vm_create_vnode_cache(vnode, &vnode->cache);
|
||||
status = vm_create_vnode_cache(vnode, queue, &vnode->cache);
|
||||
|
||||
rw_lock_read_lock(&sVnodeLock);
|
||||
vnode->Lock();
|
||||
|
||||
@@ -6,14 +6,20 @@
|
||||
#define MODIFIED_PAGE_QUEUE_H
|
||||
|
||||
|
||||
#include <Referenceable.h>
|
||||
#include <util/BinarySemaphore.h>
|
||||
|
||||
#include "VMPageQueue.h"
|
||||
|
||||
|
||||
struct ModifiedPageQueue : public VMPageQueue {
|
||||
struct ModifiedPageQueue : public BReferenceable, public VMPageQueue {
|
||||
public:
|
||||
status_t StartWriter();
|
||||
static int64 GlobalModifiedCount()
|
||||
{ return atomic_get64(&sGlobalModifiedCount); }
|
||||
|
||||
virtual ~ModifiedPageQueue();
|
||||
|
||||
status_t StartWriter(const char* name);
|
||||
void NotifyWriter() { fPageWriterCondition.WakeUp(); }
|
||||
|
||||
bool IsOverQuota(page_num_t additionalPages = 0);
|
||||
@@ -30,10 +36,17 @@ private:
|
||||
ConditionVariable fUnderQuotaCondition;
|
||||
|
||||
bigtime_t fLastAveragePageWriteDuration;
|
||||
|
||||
private:
|
||||
static int64 sGlobalModifiedCount;
|
||||
int64 fLastReportedModifiedCount = 0;
|
||||
|
||||
static bigtime_t sGlobalEstimatedWriteDuration;
|
||||
bigtime_t fLastReportedEstimatedWriteDuration = 0;
|
||||
};
|
||||
|
||||
|
||||
ModifiedPageQueue* vm_page_get_modified_queue();
|
||||
ModifiedPageQueue* vm_page_default_modified_queue();
|
||||
|
||||
|
||||
#endif // MODIFIED_PAGE_QUEUE_H
|
||||
|
||||
@@ -1139,12 +1139,21 @@ VMCache::_FreePageRange(VMCachePagesTree::Iterator it,
|
||||
DEBUG_PAGE_ACCESS_START(page);
|
||||
vm_remove_all_page_mappings(page);
|
||||
|
||||
if (page->State() == PAGE_STATE_MODIFIED) {
|
||||
// pages can't be freed in MODIFIED state
|
||||
page->modified = false;
|
||||
vm_page_set_state(page, PAGE_STATE_CACHED);
|
||||
}
|
||||
|
||||
RemovePage(page);
|
||||
// Note: When iterating through a IteratableSplayTree
|
||||
// removing the current node is safe.
|
||||
|
||||
if (page->busy) {
|
||||
// As the page has been "removed" from this cache,
|
||||
// we can wake up anyone waiting on it.
|
||||
NotifyPageEvents(page, PAGE_EVENT_NOT_BUSY);
|
||||
|
||||
fRemovedBusyPages.Add(page);
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
} else {
|
||||
@@ -1417,6 +1426,13 @@ VMCache::CanWritePage(off_t offset)
|
||||
}
|
||||
|
||||
|
||||
ModifiedPageQueue*
|
||||
VMCache::ModifiedQueue()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMCache::Fault(struct VMAddressSpace *aspace, off_t offset)
|
||||
{
|
||||
@@ -1678,7 +1694,8 @@ VMCacheFactory::CreateAnonymousCache(VMCache*& _cache, bool canOvercommit,
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
VMCacheFactory::CreateVnodeCache(VMCache*& _cache, struct vnode* vnode)
|
||||
VMCacheFactory::CreateVnodeCache(VMCache*& _cache, struct vnode* vnode,
|
||||
ModifiedPageQueue* modifiedQueue)
|
||||
{
|
||||
const uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY
|
||||
| HEAP_DONT_LOCK_KERNEL_SPACE;
|
||||
@@ -1689,7 +1706,7 @@ VMCacheFactory::CreateVnodeCache(VMCache*& _cache, struct vnode* vnode)
|
||||
if (cache == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t error = cache->Init(vnode, allocationFlags);
|
||||
status_t error = cache->Init(vnode, modifiedQueue, allocationFlags);
|
||||
if (error != B_OK) {
|
||||
cache->Delete();
|
||||
return error;
|
||||
|
||||
@@ -2205,9 +2205,10 @@ vm_create_null_area(team_id team, const char* name, void** address,
|
||||
The vnode has to be marked busy when calling this function.
|
||||
*/
|
||||
status_t
|
||||
vm_create_vnode_cache(struct vnode* vnode, struct VMCache** cache)
|
||||
vm_create_vnode_cache(struct vnode *vnode, ModifiedPageQueue* queue,
|
||||
VMCache **cache)
|
||||
{
|
||||
return VMCacheFactory::CreateVnodeCache(*cache, vnode);
|
||||
return VMCacheFactory::CreateVnodeCache(*cache, vnode, queue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ int32 gMappedPagesCount;
|
||||
|
||||
static VMPageQueue sFreePageQueue;
|
||||
static VMPageQueue sClearPageQueue;
|
||||
static ModifiedPageQueue sModifiedPageQueue;
|
||||
static ModifiedPageQueue sDefaultModifiedPageQueue;
|
||||
static VMPageQueue sInactivePageQueue;
|
||||
static VMPageQueue sActivePageQueue;
|
||||
static VMPageQueue sCachedPageQueue;
|
||||
@@ -720,23 +720,8 @@ dump_page_list(int argc, char **argv)
|
||||
static int
|
||||
find_page(int argc, char **argv)
|
||||
{
|
||||
struct vm_page *page;
|
||||
addr_t address;
|
||||
int32 index = 1;
|
||||
int i;
|
||||
|
||||
struct {
|
||||
const char* name;
|
||||
VMPageQueue* queue;
|
||||
} pageQueueInfos[] = {
|
||||
{ "free", &sFreePageQueue },
|
||||
{ "clear", &sClearPageQueue },
|
||||
{ "modified", &sModifiedPageQueue },
|
||||
{ "active", &sActivePageQueue },
|
||||
{ "inactive", &sInactivePageQueue },
|
||||
{ "cached", &sCachedPageQueue },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
if (argc < 2
|
||||
|| strlen(argv[index]) <= 2
|
||||
@@ -747,9 +732,26 @@ find_page(int argc, char **argv)
|
||||
}
|
||||
|
||||
address = strtoul(argv[index], NULL, 0);
|
||||
page = (vm_page*)address;
|
||||
struct vm_page *page = (vm_page*)address;
|
||||
|
||||
struct {
|
||||
const char* name;
|
||||
VMPageQueue* queue;
|
||||
} pageQueueInfos[] = {
|
||||
{ "free", &sFreePageQueue },
|
||||
{ "clear", &sClearPageQueue },
|
||||
{ "modified-default", &sDefaultModifiedPageQueue },
|
||||
{ "modified-cache", page->Cache() != NULL ? page->Cache()->ModifiedQueue() : NULL },
|
||||
{ "active", &sActivePageQueue },
|
||||
{ "inactive", &sInactivePageQueue },
|
||||
{ "cached", &sCachedPageQueue },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
for (int i = 0; pageQueueInfos[i].name; i++) {
|
||||
if (pageQueueInfos[i].queue == NULL)
|
||||
continue;
|
||||
|
||||
for (i = 0; pageQueueInfos[i].name; i++) {
|
||||
VMPageQueue::Iterator it = pageQueueInfos[i].queue->GetIterator();
|
||||
while (vm_page* p = it.Next()) {
|
||||
if (p == page) {
|
||||
@@ -970,8 +972,8 @@ dump_page_queue(int argc, char **argv)
|
||||
queue = &sFreePageQueue;
|
||||
else if (!strcmp(argv[1], "clear"))
|
||||
queue = &sClearPageQueue;
|
||||
else if (!strcmp(argv[1], "modified"))
|
||||
queue = &sModifiedPageQueue;
|
||||
else if (!strcmp(argv[1], "modified-default"))
|
||||
queue = &sDefaultModifiedPageQueue;
|
||||
else if (!strcmp(argv[1], "active"))
|
||||
queue = &sActivePageQueue;
|
||||
else if (!strcmp(argv[1], "inactive"))
|
||||
@@ -1106,9 +1108,9 @@ dump_page_stats(int argc, char **argv)
|
||||
sFreePageQueue.Count());
|
||||
kprintf("clear queue: %p, count = %" B_PRIuPHYSADDR "\n", &sClearPageQueue,
|
||||
sClearPageQueue.Count());
|
||||
kprintf("modified queue: %p, count = %" B_PRIuPHYSADDR " (%" B_PRId32
|
||||
kprintf("modified-default queue: %p, count = %" B_PRIuPHYSADDR " (%" B_PRId32
|
||||
" temporary, %" B_PRIuPHYSADDR " swappable, " "inactive: %"
|
||||
B_PRIuPHYSADDR ")\n", &sModifiedPageQueue, sModifiedPageQueue.Count(),
|
||||
B_PRIuPHYSADDR ")\n", &sDefaultModifiedPageQueue, sDefaultModifiedPageQueue.Count(),
|
||||
sModifiedTemporaryPages, swappableModified, swappableModifiedInactive);
|
||||
kprintf("active queue: %p, count = %" B_PRIuPHYSADDR "\n",
|
||||
&sActivePageQueue, sActivePageQueue.Count());
|
||||
@@ -1461,9 +1463,9 @@ unreserve_pages(uint32 count)
|
||||
|
||||
|
||||
ModifiedPageQueue*
|
||||
vm_page_get_modified_queue()
|
||||
vm_page_default_modified_queue()
|
||||
{
|
||||
return &sModifiedPageQueue;
|
||||
return &sDefaultModifiedPageQueue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1476,7 +1478,12 @@ page_queue_for(vm_page* page, uint8 state)
|
||||
case PAGE_STATE_INACTIVE:
|
||||
return &sInactivePageQueue;
|
||||
case PAGE_STATE_MODIFIED:
|
||||
return &sModifiedPageQueue;
|
||||
{
|
||||
VMPageQueue* queue = page->Cache()->ModifiedQueue();
|
||||
if (queue != NULL)
|
||||
return queue;
|
||||
return &sDefaultModifiedPageQueue;
|
||||
}
|
||||
case PAGE_STATE_CACHED:
|
||||
return &sCachedPageQueue;
|
||||
case PAGE_STATE_FREE:
|
||||
@@ -2020,6 +2027,7 @@ full_scan_inactive_pages(page_stats& pageStats, int32 despairLevel)
|
||||
VMPageQueue& queue = sInactivePageQueue;
|
||||
InterruptsSpinLocker queueLocker(queue.GetLock());
|
||||
uint32 maxToScan = queue.Count();
|
||||
ModifiedPageQueue* modifiedQueue = NULL;
|
||||
|
||||
vm_page* nextPage = queue.Head();
|
||||
|
||||
@@ -2098,6 +2106,15 @@ full_scan_inactive_pages(page_stats& pageStats, int32 despairLevel)
|
||||
pagesToFree--;
|
||||
pagesToCached++;
|
||||
} else if (maxToFlush > 0) {
|
||||
if (pagesToModified != 0 && modifiedQueue != cache->ModifiedQueue()) {
|
||||
// This page has a different modified queue than the previous one(s).
|
||||
// Wake up the previous queue before switching.
|
||||
if (modifiedQueue == NULL)
|
||||
modifiedQueue = &sDefaultModifiedPageQueue;
|
||||
modifiedQueue->NotifyWriter();
|
||||
modifiedQueue = cache->ModifiedQueue();
|
||||
}
|
||||
|
||||
set_page_state(page, PAGE_STATE_MODIFIED);
|
||||
maxToFlush--;
|
||||
pagesToModified++;
|
||||
@@ -2123,8 +2140,11 @@ full_scan_inactive_pages(page_stats& pageStats, int32 despairLevel)
|
||||
pagesToModified, pagesToActive);
|
||||
|
||||
// wake up the page writer, if we tossed it some pages
|
||||
if (pagesToModified > 0)
|
||||
sModifiedPageQueue.NotifyWriter();
|
||||
if (pagesToModified > 0) {
|
||||
if (modifiedQueue == NULL)
|
||||
modifiedQueue = &sDefaultModifiedPageQueue;
|
||||
modifiedQueue->NotifyWriter();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2441,7 +2461,7 @@ vm_page_init(kernel_args *args)
|
||||
TRACE(("vm_page_init: entry\n"));
|
||||
|
||||
// init page queues
|
||||
sModifiedPageQueue.Init();
|
||||
sDefaultModifiedPageQueue.Init();
|
||||
sInactivePageQueue.Init();
|
||||
sActivePageQueue.Init();
|
||||
sCachedPageQueue.Init();
|
||||
@@ -2601,7 +2621,7 @@ vm_page_init_post_thread(kernel_args *args)
|
||||
resume_thread(thread);
|
||||
|
||||
// start page writer
|
||||
sModifiedPageQueue.StartWriter();
|
||||
sDefaultModifiedPageQueue.StartWriter("default");
|
||||
|
||||
// start page daemon
|
||||
|
||||
@@ -2695,6 +2715,9 @@ vm_page_allocate_page(vm_page_reservation* reservation, uint32 flags)
|
||||
uint32 pageState = flags & VM_PAGE_ALLOC_STATE;
|
||||
ASSERT(pageState != PAGE_STATE_FREE && pageState != PAGE_STATE_CLEAR);
|
||||
|
||||
ASSERT(pageState != PAGE_STATE_MODIFIED);
|
||||
// as we can't determine which modified queue it belongs in
|
||||
|
||||
ASSERT(reservation->count > 0);
|
||||
reservation->count--;
|
||||
|
||||
@@ -3146,8 +3169,8 @@ vm_page_free_etc(VMCache* cache, vm_page* page,
|
||||
PAGE_ASSERT(page, page->State() != PAGE_STATE_FREE
|
||||
&& page->State() != PAGE_STATE_CLEAR);
|
||||
|
||||
if (page->State() == PAGE_STATE_MODIFIED && (cache != NULL && cache->temporary))
|
||||
atomic_add(&sModifiedTemporaryPages, -1);
|
||||
PAGE_ASSERT(page, page->State() != PAGE_STATE_MODIFIED);
|
||||
// as we can't determine which modified queue it's in
|
||||
|
||||
free_page(page, false);
|
||||
if (reservation == NULL)
|
||||
@@ -3238,7 +3261,7 @@ vm_page_get_stats(system_info *info)
|
||||
// modified queue count is therefore split into temporary and non-temporary
|
||||
// counts that are then added to the corresponding number.
|
||||
page_num_t modifiedNonTemporaryPages
|
||||
= (sModifiedPageQueue.Count() - sModifiedTemporaryPages);
|
||||
= (ModifiedPageQueue::GlobalModifiedCount() - sModifiedTemporaryPages);
|
||||
|
||||
info->max_pages = vm_page_num_pages();
|
||||
info->cached_pages = sCachedPageQueue.Count() + modifiedNonTemporaryPages
|
||||
|
||||
@@ -46,8 +46,12 @@
|
||||
// the maximum I/O priority shall be reached when this many pages need to
|
||||
// be written
|
||||
|
||||
#define PAGES_FLUSH_DURATION_QUOTA (3 * 1000 * 1000)
|
||||
// target maximum time needed to write out all modified pages
|
||||
#define PAGES_FLUSH_DURATION_LOCAL_QUOTA (3 * 1000 * 1000)
|
||||
#define PAGES_FLUSH_DURATION_GLOBAL_QUOTA (5 * 1000 * 1000)
|
||||
// target maximum time needed to write out all modified pages, in one & all queues
|
||||
|
||||
int64 ModifiedPageQueue::sGlobalModifiedCount = 0;
|
||||
bigtime_t ModifiedPageQueue::sGlobalEstimatedWriteDuration = 0;
|
||||
|
||||
|
||||
#if PAGE_WRITER_TRACING
|
||||
@@ -550,10 +554,11 @@ ModifiedPageQueue::_PageWriter()
|
||||
|
||||
page_num_t pagesSinceLastSuccessfulWrite = 0;
|
||||
|
||||
while (true) {
|
||||
while (fWriterThread >= 0) {
|
||||
if (queue.Count() < kNumPages) {
|
||||
fPageWriterCondition.Wait(PAGES_FLUSH_DURATION_QUOTA, true);
|
||||
// wait the full amount when no one triggers us
|
||||
// wait the full amount when no one triggers us
|
||||
if (!fPageWriterCondition.Wait(PAGES_FLUSH_DURATION_LOCAL_QUOTA, true))
|
||||
continue;
|
||||
}
|
||||
|
||||
page_num_t modifiedPages = queue.Count();
|
||||
@@ -588,10 +593,6 @@ ModifiedPageQueue::_PageWriter()
|
||||
uint32 numPages = 0;
|
||||
run.PrepareNextRun();
|
||||
|
||||
// TODO: make this laptop friendly, too (ie. only start doing
|
||||
// something if someone else did something or there is really
|
||||
// enough to do).
|
||||
|
||||
// collect pages to be written
|
||||
#ifdef TRACE_VM_PAGE_WRITER
|
||||
pageCollectionTime -= system_time();
|
||||
@@ -653,14 +654,8 @@ ModifiedPageQueue::_PageWriter()
|
||||
}
|
||||
|
||||
run.AddPage(page);
|
||||
// TODO: We're possibly adding pages of different caches and
|
||||
// thus maybe of different underlying file systems here. This
|
||||
// is a potential problem for loop file systems/devices, since
|
||||
// we could mark a page busy that would need to be accessed
|
||||
// when writing back another page, thus causing a deadlock.
|
||||
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
|
||||
//dprintf("write page %p, cache %p (%ld)\n", page, page->cache, page->cache->ref_count);
|
||||
TPW(WritePage(page));
|
||||
|
||||
@@ -913,13 +908,29 @@ vm_page_schedule_write_page_range(struct VMCache *cache, uint32 firstPage,
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ModifiedPageQueue::StartWriter()
|
||||
ModifiedPageQueue::~ModifiedPageQueue()
|
||||
{
|
||||
fPageWriterCondition.Init("page writer");
|
||||
fUnderQuotaCondition.Init(this, "ModifiedPageQueue");
|
||||
thread_id writerThread = fWriterThread;
|
||||
if (writerThread < 0)
|
||||
return;
|
||||
|
||||
fWriterThread = spawn_kernel_thread(&_WriterThreadEntry, "page writer",
|
||||
fWriterThread = -1;
|
||||
fPageWriterCondition.WakeUp();
|
||||
wait_for_thread(writerThread, NULL);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ModifiedPageQueue::StartWriter(const char* name)
|
||||
{
|
||||
char threadName[B_OS_NAME_LENGTH];
|
||||
snprintf(threadName, sizeof(threadName), "page writer: %s", name);
|
||||
|
||||
fPageWriterCondition.Init(threadName);
|
||||
fUnderQuotaCondition.Init(this, "ModifiedPageQueue");
|
||||
fLastAveragePageWriteDuration = 0;
|
||||
|
||||
fWriterThread = spawn_kernel_thread(&_WriterThreadEntry, threadName,
|
||||
B_NORMAL_PRIORITY + 1, this);
|
||||
if (fWriterThread < 0)
|
||||
return fWriterThread;
|
||||
@@ -933,11 +944,24 @@ ModifiedPageQueue::IsOverQuota(page_num_t additionalPages)
|
||||
{
|
||||
InterruptsSpinLocker _(fLock);
|
||||
|
||||
if (fLastAveragePageWriteDuration == 0)
|
||||
return false;
|
||||
bigtime_t estimatedWriteDuration = (fCount * fLastAveragePageWriteDuration);
|
||||
if ((int64)fCount != fLastReportedModifiedCount
|
||||
|| estimatedWriteDuration != fLastReportedEstimatedWriteDuration) {
|
||||
atomic_add64(&sGlobalModifiedCount, fCount - fLastReportedModifiedCount);
|
||||
fLastReportedModifiedCount = fCount;
|
||||
|
||||
const page_num_t quota = PAGES_FLUSH_DURATION_QUOTA / fLastAveragePageWriteDuration;
|
||||
return (quota < (fCount + additionalPages));
|
||||
atomic_add64(&sGlobalEstimatedWriteDuration,
|
||||
estimatedWriteDuration - fLastReportedEstimatedWriteDuration);
|
||||
fLastReportedEstimatedWriteDuration = estimatedWriteDuration;
|
||||
}
|
||||
|
||||
bigtime_t additionalPagesDuration = fLastAveragePageWriteDuration * additionalPages;
|
||||
if ((estimatedWriteDuration + additionalPagesDuration)
|
||||
> PAGES_FLUSH_DURATION_LOCAL_QUOTA)
|
||||
return true;
|
||||
|
||||
return ((atomic_get64(&sGlobalEstimatedWriteDuration) + additionalPagesDuration)
|
||||
> PAGES_FLUSH_DURATION_GLOBAL_QUOTA);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user