Commit 41d4a4f8 authored by Kirill Smelkov's avatar Kirill Smelkov

bigfile/virtmem: Do storeblk() with virtmem lock released

to avoid deadlocks.

Description is in the last patch.

Fixes: nexedi/wendelin.core#6

/cc @Tyagov, @klaus, @jm
/reviewed-on nexedi/wendelin.core!2
parents e73e22ea fb4bfb32
...@@ -319,8 +319,8 @@ PyFunc(pyfileh_isdirty, "isdirty() - are there any changes to fileh memory at al ...@@ -319,8 +319,8 @@ PyFunc(pyfileh_isdirty, "isdirty() - are there any changes to fileh memory at al
if (!PyArg_ParseTuple(args, "")) if (!PyArg_ParseTuple(args, ""))
return NULL; return NULL;
/* NOTE not strictly neccessary to virt_lock() for reading ->dirty */ /* NOTE not strictly neccessary to virt_lock() for checking ->dirty_pages not empty */
return PyBool_FromLong(pyfileh->dirty); return PyBool_FromLong(!list_empty(&pyfileh->dirty_pages));
} }
......
...@@ -51,6 +51,7 @@ Page *ramh_alloc_page(RAMH *ramh, pgoff_t pgoffset_hint) ...@@ -51,6 +51,7 @@ Page *ramh_alloc_page(RAMH *ramh, pgoff_t pgoffset_hint)
page->ramh = ramh; page->ramh = ramh;
page->ramh_pgoffset = ramh_pgoffset; page->ramh_pgoffset = ramh_pgoffset;
INIT_LIST_HEAD(&page->lru); /* NOTE ->lru left unlinked */ INIT_LIST_HEAD(&page->lru); /* NOTE ->lru left unlinked */
INIT_LIST_HEAD(&page->in_dirty); /* initially not in dirty list */
page->refcnt = 0; page->refcnt = 0;
return page; return page;
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# #
# See COPYING file for full licensing terms. # See COPYING file for full licensing terms.
from wendelin.bigfile import BigFile from wendelin.bigfile import BigFile, WRITEOUT_STORE
from threading import Thread, Lock from threading import Thread, Lock
from time import sleep from time import sleep
...@@ -68,7 +68,17 @@ PS = 2*MB ...@@ -68,7 +68,17 @@ PS = 2*MB
# V -> loadblk # V -> loadblk
# Z <- ClientStorage.invalidateTransaction() # Z <- ClientStorage.invalidateTransaction()
# Z -> zeo.load # Z -> zeo.load
# V <- fileh_invalidate_page # V <- fileh_invalidate_page (possibly of unrelated page)
#
# --------
# and similarly for storeblk:
#
# T1 T2
#
# commit same as ^^^
# V -> storeblk
#
# Z -> zeo.store
def test_thread_lock_vs_virtmem_lock(): def test_thread_lock_vs_virtmem_lock():
Z = Lock() Z = Lock()
c12 = NotifyChannel() # T1 -> T2 c12 = NotifyChannel() # T1 -> T2
...@@ -77,42 +87,55 @@ def test_thread_lock_vs_virtmem_lock(): ...@@ -77,42 +87,55 @@ def test_thread_lock_vs_virtmem_lock():
class ZLockBigFile(BigFile): class ZLockBigFile(BigFile):
def __new__(cls, blksize): def __new__(cls, blksize):
obj = BigFile.__new__(cls, blksize) obj = BigFile.__new__(cls, blksize)
obj.cycle = 0
return obj return obj
def loadblk(self, blk, buf): def Zsync_and_lockunlock(self):
tell, wait = c12.tell, c21.wait tell, wait = c12.tell, c21.wait
# on the first cycle we synchronize with invalidate in T2 # synchronize with invalidate in T2
if self.cycle == 0: tell('T1-V-under')
tell('T1-V-under') wait('T2-Z-taken')
wait('T2-Z-taken')
# this will deadlock, if V is plain lock and calling from under-virtmem # this will deadlock, if V is plain lock and calling from under-virtmem
# is done with V held # is done with V held
Z.acquire() Z.acquire()
Z.release() Z.release()
self.cycle += 1 def loadblk(self, blk, buf):
self.Zsync_and_lockunlock()
def storeblk(self, blk, buf):
self.Zsync_and_lockunlock()
f = ZLockBigFile(PS) f = ZLockBigFile(PS)
fh = f.fileh_open() fh = f.fileh_open()
fh2 = f.fileh_open()
vma = fh.mmap(0, 1) vma = fh.mmap(0, 1)
m = memoryview(vma) m = memoryview(vma)
def T1(): def T1():
m[0] # calls ZLockBigFile.loadblk() m[0] # calls ZLockBigFile.loadblk()
tell, wait = c12.tell, c21.wait
wait('T2-Z-released')
m[0] = bord_py3(b'1') # make page dirty
fh.dirty_writeout(WRITEOUT_STORE) # calls ZLockBigFile.storeblk()
def T2(): def T2():
tell, wait = c21.tell, c12.wait tell, wait = c21.tell, c12.wait
wait('T1-V-under') # cycle 0: vs loadblk in T0
Z.acquire() # cycle 1: vs storeblk in T0
tell('T2-Z-taken') for _ in range(2):
wait('T1-V-under')
Z.acquire()
tell('T2-Z-taken')
fh.invalidate_page(0) fh2.invalidate_page(0) # NOTE invalidating page _not_ of fh
Z.release() Z.release()
tell('T2-Z-released')
t1, t2 = Thread(target=T1), Thread(target=T2) t1, t2 = Thread(target=T1), Thread(target=T2)
...@@ -185,7 +208,7 @@ def test_thread_multiaccess_parallel(): ...@@ -185,7 +208,7 @@ def test_thread_multiaccess_parallel():
t1.join(); t2.join() t1.join(); t2.join()
# loading vs invalidate in another thread # loading vs invalidate of same page in another thread
def test_thread_load_vs_invalidate(): def test_thread_load_vs_invalidate():
c12 = NotifyChannel() # T1 -> T2 c12 = NotifyChannel() # T1 -> T2
c21 = NotifyChannel() # T2 -> T1 c21 = NotifyChannel() # T2 -> T1
......
...@@ -325,13 +325,13 @@ void test_file_access_synthetic(void) ...@@ -325,13 +325,13 @@ void test_file_access_synthetic(void)
ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1(!fh->dirty);
CHECK_NOPAGE( 100 ); CHECK_NOPAGE( 100 );
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_NOPAGE( 102 ); CHECK_NOPAGE( 102 );
CHECK_NOPAGE( 103 ); CHECK_NOPAGE( 103 );
ok1(list_empty(&ram->lru_list)); ok1(list_empty(&ram->lru_list));
ok1(list_empty(&fh->dirty_pages));
/* simulate read access to page[0] - it should load it */ /* simulate read access to page[0] - it should load it */
...@@ -343,7 +343,6 @@ void test_file_access_synthetic(void) ...@@ -343,7 +343,6 @@ void test_file_access_synthetic(void)
ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1(!fh->dirty);
page0 = pagemap_get(&fh->pagemap, 100); page0 = pagemap_get(&fh->pagemap, 100);
CHECK_PAGE (page0, 100, PAGE_LOADED, 1); CHECK_PAGE (page0, 100, PAGE_LOADED, 1);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
...@@ -357,6 +356,8 @@ void test_file_access_synthetic(void) ...@@ -357,6 +356,8 @@ void test_file_access_synthetic(void)
ok1(ram->lru_list.prev == &page0->lru); ok1(ram->lru_list.prev == &page0->lru);
ok1(page0->lru.prev == &ram->lru_list); ok1(page0->lru.prev == &ram->lru_list);
ok1(list_empty(&fh->dirty_pages));
/* simulate write access to page[2] - it should load it and mark page dirty */ /* simulate write access to page[2] - it should load it and mark page dirty */
diag("write page[2]"); diag("write page[2]");
...@@ -367,7 +368,6 @@ void test_file_access_synthetic(void) ...@@ -367,7 +368,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
page2 = pagemap_get(&fh->pagemap, 102); page2 = pagemap_get(&fh->pagemap, 102);
CHECK_PAGE (page0, 100, PAGE_LOADED, 1); CHECK_PAGE (page0, 100, PAGE_LOADED, 1);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
...@@ -385,6 +385,9 @@ void test_file_access_synthetic(void) ...@@ -385,6 +385,9 @@ void test_file_access_synthetic(void)
ok1(page2->lru.prev == &page0->lru); ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &ram->lru_list); ok1(page0->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
/* read access to page[3] - load */ /* read access to page[3] - load */
diag("read page[3]"); diag("read page[3]");
...@@ -395,7 +398,6 @@ void test_file_access_synthetic(void) ...@@ -395,7 +398,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1( M(vma, 3)); B(vma, 3*PSb); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1( M(vma, 3)); B(vma, 3*PSb); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
page3 = pagemap_get(&fh->pagemap, 103); page3 = pagemap_get(&fh->pagemap, 103);
CHECK_PAGE (page0, 100, PAGE_LOADED, 1); CHECK_PAGE (page0, 100, PAGE_LOADED, 1);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
...@@ -417,6 +419,9 @@ void test_file_access_synthetic(void) ...@@ -417,6 +419,9 @@ void test_file_access_synthetic(void)
ok1(page2->lru.prev == &page0->lru); ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &ram->lru_list); ok1(page0->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
/* write access to page[0] - upgrade loaded -> dirty */ /* write access to page[0] - upgrade loaded -> dirty */
diag("write page[0]"); diag("write page[0]");
...@@ -427,7 +432,6 @@ void test_file_access_synthetic(void) ...@@ -427,7 +432,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1( M(vma, 3)); B(vma, 3*PSb); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1( M(vma, 3)); B(vma, 3*PSb); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 1); CHECK_PAGE (page0, 100, PAGE_DIRTY, 1);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 1); CHECK_PAGE (page2, 102, PAGE_DIRTY, 1);
...@@ -448,6 +452,10 @@ void test_file_access_synthetic(void) ...@@ -448,6 +452,10 @@ void test_file_access_synthetic(void)
ok1(page3->lru.prev == &page2->lru); ok1(page3->lru.prev == &page2->lru);
ok1(page2->lru.prev == &ram->lru_list); ok1(page2->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
/* read page[1] /* read page[1]
* *
...@@ -462,7 +470,6 @@ void test_file_access_synthetic(void) ...@@ -462,7 +470,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
page1 = pagemap_get(&fh->pagemap, 101); page1 = pagemap_get(&fh->pagemap, 101);
page3 = pagemap_get(&fh->pagemap, 103); page3 = pagemap_get(&fh->pagemap, 103);
ok1(!page3); ok1(!page3);
...@@ -486,6 +493,10 @@ void test_file_access_synthetic(void) ...@@ -486,6 +493,10 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page2->lru); ok1(page0->lru.prev == &page2->lru);
ok1(page2->lru.prev == &ram->lru_list); ok1(page2->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
/* now explicit reclaim - should evict page[1] (the only PAGE_LOADED page) */ /* now explicit reclaim - should evict page[1] (the only PAGE_LOADED page) */
diag("reclaim"); diag("reclaim");
...@@ -496,7 +507,6 @@ void test_file_access_synthetic(void) ...@@ -496,7 +507,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
page1 = pagemap_get(&fh->pagemap, 101); page1 = pagemap_get(&fh->pagemap, 101);
ok1(!page1); ok1(!page1);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 1); CHECK_PAGE (page0, 100, PAGE_DIRTY, 1);
...@@ -516,6 +526,10 @@ void test_file_access_synthetic(void) ...@@ -516,6 +526,10 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page2->lru); ok1(page0->lru.prev == &page2->lru);
ok1(page2->lru.prev == &ram->lru_list); ok1(page2->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
/* unmap vma - dirty pages should stay in fh->pagemap and memory should /* unmap vma - dirty pages should stay in fh->pagemap and memory should
* not be forgotten */ * not be forgotten */
...@@ -524,7 +538,6 @@ void test_file_access_synthetic(void) ...@@ -524,7 +538,6 @@ void test_file_access_synthetic(void)
ok1(list_empty(&fh->mmaps)); ok1(list_empty(&fh->mmaps));
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0); CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 0); CHECK_PAGE (page2, 102, PAGE_DIRTY, 0);
...@@ -534,6 +547,10 @@ void test_file_access_synthetic(void) ...@@ -534,6 +547,10 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page2->lru); ok1(page0->lru.prev == &page2->lru);
ok1(page2->lru.prev == &ram->lru_list); ok1(page2->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
b0 = page_mmap(page0, NULL, PROT_READ); b0 = page_mmap(page0, NULL, PROT_READ);
ok1(b0); ok1(b0);
b2 = page_mmap(page2, NULL, PROT_READ); b2 = page_mmap(page2, NULL, PROT_READ);
...@@ -563,7 +580,6 @@ void test_file_access_synthetic(void) ...@@ -563,7 +580,6 @@ void test_file_access_synthetic(void)
ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0); CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 0); CHECK_PAGE (page2, 102, PAGE_DIRTY, 0);
...@@ -573,6 +589,10 @@ void test_file_access_synthetic(void) ...@@ -573,6 +589,10 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page2->lru); ok1(page0->lru.prev == &page2->lru);
ok1(page2->lru.prev == &ram->lru_list); ok1(page2->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
/* read access to page[2] - should map it R/W - the page is in PAGE_DIRTY state */ /* read access to page[2] - should map it R/W - the page is in PAGE_DIRTY state */
diag("read page[2]"); diag("read page[2]");
...@@ -583,7 +603,6 @@ void test_file_access_synthetic(void) ...@@ -583,7 +603,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0); CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 1); CHECK_PAGE (page2, 102, PAGE_DIRTY, 1);
...@@ -593,6 +612,10 @@ void test_file_access_synthetic(void) ...@@ -593,6 +612,10 @@ void test_file_access_synthetic(void)
ok1(page2->lru.prev == &page0->lru); ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &ram->lru_list); ok1(page0->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
/* discard - changes should go away */ /* discard - changes should go away */
diag("discard"); diag("discard");
...@@ -603,7 +626,6 @@ void test_file_access_synthetic(void) ...@@ -603,7 +626,6 @@ void test_file_access_synthetic(void)
ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1(!fh->dirty);
CHECK_PAGE (page0, 100, PAGE_EMPTY, 0); CHECK_PAGE (page0, 100, PAGE_EMPTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_EMPTY, 0); CHECK_PAGE (page2, 102, PAGE_EMPTY, 0);
...@@ -613,6 +635,8 @@ void test_file_access_synthetic(void) ...@@ -613,6 +635,8 @@ void test_file_access_synthetic(void)
ok1(page2->lru.prev == &page0->lru); ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &ram->lru_list); ok1(page0->lru.prev == &ram->lru_list);
ok1(list_empty(&fh->dirty_pages));
/* writeout in 3 variants - STORE, MARK, STORE+MARK */ /* writeout in 3 variants - STORE, MARK, STORE+MARK */
diag("writeout"); diag("writeout");
...@@ -640,7 +664,6 @@ void test_file_access_synthetic(void) ...@@ -640,7 +664,6 @@ void test_file_access_synthetic(void)
ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1( M(vma, 3)); B(vma, 3*PSb); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1( M(vma, 3)); B(vma, 3*PSb); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1(!fh->dirty);
CHECK_PAGE (page0, 100, PAGE_EMPTY, 0); CHECK_PAGE (page0, 100, PAGE_EMPTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_EMPTY, 0); CHECK_PAGE (page2, 102, PAGE_EMPTY, 0);
...@@ -651,11 +674,13 @@ void test_file_access_synthetic(void) ...@@ -651,11 +674,13 @@ void test_file_access_synthetic(void)
ok1(page2->lru.prev == &page0->lru); ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &ram->lru_list); ok1(page0->lru.prev == &ram->lru_list);
ok1(list_empty(&fh->dirty_pages));
/* prepare state (2 dirty pages, only 1 mapped) */ /* prepare state (2 dirty pages, only 1 mapped) */
void mkdirty2() { void mkdirty2() {
xvma_on_pagefault(vma, vma->addr_start + 0*PS, 1); /* write page[0] */
xvma_on_pagefault(vma, vma->addr_start + 2*PS, 1); /* write page[2] */ xvma_on_pagefault(vma, vma->addr_start + 2*PS, 1); /* write page[2] */
xvma_on_pagefault(vma, vma->addr_start + 0*PS, 1); /* write page[0] */
vma_unmap(vma); vma_unmap(vma);
err = fileh_mmap(vma, fh, 100, 4); err = fileh_mmap(vma, fh, 100, 4);
ok1(!err); ok1(!err);
...@@ -666,7 +691,6 @@ void test_file_access_synthetic(void) ...@@ -666,7 +691,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0); CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 1); CHECK_PAGE (page2, 102, PAGE_DIRTY, 1);
...@@ -676,6 +700,10 @@ void test_file_access_synthetic(void) ...@@ -676,6 +700,10 @@ void test_file_access_synthetic(void)
ok1(page2->lru.prev == &page0->lru); ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &page3->lru); ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list); ok1(page3->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
} }
diag("writeout (store)"); diag("writeout (store)");
...@@ -692,12 +720,15 @@ void test_file_access_synthetic(void) ...@@ -692,12 +720,15 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0); CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 1); CHECK_PAGE (page2, 102, PAGE_DIRTY, 1);
CHECK_PAGE (page3, 103, PAGE_LOADED, 0); CHECK_PAGE (page3, 103, PAGE_LOADED, 0);
/* NOTE - becomes sorted by ->f_pgoffset */
ok1(fh->dirty_pages.next == &page0->in_dirty);
ok1(page0->in_dirty.next == &page2->in_dirty);
ok1(page2->in_dirty.next == &fh->dirty_pages);
diag("writeout (mark)"); diag("writeout (mark)");
blkv_len = 0; blkv_len = 0;
...@@ -710,7 +741,6 @@ void test_file_access_synthetic(void) ...@@ -710,7 +741,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1( M(vma, 2)); B(vma, 2*PSb); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1(!fh->dirty);
CHECK_PAGE (page0, 100, PAGE_LOADED, 0); CHECK_PAGE (page0, 100, PAGE_LOADED, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_LOADED, 1); CHECK_PAGE (page2, 102, PAGE_LOADED, 1);
...@@ -721,6 +751,7 @@ void test_file_access_synthetic(void) ...@@ -721,6 +751,7 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page3->lru); ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list); ok1(page3->lru.prev == &ram->lru_list);
ok1(list_empty(&fh->dirty_pages));
diag("writeout (store+mark)"); diag("writeout (store+mark)");
mkdirty2(); mkdirty2();
...@@ -736,7 +767,6 @@ void test_file_access_synthetic(void) ...@@ -736,7 +767,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1( M(vma, 2)); B(vma, 2*PSb); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1(!fh->dirty);
CHECK_PAGE (page0, 100, PAGE_LOADED, 0); CHECK_PAGE (page0, 100, PAGE_LOADED, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_LOADED, 1); CHECK_PAGE (page2, 102, PAGE_LOADED, 1);
...@@ -747,6 +777,7 @@ void test_file_access_synthetic(void) ...@@ -747,6 +777,7 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page3->lru); ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list); ok1(page3->lru.prev == &ram->lru_list);
ok1(list_empty(&fh->dirty_pages));
diag("invalidate"); diag("invalidate");
mkdirty2(); mkdirty2();
...@@ -758,7 +789,6 @@ void test_file_access_synthetic(void) ...@@ -758,7 +789,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0); CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 1); CHECK_PAGE (page2, 102, PAGE_DIRTY, 1);
...@@ -769,6 +799,10 @@ void test_file_access_synthetic(void) ...@@ -769,6 +799,10 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page3->lru); ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list); ok1(page3->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
fileh_invalidate_page(fh, 103); fileh_invalidate_page(fh, 103);
...@@ -777,7 +811,6 @@ void test_file_access_synthetic(void) ...@@ -777,7 +811,6 @@ void test_file_access_synthetic(void)
ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12; ok1( M(vma, 2)); B(vma, 2*PSb); B(vma, 2*PSb) = 12;
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0); CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 1); CHECK_PAGE (page2, 102, PAGE_DIRTY, 1);
...@@ -788,6 +821,10 @@ void test_file_access_synthetic(void) ...@@ -788,6 +821,10 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page3->lru); ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list); ok1(page3->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &page2->in_dirty);
ok1(page2->in_dirty.prev == &fh->dirty_pages);
fileh_invalidate_page(fh, 102); fileh_invalidate_page(fh, 102);
...@@ -796,7 +833,6 @@ void test_file_access_synthetic(void) ...@@ -796,7 +833,6 @@ void test_file_access_synthetic(void)
ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0); CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_EMPTY, 0); CHECK_PAGE (page2, 102, PAGE_EMPTY, 0);
...@@ -807,6 +843,9 @@ void test_file_access_synthetic(void) ...@@ -807,6 +843,9 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page3->lru); ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list); ok1(page3->lru.prev == &ram->lru_list);
ok1(fh->dirty_pages.prev == &page0->in_dirty);
ok1(page0->in_dirty.prev == &fh->dirty_pages);
fileh_invalidate_page(fh, 100); fileh_invalidate_page(fh, 100);
...@@ -815,7 +854,6 @@ void test_file_access_synthetic(void) ...@@ -815,7 +854,6 @@ void test_file_access_synthetic(void)
ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1(!M(vma, 3)); MUST_FAULT( B(vma, 3*PSb) ); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1(!fh->dirty);
CHECK_PAGE (page0, 100, PAGE_EMPTY, 0); CHECK_PAGE (page0, 100, PAGE_EMPTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_EMPTY, 0); CHECK_PAGE (page2, 102, PAGE_EMPTY, 0);
...@@ -826,6 +864,8 @@ void test_file_access_synthetic(void) ...@@ -826,6 +864,8 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page3->lru); ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list); ok1(page3->lru.prev == &ram->lru_list);
ok1(list_empty(&fh->dirty_pages));
/* read page[3] back */ /* read page[3] back */
xvma_on_pagefault(vma, vma->addr_start + 3*PS, 0); xvma_on_pagefault(vma, vma->addr_start + 3*PS, 0);
...@@ -834,7 +874,6 @@ void test_file_access_synthetic(void) ...@@ -834,7 +874,6 @@ void test_file_access_synthetic(void)
ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 ); ok1(!M(vma, 2)); MUST_FAULT( B(vma, 2*PSb) ); MUST_FAULT( B(vma, 2*PSb) = 12 );
ok1( M(vma, 3)); B(vma, 3*PSb); MUST_FAULT( B(vma, 3*PSb) = 13 ); ok1( M(vma, 3)); B(vma, 3*PSb); MUST_FAULT( B(vma, 3*PSb) = 13 );
ok1(!fh->dirty);
CHECK_PAGE (page0, 100, PAGE_EMPTY, 0); CHECK_PAGE (page0, 100, PAGE_EMPTY, 0);
CHECK_NOPAGE( 101 ); CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_EMPTY, 0); CHECK_PAGE (page2, 102, PAGE_EMPTY, 0);
...@@ -845,6 +884,8 @@ void test_file_access_synthetic(void) ...@@ -845,6 +884,8 @@ void test_file_access_synthetic(void)
ok1(page2->lru.prev == &page0->lru); ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &ram->lru_list); ok1(page0->lru.prev == &ram->lru_list);
ok1(list_empty(&fh->dirty_pages));
diag("fileh_close"); diag("fileh_close");
/* dirty some pages again - so that test fileh_close with not all pages being non-dirty */ /* dirty some pages again - so that test fileh_close with not all pages being non-dirty */
......
...@@ -149,6 +149,8 @@ int fileh_open(BigFileH *fileh, BigFile *file, RAM *ram) ...@@ -149,6 +149,8 @@ int fileh_open(BigFileH *fileh, BigFile *file, RAM *ram)
fileh->file = file; fileh->file = file;
INIT_LIST_HEAD(&fileh->mmaps); INIT_LIST_HEAD(&fileh->mmaps);
INIT_LIST_HEAD(&fileh->dirty_pages);
fileh->writeout_inprogress = 0;
pagemap_init(&fileh->pagemap, ilog2_exact(ram->pagesize)); pagemap_init(&fileh->pagemap, ilog2_exact(ram->pagesize));
out: out:
...@@ -171,6 +173,9 @@ void fileh_close(BigFileH *fileh) ...@@ -171,6 +173,9 @@ void fileh_close(BigFileH *fileh)
// fileh, but mapping exists - real fileh release is delayed to last unmap ? // fileh, but mapping exists - real fileh release is delayed to last unmap ?
BUG_ON(!list_empty(&fileh->mmaps)); BUG_ON(!list_empty(&fileh->mmaps));
/* it's an error to close fileh while writeout is in progress */
BUG_ON(fileh->writeout_inprogress);
/* drop all pages (dirty or not) associated with this fileh */ /* drop all pages (dirty or not) associated with this fileh */
pagemap_for_each(page, &fileh->pagemap) { pagemap_for_each(page, &fileh->pagemap) {
/* it's an error to close fileh to mapping of which an access is /* it's an error to close fileh to mapping of which an access is
...@@ -182,6 +187,8 @@ void fileh_close(BigFileH *fileh) ...@@ -182,6 +187,8 @@ void fileh_close(BigFileH *fileh)
free(page); free(page);
} }
BUG_ON(!list_empty(&fileh->dirty_pages));
/* and clear pagemap */ /* and clear pagemap */
pagemap_clear(&fileh->pagemap); pagemap_clear(&fileh->pagemap);
...@@ -296,11 +303,24 @@ void vma_unmap(VMA *vma) ...@@ -296,11 +303,24 @@ void vma_unmap(VMA *vma)
* WRITEOUT / DISCARD * * WRITEOUT / DISCARD *
**********************/ **********************/
/* helper for sorting dirty pages by ->f_pgoffset */
static int hpage_indirty_cmp_bypgoffset(struct list_head *hpage1, struct list_head *hpage2, void *_)
{
Page *page1 = list_entry(hpage1, typeof(*page1), in_dirty);
Page *page2 = list_entry(hpage2, typeof(*page2), in_dirty);
if (page1->f_pgoffset < page2->f_pgoffset)
return -1;
if (page1->f_pgoffset > page2->f_pgoffset)
return +1;
return 0;
}
int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags) int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags)
{ {
Page *page; Page *page;
BigFile *file = fileh->file; BigFile *file = fileh->file;
struct list_head *hmmap; struct list_head *hpage, *hpage_next, *hmmap;
sigset_t save_sigset; sigset_t save_sigset;
int err = 0; int err = 0;
...@@ -312,12 +332,18 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags) ...@@ -312,12 +332,18 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags)
sigsegv_block(&save_sigset); sigsegv_block(&save_sigset);
virt_lock(); virt_lock();
/* concurrent writeouts are not allowed */
BUG_ON(fileh->writeout_inprogress);
fileh->writeout_inprogress = 1;
/* pages are stored (if stored) in sorted order */
if (flags & WRITEOUT_STORE)
list_sort(&fileh->dirty_pages, hpage_indirty_cmp_bypgoffset, NULL);
/* write out dirty pages */ /* write out dirty pages */
pagemap_for_each(page, &fileh->pagemap) { list_for_each_safe(hpage, hpage_next, &fileh->dirty_pages) {
/* XXX we scan whole file pages which could be slow page = list_entry(hpage, typeof(*page), in_dirty);
* TODO -> maintain something like separate dirty_list ? */ BUG_ON(page->state != PAGE_DIRTY);
if (page->state != PAGE_DIRTY)
continue;
/* ->storeblk() */ /* ->storeblk() */
if (flags & WRITEOUT_STORE) { if (flags & WRITEOUT_STORE) {
...@@ -325,35 +351,29 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags) ...@@ -325,35 +351,29 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags)
blk_t blk = page->f_pgoffset; // NOTE assumes blksize = pagesize blk_t blk = page->f_pgoffset; // NOTE assumes blksize = pagesize
void *pagebuf; void *pagebuf;
int mapped_tmp = 0;
if (!page->refcnt) {
/* page not mmaped anywhere - mmap it temporarily somewhere */
pagebuf = page_mmap(page, NULL, PROT_READ);
TODO(!pagebuf); // XXX err
mapped_tmp = 1;
}
else { /* mmap page temporarily somewhere
/* some vma mmaps page - use that memory directly */ *
* ( we cannot use present page mapping in some vma directly,
/* XXX this assumes there is small #vma and is ugly - in general it * because while storeblk is called with virtmem lock released that
* should be simpler via back-pointers from page? */ * mapping can go away ) */
pagebuf = NULL; pagebuf = page_mmap(page, NULL, PROT_READ);
list_for_each(hmmap, &fileh->mmaps) { TODO(!pagebuf); // XXX err
VMA *vma = list_entry(hmmap, typeof(*vma), same_fileh);
if (vma_page_ismapped(vma, page)) { /* unlock virtmem before calling storeblk()
pagebuf = vma_page_addr(vma, page); *
break; * that call is potentially slow and external code can take other
} * locks. If that "other locks" are also taken before external code
} * calls e.g. fileh_invalidate_page() in different codepath a deadlock
BUG_ON(!pagebuf); * can happen. (similar to loadblk case) */
} virt_unlock();
err = file->file_ops->storeblk(file, blk, pagebuf); err = file->file_ops->storeblk(file, blk, pagebuf);
if (mapped_tmp) /* relock virtmem */
xmunmap(pagebuf, page_size(page)); virt_lock();
xmunmap(pagebuf, page_size(page));
if (err) if (err)
goto out; goto out;
...@@ -362,7 +382,7 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags) ...@@ -362,7 +382,7 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags)
/* page.state -> PAGE_LOADED and correct mappings RW -> R */ /* page.state -> PAGE_LOADED and correct mappings RW -> R */
if (flags & WRITEOUT_MARKSTORED) { if (flags & WRITEOUT_MARKSTORED) {
page->state = PAGE_LOADED; page->state = PAGE_LOADED;
fileh->dirty--; list_del_init(&page->in_dirty);
list_for_each(hmmap, &fileh->mmaps) { list_for_each(hmmap, &fileh->mmaps) {
VMA *vma = list_entry(hmmap, typeof(*vma), same_fileh); VMA *vma = list_entry(hmmap, typeof(*vma), same_fileh);
...@@ -375,7 +395,9 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags) ...@@ -375,7 +395,9 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags)
/* if we successfully finished with markstored flag set - all dirty pages /* if we successfully finished with markstored flag set - all dirty pages
* should become non-dirty */ * should become non-dirty */
if (flags & WRITEOUT_MARKSTORED) if (flags & WRITEOUT_MARKSTORED)
BUG_ON(fileh->dirty); BUG_ON(!list_empty(&fileh->dirty_pages));
fileh->writeout_inprogress = 0;
out: out:
virt_unlock(); virt_unlock();
...@@ -387,18 +409,23 @@ out: ...@@ -387,18 +409,23 @@ out:
void fileh_dirty_discard(BigFileH *fileh) void fileh_dirty_discard(BigFileH *fileh)
{ {
Page *page; Page *page;
struct list_head *hpage, *hpage_next;
sigset_t save_sigset; sigset_t save_sigset;
sigsegv_block(&save_sigset); sigsegv_block(&save_sigset);
virt_lock(); virt_lock();
/* XXX we scan whole file pages which could be slow /* discard is not allowed to run in parallel to writeout */
* TODO -> maintain something like separate dirty_list ? */ BUG_ON(fileh->writeout_inprogress);
pagemap_for_each(page, &fileh->pagemap)
if (page->state == PAGE_DIRTY) list_for_each_safe(hpage, hpage_next, &fileh->dirty_pages) {
page_drop_memory(page); page = list_entry(hpage, typeof(*page), in_dirty);
BUG_ON(page->state != PAGE_DIRTY);
BUG_ON(fileh->dirty); page_drop_memory(page);
}
BUG_ON(!list_empty(&fileh->dirty_pages));
virt_unlock(); virt_unlock();
sigsegv_restore(&save_sigset); sigsegv_restore(&save_sigset);
...@@ -417,6 +444,9 @@ void fileh_invalidate_page(BigFileH *fileh, pgoff_t pgoffset) ...@@ -417,6 +444,9 @@ void fileh_invalidate_page(BigFileH *fileh, pgoff_t pgoffset)
sigsegv_block(&save_sigset); sigsegv_block(&save_sigset);
virt_lock(); virt_lock();
/* it's an error to invalidate fileh while writeout is in progress */
BUG_ON(fileh->writeout_inprogress);
page = pagemap_get(&fileh->pagemap, pgoffset); page = pagemap_get(&fileh->pagemap, pgoffset);
if (page) { if (page) {
/* for pages where loading is in progress, we just remove the page from /* for pages where loading is in progress, we just remove the page from
...@@ -639,7 +669,7 @@ VMFaultResult vma_on_pagefault(VMA *vma, uintptr_t addr, int write) ...@@ -639,7 +669,7 @@ VMFaultResult vma_on_pagefault(VMA *vma, uintptr_t addr, int write)
* that call is potentially slow and external code can take other * that call is potentially slow and external code can take other
* locks. If that "other locks" are also taken before external code * locks. If that "other locks" are also taken before external code
* calls e.g. fileh_invalidate_page() in different codepath a deadlock * calls e.g. fileh_invalidate_page() in different codepath a deadlock
* can happen. */ * can happen. (similar to storeblk case) */
page->state = PAGE_LOADING; page->state = PAGE_LOADING;
virt_unlock(); virt_unlock();
...@@ -721,8 +751,12 @@ VMFaultResult vma_on_pagefault(VMA *vma, uintptr_t addr, int write) ...@@ -721,8 +751,12 @@ VMFaultResult vma_on_pagefault(VMA *vma, uintptr_t addr, int write)
} }
// XXX also call page->markdirty() ? // XXX also call page->markdirty() ?
if (newstate == PAGE_DIRTY && newstate != page->state) if (newstate == PAGE_DIRTY && newstate != page->state) {
fileh->dirty++; /* it is not allowed to modify pages while writeout is in progress */
BUG_ON(fileh->writeout_inprogress);
list_add_tail(&page->in_dirty, &fileh->dirty_pages);
}
page->state = max(page->state, newstate); page->state = max(page->state, newstate);
/* mark page as used recently */ /* mark page as used recently */
...@@ -838,6 +872,8 @@ static void page_drop_memory(Page *page) ...@@ -838,6 +872,8 @@ static void page_drop_memory(Page *page)
/* NOTE we try not to drop memory for loading-in-progress pages. /* NOTE we try not to drop memory for loading-in-progress pages.
* so if this is called for such a page - it is a bug. */ * so if this is called for such a page - it is a bug. */
BUG_ON(page->state == PAGE_LOADING); BUG_ON(page->state == PAGE_LOADING);
/* same for storing-in-progress */
BUG_ON(page->fileh->writeout_inprogress && page->state == PAGE_DIRTY);
if (page->state == PAGE_EMPTY) if (page->state == PAGE_EMPTY)
return; return;
...@@ -850,7 +886,7 @@ static void page_drop_memory(Page *page) ...@@ -850,7 +886,7 @@ static void page_drop_memory(Page *page)
/* 2) release memory to ram */ /* 2) release memory to ram */
ramh_drop_memory(page->ramh, page->ramh_pgoffset); ramh_drop_memory(page->ramh, page->ramh_pgoffset);
if (page->state == PAGE_DIRTY) if (page->state == PAGE_DIRTY)
page->fileh->dirty--; list_del_init(&page->in_dirty);
page->state = PAGE_EMPTY; page->state = PAGE_EMPTY;
// XXX touch lru? // XXX touch lru?
......
...@@ -65,10 +65,11 @@ struct BigFileH { ...@@ -65,10 +65,11 @@ struct BigFileH {
PageMap pagemap; PageMap pagemap;
// XXX not sure we need this /* fileh dirty pages */
// -> currently is used to know whether to join ZODB DataManager serving ZBigFile struct list_head dirty_pages; /* _ -> page->in_dirty */
// XXX maybe change into dirty_list in the future?
unsigned dirty; /* whether writeout is currently in progress */
int writeout_inprogress;
}; };
typedef struct BigFileH BigFileH; typedef struct BigFileH BigFileH;
...@@ -99,6 +100,9 @@ struct Page { ...@@ -99,6 +100,9 @@ struct Page {
/* in recently-used pages for ramh->ram (ram->lru_list -> _) */ /* in recently-used pages for ramh->ram (ram->lru_list -> _) */
struct list_head lru; struct list_head lru;
/* in dirty pages for fileh (fileh->dirty_pages -> _) */
struct list_head in_dirty;
int refcnt; /* each mapping in a vma counts here */ int refcnt; /* each mapping in a vma counts here */
}; };
typedef struct Page Page; typedef struct Page Page;
...@@ -152,6 +156,7 @@ int fileh_open(BigFileH *fileh, BigFile *file, RAM *ram); ...@@ -152,6 +156,7 @@ int fileh_open(BigFileH *fileh, BigFile *file, RAM *ram);
/* close fileh /* close fileh
* *
* it's an error to call fileh_close with existing mappings * it's an error to call fileh_close with existing mappings
* it's an error to call fileh_close while writeout for fileh is in progress
*/ */
void fileh_close(BigFileH *fileh); void fileh_close(BigFileH *fileh);
...@@ -204,6 +209,12 @@ enum WriteoutFlags { ...@@ -204,6 +209,12 @@ enum WriteoutFlags {
* *
* No guarantee is made about atomicity - e.g. if this call fails, some * No guarantee is made about atomicity - e.g. if this call fails, some
* pages could be written and some left in memory in dirty state. * pages could be written and some left in memory in dirty state.
*
* it's an error for a given fileh to call several fileh_dirty_writeout() in
* parallel.
*
* it's an error for a given fileh to modify its pages while writeout is in
* progress: until fileh_dirty_writeout(... | WRITEOUT_STORE) has finished.
*/ */
int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags); int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags);
...@@ -215,6 +226,9 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags); ...@@ -215,6 +226,9 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags);
* - it is unmapped from all mmaps; * - it is unmapped from all mmaps;
* - its content is discarded; * - its content is discarded;
* - its backing memory is released to OS. * - its backing memory is released to OS.
*
* it's an error for a given fileh to call fileh_dirty_discard() while writeout
* is in progress.
*/ */
void fileh_dirty_discard(BigFileH *fileh); void fileh_dirty_discard(BigFileH *fileh);
...@@ -229,6 +243,9 @@ void fileh_dirty_discard(BigFileH *fileh); ...@@ -229,6 +243,9 @@ void fileh_dirty_discard(BigFileH *fileh);
* *
* ( Such invalidation is needed to synchronize fileh memory, when we know a * ( Such invalidation is needed to synchronize fileh memory, when we know a
* file was changed externally ) * file was changed externally )
*
* it's an error to call fileh_invalidate_page() while writeout for fileh is in
* progress.
*/ */
void fileh_invalidate_page(BigFileH *fileh, pgoff_t pgoffset); void fileh_invalidate_page(BigFileH *fileh, pgoff_t pgoffset);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment