Commit cb779c7b authored by Kirill Smelkov's avatar Kirill Smelkov

bigfile/virtmem: Client API to invalidate a fileh page

FileH is a handle representing snapshot of a file. If, for a pgoffset,
fileh already has loaded page, but we know the content of the file has
changed externally after loading has been done, we need to propagate to
fileh that such-and-such page should be invalidated (and reloaded on
next access).

This patch introduces

    fileh_invalidate_page(fileh, pgoffset)

to do just that.

In the next patch we'll use this facility to propagate invalidations of
ZBlk ZODB objects to virtmem subsystem.

NOTE

Since invalidation removes "dirtiness" from a page state, several
subsequent invalidations can make a fileh completely non-dirty
(invalidating all dirty page). Previously fileh->dirty was just a one
bit, so we needed to improve how we track dirtiness.

One way would be to have a dirty list for fileh pages and operate on
that. This has advantage to even optimize dirty pages processing like
fileh_dirty_writeout() where we currently scan through all fileh pages
just to write only PAGE_DIRTY ones.

Another simpler way is to make fileh->dirty a counter and maintain that.

Since we are going to move virtmem subsystem back into the kernel, here,
a simpler less-intrusive approach is used.
parent c7c01ce4
...@@ -728,6 +728,105 @@ void test_file_access_synthetic(void) ...@@ -728,6 +728,105 @@ 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);
diag("invalidate");
mkdirty2();
fileh_invalidate_page(fh, 101);
ok1(!M(vma, 0)); MUST_FAULT( B(vma, 0*PSb) ); MUST_FAULT( B(vma, 0*PSb) = 10 );
ok1(!M(vma, 1)); MUST_FAULT( B(vma, 1*PSb) ); MUST_FAULT( B(vma, 1*PSb) = 11 );
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( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 1);
CHECK_PAGE (page3, 103, PAGE_LOADED, 0);
ok1(ram->lru_list.prev == &page2->lru);
ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list);
fileh_invalidate_page(fh, 103);
ok1(!M(vma, 0)); MUST_FAULT( B(vma, 0*PSb) ); MUST_FAULT( B(vma, 0*PSb) = 10 );
ok1(!M(vma, 1)); MUST_FAULT( B(vma, 1*PSb) ); MUST_FAULT( B(vma, 1*PSb) = 11 );
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( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_DIRTY, 1);
CHECK_PAGE (page3, 103, PAGE_EMPTY, 0);
ok1(ram->lru_list.prev == &page2->lru);
ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list);
fileh_invalidate_page(fh, 102);
ok1(!M(vma, 0)); MUST_FAULT( B(vma, 0*PSb) ); MUST_FAULT( B(vma, 0*PSb) = 10 );
ok1(!M(vma, 1)); MUST_FAULT( B(vma, 1*PSb) ); MUST_FAULT( B(vma, 1*PSb) = 11 );
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( fh->dirty);
CHECK_PAGE (page0, 100, PAGE_DIRTY, 0);
CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_EMPTY, 0);
CHECK_PAGE (page3, 103, PAGE_EMPTY, 0);
ok1(ram->lru_list.prev == &page2->lru);
ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list);
fileh_invalidate_page(fh, 100);
ok1(!M(vma, 0)); MUST_FAULT( B(vma, 0*PSb) ); MUST_FAULT( B(vma, 0*PSb) = 10 );
ok1(!M(vma, 1)); MUST_FAULT( B(vma, 1*PSb) ); MUST_FAULT( B(vma, 1*PSb) = 11 );
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(!fh->dirty);
CHECK_PAGE (page0, 100, PAGE_EMPTY, 0);
CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_EMPTY, 0);
CHECK_PAGE (page3, 103, PAGE_EMPTY, 0);
ok1(ram->lru_list.prev == &page2->lru);
ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &page3->lru);
ok1(page3->lru.prev == &ram->lru_list);
/* read page[3] back */
vma_on_pagefault(vma, vma->addr_start + 3*PS, 0);
ok1(!M(vma, 0)); MUST_FAULT( B(vma, 0*PSb) ); MUST_FAULT( B(vma, 0*PSb) = 10 );
ok1(!M(vma, 1)); MUST_FAULT( B(vma, 1*PSb) ); MUST_FAULT( B(vma, 1*PSb) = 11 );
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(!fh->dirty);
CHECK_PAGE (page0, 100, PAGE_EMPTY, 0);
CHECK_NOPAGE( 101 );
CHECK_PAGE (page2, 102, PAGE_EMPTY, 0);
CHECK_PAGE (page3, 103, PAGE_LOADED, 1);
ok1(ram->lru_list.prev == &page3->lru);
ok1(page3->lru.prev == &page2->lru);
ok1(page2->lru.prev == &page0->lru);
ok1(page0->lru.prev == &ram->lru_list);
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 */
mkdirty2(); mkdirty2();
......
...@@ -346,6 +346,7 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags) ...@@ -346,6 +346,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_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);
...@@ -355,8 +356,10 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags) ...@@ -355,8 +356,10 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags)
} }
/* if we successfully finished with markstored flag set - all dirty pages
* should become non-dirty */
if (flags & WRITEOUT_MARKSTORED) if (flags & WRITEOUT_MARKSTORED)
fileh->dirty = 0; BUG_ON(fileh->dirty);
out: out:
virt_unlock(); virt_unlock();
...@@ -379,7 +382,29 @@ void fileh_dirty_discard(BigFileH *fileh) ...@@ -379,7 +382,29 @@ void fileh_dirty_discard(BigFileH *fileh)
if (page->state == PAGE_DIRTY) if (page->state == PAGE_DIRTY)
page_drop_memory(page); page_drop_memory(page);
fileh->dirty = 0; BUG_ON(fileh->dirty);
virt_unlock();
sigsegv_restore(&save_sigset);
}
/****************
* INVALIDATION *
****************/
void fileh_invalidate_page(BigFileH *fileh, pgoff_t pgoffset)
{
Page *page;
sigset_t save_sigset;
sigsegv_block(&save_sigset);
virt_lock();
page = pagemap_get(&fileh->pagemap, pgoffset);
if (page)
page_drop_memory(page);
virt_unlock(); virt_unlock();
sigsegv_restore(&save_sigset); sigsegv_restore(&save_sigset);
} }
...@@ -615,9 +640,9 @@ void vma_on_pagefault(VMA *vma, uintptr_t addr, int write) ...@@ -615,9 +640,9 @@ void 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)
fileh->dirty++;
page->state = max(page->state, newstate); page->state = max(page->state, newstate);
if (page->state == PAGE_DIRTY)
fileh->dirty = 1;
/* mark page as used recently */ /* mark page as used recently */
// XXX = list_move_tail() // XXX = list_move_tail()
...@@ -738,6 +763,8 @@ static void page_drop_memory(Page *page) ...@@ -738,6 +763,8 @@ 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)
page->fileh->dirty--;
page->state = PAGE_EMPTY; page->state = PAGE_EMPTY;
// XXX touch lru? // XXX touch lru?
......
...@@ -68,7 +68,7 @@ struct BigFileH { ...@@ -68,7 +68,7 @@ struct BigFileH {
// XXX not sure we need this // XXX not sure we need this
// -> currently is used to know whether to join ZODB DataManager serving ZBigFile // -> currently is used to know whether to join ZODB DataManager serving ZBigFile
// XXX maybe change into dirty_list in the future? // XXX maybe change into dirty_list in the future?
unsigned dirty : 1; unsigned dirty;
}; };
typedef struct BigFileH BigFileH; typedef struct BigFileH BigFileH;
...@@ -215,6 +215,20 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags); ...@@ -215,6 +215,20 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags);
void fileh_dirty_discard(BigFileH *fileh); void fileh_dirty_discard(BigFileH *fileh);
/* invalidate fileh page
*
* Make sure that page corresponding to pgoffset is not present in fileh memory.
*
* The page could be in either dirty or loaded or empty state. In all
* cases page transitions to empty state and its memory is forgotten.
*
* ( Such invalidation is needed to synchronize fileh memory, when we know a
* file was changed externally )
*/
void fileh_invalidate_page(BigFileH *fileh, pgoff_t pgoffset);
/* pagefault handler /* pagefault handler
* *
* serves read/write access to protected memory: loads data from file on demand * serves read/write access to protected memory: loads data from file on demand
......
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