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:
Augustin Cavalier
2026-07-18 18:51:43 +00:00
committed by waddlesplash
parent de0a0420a8
commit 3bfaa91290
13 changed files with 216 additions and 74 deletions
@@ -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;
+3 -1
View File
@@ -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);
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
{
+5 -1
View File
@@ -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();
+16 -3
View File
@@ -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
+19 -2
View File
@@ -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;
+3 -2
View File
@@ -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);
}
+55 -32
View File
@@ -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
+48 -24
View File
@@ -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);
}