From: David Howells The attached patch provides a filesystem-specific page bit that a filesystem can synchronise upon. This can be used, for example, by a netfs to synchronise with CacheFS writing its pages to disk. The PG_checked bit is replaced with PG_fs_misc, and various operations are provided based upon that. The *PageChecked() macros still exist, though now they just convert to *PageFsMisc() macros. The name of the "checked" macros seems appropriate as they're used for metadata page validation by various filesystems. Signed-off-by: David Howells Cc: Trond Myklebust Signed-off-by: Andrew Morton --- fs/afs/dir.c | 5 +---- fs/ext2/dir.c | 6 +++--- fs/ext3/inode.c | 10 +++++----- fs/freevxfs/vxfs_subr.c | 2 +- fs/reiserfs/inode.c | 10 +++++----- fs/ufs/dir.c | 6 +++--- include/linux/page-flags.h | 15 ++++++++++----- include/linux/pagemap.h | 11 +++++++++++ mm/filemap.c | 17 +++++++++++++++++ mm/migrate.c | 4 ++-- mm/page_alloc.c | 2 +- 11 files changed, 59 insertions(+), 29 deletions(-) diff -puN fs/afs/dir.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit fs/afs/dir.c --- a/fs/afs/dir.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/fs/afs/dir.c @@ -155,11 +155,9 @@ static inline void afs_dir_check_page(st } } - SetPageChecked(page); return; error: - SetPageChecked(page); SetPageError(page); } /* end afs_dir_check_page() */ @@ -191,8 +189,7 @@ static struct page *afs_dir_get_page(str kmap(page); if (!PageUptodate(page)) goto fail; - if (!PageChecked(page)) - afs_dir_check_page(dir, page); + afs_dir_check_page(dir, page); if (PageError(page)) goto fail; } diff -puN fs/ext2/dir.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit fs/ext2/dir.c --- a/fs/ext2/dir.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/fs/ext2/dir.c @@ -112,7 +112,7 @@ static void ext2_check_page(struct page if (offs != limit) goto Eend; out: - SetPageChecked(page); + SetPageFsMisc(page); return; /* Too bad, we had an error */ @@ -152,7 +152,7 @@ Eend: dir->i_ino, (page->index<inode)); fail: - SetPageChecked(page); + SetPageFsMisc(page); SetPageError(page); } @@ -165,7 +165,7 @@ static struct page * ext2_get_page(struc kmap(page); if (!PageUptodate(page)) goto fail; - if (!PageChecked(page)) + if (!PageFsMisc(page)) ext2_check_page(page); if (PageError(page)) goto fail; diff -puN fs/ext3/inode.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit fs/ext3/inode.c --- a/fs/ext3/inode.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/fs/ext3/inode.c @@ -1536,12 +1536,12 @@ static int ext3_journalled_writepage(str goto no_write; } - if (!page_has_buffers(page) || PageChecked(page)) { + if (!page_has_buffers(page) || PageFsMisc(page)) { /* * It's mmapped pagecache. Add buffers and journal it. There * doesn't seem much point in redirtying the page here. */ - ClearPageChecked(page); + ClearPageFsMisc(page); ret = block_prepare_write(page, 0, PAGE_CACHE_SIZE, ext3_get_block); if (ret != 0) { @@ -1598,7 +1598,7 @@ static void ext3_invalidatepage(struct p * If it's a full truncate we just forget about the pending dirtying */ if (offset == 0) - ClearPageChecked(page); + ClearPageFsMisc(page); journal_invalidatepage(journal, page, offset); } @@ -1607,7 +1607,7 @@ static int ext3_releasepage(struct page { journal_t *journal = EXT3_JOURNAL(page->mapping->host); - WARN_ON(PageChecked(page)); + WARN_ON(PageFsMisc(page)); if (!page_has_buffers(page)) return 0; return journal_try_to_free_buffers(journal, page, wait); @@ -1703,7 +1703,7 @@ out: */ static int ext3_journalled_set_page_dirty(struct page *page) { - SetPageChecked(page); + SetPageFsMisc(page); return __set_page_dirty_nobuffers(page); } diff -puN fs/freevxfs/vxfs_subr.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit fs/freevxfs/vxfs_subr.c --- a/fs/freevxfs/vxfs_subr.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/fs/freevxfs/vxfs_subr.c @@ -78,7 +78,7 @@ vxfs_get_page(struct address_space *mapp kmap(pp); if (!PageUptodate(pp)) goto fail; - /** if (!PageChecked(pp)) **/ + /** if (!PageFsMisc(pp)) **/ /** vxfs_check_page(pp); **/ if (PageError(pp)) goto fail; diff -puN fs/reiserfs/inode.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit fs/reiserfs/inode.c --- a/fs/reiserfs/inode.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/fs/reiserfs/inode.c @@ -2343,7 +2343,7 @@ static int reiserfs_write_full_page(stru struct buffer_head *head, *bh; int partial = 0; int nr = 0; - int checked = PageChecked(page); + int checked = PageFsMisc(page); struct reiserfs_transaction_handle th; struct super_block *s = inode->i_sb; int bh_per_page = PAGE_CACHE_SIZE / s->s_blocksize; @@ -2421,7 +2421,7 @@ static int reiserfs_write_full_page(stru * blocks we're going to log */ if (checked) { - ClearPageChecked(page); + ClearPageFsMisc(page); reiserfs_write_lock(s); error = journal_begin(&th, s, bh_per_page + 1); if (error) { @@ -2802,7 +2802,7 @@ static void reiserfs_invalidatepage(stru BUG_ON(!PageLocked(page)); if (offset == 0) - ClearPageChecked(page); + ClearPageFsMisc(page); if (!page_has_buffers(page)) goto out; @@ -2843,7 +2843,7 @@ static int reiserfs_set_page_dirty(struc { struct inode *inode = page->mapping->host; if (reiserfs_file_data_log(inode)) { - SetPageChecked(page); + SetPageFsMisc(page); return __set_page_dirty_nobuffers(page); } return __set_page_dirty_buffers(page); @@ -2866,7 +2866,7 @@ static int reiserfs_releasepage(struct p struct buffer_head *bh; int ret = 1; - WARN_ON(PageChecked(page)); + WARN_ON(PageFsMisc(page)); spin_lock(&j->j_dirty_buffers_lock); head = page_buffers(page); bh = head; diff -puN fs/ufs/dir.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit fs/ufs/dir.c --- a/fs/ufs/dir.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/fs/ufs/dir.c @@ -135,7 +135,7 @@ static void ufs_check_page(struct page * if (offs != limit) goto Eend; out: - SetPageChecked(page); + SetPageFsMisc(page); return; /* Too bad, we had an error */ @@ -173,7 +173,7 @@ Eend: "offset=%lu", dir->i_ino, (page->index<. */ +#define PG_fs_misc 8 #define PG_arch_1 9 #define PG_reserved 10 #define PG_private 11 /* If pagecache, has fs-private data */ @@ -165,10 +165,6 @@ static inline void SetPageUptodate(struc #define PageHighMem(page) 0 /* needed to optimize away at compile time */ #endif -#define PageChecked(page) test_bit(PG_checked, &(page)->flags) -#define SetPageChecked(page) set_bit(PG_checked, &(page)->flags) -#define ClearPageChecked(page) clear_bit(PG_checked, &(page)->flags) - #define PageReserved(page) test_bit(PG_reserved, &(page)->flags) #define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags) #define ClearPageReserved(page) clear_bit(PG_reserved, &(page)->flags) @@ -267,4 +263,13 @@ static inline void set_page_writeback(st test_set_page_writeback(page); } +/* + * Filesystem-specific page bit testing + */ +#define PageFsMisc(page) test_bit(PG_fs_misc, &(page)->flags) +#define SetPageFsMisc(page) set_bit(PG_fs_misc, &(page)->flags) +#define TestSetPageFsMisc(page) test_and_set_bit(PG_fs_misc, &(page)->flags) +#define ClearPageFsMisc(page) clear_bit(PG_fs_misc, &(page)->flags) +#define TestClearPageFsMisc(page) test_and_clear_bit(PG_fs_misc, &(page)->flags) + #endif /* PAGE_FLAGS_H */ diff -puN include/linux/pagemap.h~fs-cache-provide-a-filesystem-specific-syncable-page-bit include/linux/pagemap.h --- a/include/linux/pagemap.h~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/include/linux/pagemap.h @@ -189,6 +189,17 @@ static inline void wait_on_page_writebac extern void end_page_writeback(struct page *page); /* + * Wait for filesystem-specific page synchronisation to complete + */ +static inline void wait_on_page_fs_misc(struct page *page) +{ + if (PageFsMisc(page)) + wait_on_page_bit(page, PG_fs_misc); +} + +extern void fastcall end_page_fs_misc(struct page *page); + +/* * Fault a userspace page into pagetables. Return non-zero on a fault. * * This assumes that two userspace pages are always sufficient. That's diff -puN mm/filemap.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit mm/filemap.c --- a/mm/filemap.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/mm/filemap.c @@ -555,6 +555,23 @@ void end_page_writeback(struct page *pag } EXPORT_SYMBOL(end_page_writeback); +/* + * Note completion of filesystem specific page synchronisation + * + * This is used to allow a page to be written to a filesystem cache in the + * background without holding up the completion of readpage + */ +void fastcall end_page_fs_misc(struct page *page) +{ + smp_mb__before_clear_bit(); + if (!TestClearPageFsMisc(page)) + BUG(); + smp_mb__after_clear_bit(); + __wake_up_bit(page_waitqueue(page), &page->flags, PG_fs_misc); +} + +EXPORT_SYMBOL(end_page_fs_misc); + /** * __lock_page - get a lock on the page, assuming we need to sleep to get it * @page: the page to lock diff -puN mm/migrate.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit mm/migrate.c --- a/mm/migrate.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/mm/migrate.c @@ -353,8 +353,8 @@ static void migrate_page_copy(struct pag SetPageUptodate(newpage); if (PageActive(page)) SetPageActive(newpage); - if (PageChecked(page)) - SetPageChecked(newpage); + if (PageFsMisc(page)) + SetPageFsMisc(newpage); if (PageMappedToDisk(page)) SetPageMappedToDisk(newpage); diff -puN mm/page_alloc.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit mm/page_alloc.c --- a/mm/page_alloc.c~fs-cache-provide-a-filesystem-specific-syncable-page-bit +++ a/mm/page_alloc.c @@ -599,7 +599,7 @@ static int prep_new_page(struct page *pa page->flags &= ~(1 << PG_uptodate | 1 << PG_error | 1 << PG_referenced | 1 << PG_arch_1 | - 1 << PG_checked | 1 << PG_mappedtodisk); + 1 << PG_fs_misc | 1 << PG_mappedtodisk); set_page_private(page, 0); set_page_refcounted(page); _