From 328f263a571c5561f933b89152984a263145f7e8 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 25 Jul 2007 20:39:08 -0700 Subject: [PATCH] Large page order operations, zeroing and flushing We may have to zero and flush higher order pages. Implement clear_mapping_page and flush_mapping_page to do that job. Replace the flushing and clearing at some key locations for the pagecache. In some places it is necesssary to determine the page order in use from the page struct since no mapping is available. Add a series of page_cache_page_xx functions: page_cache_head(page) -> Determine head page from a tail page page_cache_base_pages(page) -> Number of base pages of a page page_cache_page_order(page) -> Determine page order of a page page_cache_page_size(page) -> Determine page size of a page page_cache_page_shift(page) -> Determine page shif of a page Signed-off-by: Christoph Lameter --- fs/libfs.c | 4 +-- fs/reiserfs/file.c | 2 - include/linux/pagemap.h | 58 +++++++++++++++++++++++++++++++++++++++++++++--- mm/filemap.c | 4 +-- mm/filemap_xip.c | 4 +-- 5 files changed, 62 insertions(+), 10 deletions(-) Index: linux-2.6/include/linux/pagemap.h =================================================================== --- linux-2.6.orig/include/linux/pagemap.h 2007-09-10 22:20:33.000000000 -0700 +++ linux-2.6/include/linux/pagemap.h 2007-09-10 22:37:25.000000000 -0700 @@ -91,6 +91,31 @@ static inline unsigned int page_cache_of return pos & ~PAGE_MASK; } +static inline struct page *page_cache_head(struct page *page) +{ + return page; +} + +static inline int page_cache_base_pages(struct page *page) +{ + return 1; +} + +static inline unsigned long page_cache_page_order(struct page *page) +{ + return 0; +} + +static inline unsigned long page_cache_page_size(struct page *page) +{ + return PAGE_SIZE; +} + +static inline unsigned long page_cache_page_shift(struct page *page) +{ + return PAGE_SHIFT; +} + static inline pgoff_t page_cache_index(struct address_space *a, loff_t pos) { @@ -263,6 +288,33 @@ static inline void wait_on_page_writebac extern void end_page_writeback(struct page *page); /* + * Clear a higher order page + */ +static inline void clear_mapping_page(struct page *page) +{ + int nr_pages = page_cache_base_pages(page); + int i; + + for (i = 0; i < nr_pages; i++) + clear_highpage(page + i); +} + +/* + * Primitive support for flushing higher order pages. + * + * A bit stupid: On many platforms flushing the first page + * will flush any TLB starting there + */ +static inline void flush_mapping_page(struct page *page) +{ + int nr_pages = page_cache_base_pages(page); + int i; + + for (i = 0; i < nr_pages; i++) + flush_dcache_page(page + i); +} + +/* * Zeroing of segments of a page */ static inline void zero_user_segments(struct page *page, @@ -271,8 +323,8 @@ static inline void zero_user_segments(st { void *kaddr = kmap_atomic(page, KM_USER0); - BUG_ON(end1 > PAGE_SIZE || - end2 > PAGE_SIZE); + BUG_ON(end1 > page_cache_page_size(page) || + end2 > page_cache_page_size(page)); if (end1 > start1) memset(kaddr + start1, 0, end1 - start1); @@ -281,7 +333,7 @@ static inline void zero_user_segments(st memset(kaddr + start2, 0, end2 - start2); kunmap_atomic(kaddr, KM_USER0); - flush_dcache_page(page); + flush_mapping_page(page); } static inline void zero_user_segment(struct page *page, Index: linux-2.6/fs/libfs.c =================================================================== --- linux-2.6.orig/fs/libfs.c 2007-09-10 22:20:33.000000000 -0700 +++ linux-2.6/fs/libfs.c 2007-09-10 22:37:05.000000000 -0700 @@ -330,8 +330,8 @@ int simple_rename(struct inode *old_dir, int simple_readpage(struct file *file, struct page *page) { - clear_highpage(page); - flush_dcache_page(page); + clear_mapping_page(page); + flush_mapping_page(page); SetPageUptodate(page); unlock_page(page); return 0; Index: linux-2.6/mm/filemap.c =================================================================== --- linux-2.6.orig/mm/filemap.c 2007-09-10 22:20:33.000000000 -0700 +++ linux-2.6/mm/filemap.c 2007-09-10 22:37:17.000000000 -0700 @@ -941,7 +941,7 @@ page_ok: * before reading the page on the kernel side. */ if (mapping_writably_mapped(mapping)) - flush_dcache_page(page); + flush_mapping_page(page); /* * When a sequential read accesses a page several times, @@ -1932,7 +1932,7 @@ generic_file_buffered_write(struct kiocb else copied = filemap_copy_from_user_iovec(page, offset, cur_iov, iov_base, bytes); - flush_dcache_page(page); + flush_mapping_page(page); status = a_ops->commit_write(file, page, offset, offset+bytes); if (status == AOP_TRUNCATED_PAGE) { page_cache_release(page); Index: linux-2.6/mm/filemap_xip.c =================================================================== --- linux-2.6.orig/mm/filemap_xip.c 2007-09-10 22:20:33.000000000 -0700 +++ linux-2.6/mm/filemap_xip.c 2007-09-10 22:23:32.000000000 -0700 @@ -104,7 +104,7 @@ do_xip_mapping_read(struct address_space * before reading the page on the kernel side. */ if (mapping_writably_mapped(mapping)) - flush_dcache_page(page); + flush_mapping_page(page); /* * Ok, we have the page, so now we can copy it to user space... @@ -320,7 +320,7 @@ __xip_file_write(struct file *filp, cons } copied = filemap_copy_from_user(page, offset, buf, bytes); - flush_dcache_page(page); + flush_mapping_page(page); if (likely(copied > 0)) { status = copied; Index: linux-2.6/fs/reiserfs/file.c =================================================================== --- linux-2.6.orig/fs/reiserfs/file.c 2007-09-10 22:20:33.000000000 -0700 +++ linux-2.6/fs/reiserfs/file.c 2007-09-10 22:23:32.000000000 -0700 @@ -748,7 +748,7 @@ static int reiserfs_copy_from_user_to_fi page_fault = __copy_from_user(page_address(page) + offset, buf, count); // Copy the data. /* Flush processor's dcache for this page */ - flush_dcache_page(page); + flush_mapping_page(page); kunmap(page); buf += count; write_bytes -= count;