Commit cb779c7b by 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.
1 parent c7c01ce4
......@@ -728,6 +728,105 @@ void test_file_access_synthetic(void)
ok1(page0->lru.prev == &page3->lru);
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");
/* dirty some pages again - so that test fileh_close with not all pages being non-dirty */
mkdirty2();
......
......@@ -346,6 +346,7 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags)
/* page.state -> PAGE_LOADED and correct mappings RW -> R */
if (flags & WRITEOUT_MARKSTORED) {
page->state = PAGE_LOADED;
fileh->dirty--;
list_for_each(hmmap, &fileh->mmaps) {
VMA *vma = list_entry(hmmap, typeof(*vma), same_fileh);
......@@ -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)
fileh->dirty = 0;
BUG_ON(fileh->dirty);
out:
virt_unlock();
......@@ -379,7 +382,29 @@ void fileh_dirty_discard(BigFileH *fileh)
if (page->state == PAGE_DIRTY)
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();
sigsegv_restore(&save_sigset);
}
......@@ -615,9 +640,9 @@ void vma_on_pagefault(VMA *vma, uintptr_t addr, int write)
}
// XXX also call page->markdirty() ?
if (newstate == PAGE_DIRTY && newstate != page->state)
fileh->dirty++;
page->state = max(page->state, newstate);
if (page->state == PAGE_DIRTY)
fileh->dirty = 1;
/* mark page as used recently */
// XXX = list_move_tail()
......@@ -738,6 +763,8 @@ static void page_drop_memory(Page *page)
/* 2) release memory to ram */
ramh_drop_memory(page->ramh, page->ramh_pgoffset);
if (page->state == PAGE_DIRTY)
page->fileh->dirty--;
page->state = PAGE_EMPTY;
// XXX touch lru?
......
......@@ -68,7 +68,7 @@ struct BigFileH {
// XXX not sure we need this
// -> currently is used to know whether to join ZODB DataManager serving ZBigFile
// XXX maybe change into dirty_list in the future?
unsigned dirty : 1;
unsigned dirty;
};
typedef struct BigFileH BigFileH;
......@@ -215,6 +215,20 @@ int fileh_dirty_writeout(BigFileH *fileh, enum WriteoutFlags flags);
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
*
* serves read/write access to protected memory: loads data from file on demand
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!