Large Blocksize Support V7 (adds memmap support)
Subsystems (23)
PATCH 30/41 Add VM_BUG_ONs to check for correct page order
In reply to Christoph LameterBefore allowing different page orders it may be wise to get some checkpoints in at various places. Checkpoints will help debugging whenever a wrong order page shows up in a mapping. Helps when converting new filesystems to utilize larger pages.
--- fs/buffer.c | 1 + mm/filemap.c | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-)
fs/buffer.c +1 -0
@@ -893,6 +893,7 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,long offset;unsigned int page_size =page_cache_size (page->mapping); +BUG_ON (size > page_size); try_again: head =NULL ; offset = page_size;
mm/filemap.c +15 -3
@@ -128,6 +128,7 @@ void remove_from_page_cache(struct page *page)struct address_space *mapping = page->mapping;BUG_ON (!PageLocked (page)); +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (page));write_lock_irq (&mapping->tree_lock);__remove_from_page_cache (page);
@@ -269,6 +270,7 @@ int wait_on_page_writeback_range(struct address_space *mapping,if (page->index > end)continue ; +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (page));wait_on_page_writeback (page);if (PageError (page)) ret = -EIO ;
@@ -440,6 +442,7 @@ int add_to_page_cache(struct page *page, struct address_space *mapping, {int error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM); +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (page));if (error ==0 ) {write_lock_irq (&mapping->tree_lock); error =radix_tree_insert (&mapping->page_tree, offset, page);
@@ -599,8 +602,10 @@ struct page * find_get_page(struct address_space *mapping, unsigned long offset)read_lock_irq (&mapping->tree_lock); page =radix_tree_lookup (&mapping->page_tree, offset); -if (page) +if (page) { +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (page));page_cache_get (page); + }read_unlock_irq (&mapping->tree_lock);return page; }
@@ -625,6 +630,7 @@ struct page *find_lock_page(struct address_space *mapping, repeat: page =radix_tree_lookup (&mapping->page_tree, offset);if (page) { +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (page));page_cache_get (page);if (TestSetPageLocked (page)) {read_unlock_irq (&mapping->tree_lock);
@@ -715,8 +721,10 @@ unsigned find_get_pages(struct address_space *mapping, pgoff_t start,read_lock_irq (&mapping->tree_lock); ret =radix_tree_gang_lookup (&mapping->page_tree, (void **)pages, start, nr_pages); -for (i =0 ; i < ret; i++) +for (i =0 ; i < ret; i++) { +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (pages[i]));page_cache_get (pages[i]); + }read_unlock_irq (&mapping->tree_lock);return ret; }
@@ -746,6 +754,7 @@ unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t index,if (pages[i]->mapping ==NULL || pages[i]->index != index)break ; +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (pages[i]));page_cache_get (pages[i]); index++; }
@@ -774,8 +783,10 @@ unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index,read_lock_irq (&mapping->tree_lock); ret =radix_tree_gang_lookup_tag (&mapping->page_tree, (void **)pages, *index, nr_pages, tag); -for (i =0 ; i < ret; i++) +for (i =0 ; i < ret; i++) { +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (pages[i]));page_cache_get (pages[i]); + }if (ret) *index = pages[ret -1 ]->index +1 ;read_unlock_irq (&mapping->tree_lock);
@@ -2233,6 +2244,7 @@ int try_to_release_page(struct page *page, gfp_t gfp_mask)struct address_space *const mapping = page->mapping;BUG_ON (!PageLocked (page)); +VM_BUG_ON (mapping_order (mapping) !=page_cache_page_order (page));if (PageWriteback (page))return 0 ;
Signature · 3 lines
1.5.2.5 --
PATCH 31/41 Large Blocksize: Core piece
In reply to Christoph LameterProvide an alternate definition for the page_cache_xxx(mapping, ...) functions that can determine the current page size from the mapping and generate the appropriate shifts, sizes and mask for the page cache operations. Add alternate definitions for the page_cache_page_xx(page, ...) function as well. Change the basic functions that allocate pages for the page cache to be able to handle higher order allocations.
Provide a new function
mapping_setup(stdruct address_space *, gfp_t mask, int order)
that allows the setup of a mapping of any compound page order.
mapping_set_gfp_mask() is still provided but it sets mappings to order 0. Calls to mapping_set_gfp_mask() must be converted to mapping_setup() in order for the filesystem to be able to use larger pages. For some key block devices and filesystems the conversion is done here.
mapping_setup() for higher order is only allowed if the mapping does not use DMA mappings or HIGHMEM since we do not support bouncing at the moment. Thus BUG() on DMA mappings and clear the highmem bit of higher order mappings.
Modify the set_blocksize() function so that an arbitrary blocksize can be set. Blocksizes up to MAX_ORDER - 1 can be set. This is typically 8MB on many platforms (order 11). Typically file systems are not only limited by the core VM but also by the structure of their internal data structures. The core VM limitations fall away with this patch. The functionality provided here can do nothing about the internal limitations of filesystems.
Known internal limitations:
Ext2 64k XFS 64k Reiserfs 8k Ext3 64k Ext4 64k
--- block/Kconfig | 14 ++++ drivers/block/rd.c | 6 + fs/block_dev.c | 29 +++++--- fs/buffer.c | 4 - fs/inode.c | 7 +- fs/xfs/linux-2.6/xfs_buf.c | 3 include/linux/buffer_head.h | 12 +++ include/linux/fs.h | 5 + include/linux/pagemap.h | 150 ++++++++++++++++++++++++++++++++++++++++++-- mm/filemap.c | 17 +++- 10 files changed, 218 insertions(+), 29 deletions(-)
block/Kconfig +14 -0
@@ -62,6 +62,20 @@ config BLK_DEV_BSG protocols (e.g. Task Management Functions and SMP in Serial Attached SCSI). +# +# The functions to switch on larger pages in a filesystem will return an error +# if the gfp flags for a mapping require only DMA pages. Highmem will always +# be switched off for higher order mappings. +# +config LARGE_BLOCKSIZE + bool "Support blocksizes larger than page size" + default n + depends on EXPERIMENTAL + help + Allows the page cache to support higher orders of pages. Higher + order page cache pages may be useful to increase I/O performance + anbd support special devices like CD or DVDs and Flash. + endif # BLOCK source block/Kconfig.iosched
drivers/block/rd.c +4 -2
@@ -121,7 +121,8 @@ static void make_page_uptodate(struct pa } }while ((bh = bh->b_this_page) != head); }else { -memset (page_address (page),0 ,page_cache_size (page_mapping (page))); +memset (page_address (page),0 , +page_cache_page_size (page)); }flush_dcache_page (page);SetPageUptodate (page);
@@ -380,7 +381,8 @@ static int rd_open(struct inode *inode, gfp_mask =mapping_gfp_mask (mapping); gfp_mask &= ~(__GFP_FS|__GFP_IO); gfp_mask |= __GFP_HIGH; -mapping_set_gfp_mask (mapping, gfp_mask); +mapping_setup (mapping, gfp_mask, +page_cache_blkbits_to_order (inode->i_blkbits)); }return 0 ;
fs/block_dev.c +20 -9
@@ -63,36 +63,46 @@ static void kill_bdev(struct block_devicreturn ;invalidate_bh_lrus ();truncate_inode_pages (bdev->bd_inode->i_mapping,0 ); -} +}int set_blocksize (struct block_device *bdev,int size) { -/* Size must be a power of two, and between 512 and PAGE_SIZE */ -if (size >PAGE_SIZE || size <512 || !is_power_of_2 (size)) +int order; + +if (size > (PAGE_SIZE << (MAX_ORDER -1 )) || + size <512 || !is_power_of_2 (size))return -EINVAL ;/* Size cannot be smaller than the size supported by the device */ if (size <bdev_hardsect_size (bdev))return -EINVAL ; + order =page_cache_blocksize_to_order (size); +/* Don't change the size if it is same as current */ if (bdev->bd_block_size != size) { +int bits =blksize_bits (size); +struct address_space *mapping = + bdev->bd_inode->i_mapping; +sync_blockdev (bdev); - bdev->bd_block_size = size; - bdev->bd_inode->i_blkbits =blksize_bits (size);kill_bdev (bdev); + bdev->bd_block_size = size; + bdev->bd_inode->i_blkbits = bits; +mapping_setup (mapping,GFP_NOFS , order); }return 0 ; } -EXPORT_SYMBOL (set_blocksize);int sb_set_blocksize (struct super_block *sb,int size) {if (set_blocksize (sb->s_bdev, size))return 0 ; -/* If we get here, we know size is power of two -* and it's value is between 512 and PAGE_SIZE */ +/* +* If we get here, we know size is power of two +* and it's value is valid for the page cache +*/ sb->s_blocksize = size; sb->s_blocksize_bits =blksize_bits (size);return sb->s_blocksize;
@@ -574,7 +584,8 @@ struct block_device *bdget(dev_t dev) inode->i_rdev = dev; inode->i_bdev = bdev; inode->i_data.a_ops = &def_blk_aops; -mapping_set_gfp_mask (&inode->i_data,GFP_USER ); +mapping_setup (&inode->i_data,GFP_USER , +page_cache_blkbits_to_order (inode->i_blkbits)); inode->i_data.backing_dev_info = &default_backing_dev_info;spin_lock (&bdev_lock);list_add (&bdev->bd_list, &all_bdevs);
fs/buffer.c +2 -2
@@ -1090,7 +1090,7 @@ __getblk_slow(struct block_device *bdev, {/* Size must be multiple of hard sectorsize */ if (unlikely (size & (bdev_hardsect_size (bdev)-1 ) || - (size <512 || size >PAGE_SIZE ))) { + size <512 || size > (PAGE_SIZE << (MAX_ORDER -1 )))) {printk (KERN_ERR "getblk(): invalid block size %d requested\n" , size); printk(KERN_ERR "hardsect size: %d\n",
@@ -1428,7 +1428,7 @@ void set_bh_page(struct buffer_head *bh, struct page *page, unsigned long offset) { bh->b_page = page; -BUG_ON (offset >=PAGE_SIZE ); +BUG_ON (offset >=page_cache_page_size (page));if (PageHighMem (page))/* * This catches illegal uses and preserves the offset:
fs/inode.c +4 -3
@@ -145,7 +145,8 @@ static struct inode *alloc_inode(struct mapping->a_ops = &empty_aops; mapping->host = inode; mapping->flags =0 ; -mapping_set_gfp_mask (mapping,GFP_HIGHUSER_PAGECACHE ); +mapping_setup (mapping,GFP_HIGHUSER_PAGECACHE , +page_cache_blkbits_to_order (inode->i_blkbits)); mapping->assoc_mapping =NULL ; mapping->backing_dev_info = &default_backing_dev_info;
@@ -243,7 +244,7 @@ void clear_inode(struct inode *inode) {might_sleep ();invalidate_inode_buffers (inode); - +BUG_ON (inode->i_data.nrpages);BUG_ON (!(inode->i_state &I_FREEING ));BUG_ON (inode->i_state &I_CLEAR );
@@ -528,7 +529,7 @@ repeat:* for allocations related to inode->i_mapping is GFP_HIGHUSER_PAGECACHE. * If HIGHMEM pages are unsuitable or it is known that pages allocated * for the page cache are not reclaimable or migratable, -* mapping_set_gfp_mask() must be called with suitable flags on the +* mapping_setup() must be called with suitable flags and bits on the * newly created inode's mapping * */
fs/xfs/linux-2.6/xfs_buf.c +2 -1
@@ -1547,7 +1547,8 @@ xfs_mapping_buftarg( mapping = &inode->i_data; mapping->a_ops = &mapping_aops; mapping->backing_dev_info = bdi; -mapping_set_gfp_mask (mapping,GFP_NOFS ); +mapping_setup (mapping,GFP_NOFS , +page_cache_blkbits_to_order (inode->i_blkbits)); btp->bt_mapping = mapping;return 0 ; }
include/linux/buffer_head.h +11 -1
@@ -129,7 +129,17 @@ BUFFER_FNS(Ordered, ordered) BUFFER_FNS(Eopnotsupp, eopnotsupp) BUFFER_FNS(Unwritten, unwritten) -#define bh_offset (bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) +static inline unsigned long bh_offset (struct buffer_head *bh) +{ +/* +* No mapping available. Use page struct to obtain +* order. +*/ +unsigned long mask =page_cache_page_size (bh->b_page) -1 ; + +return (unsigned long )bh->b_data & mask; +} +#define touch_buffer (bh) mark_page_accessed(bh->b_page)/* If we *know* page->private refers to buffer_heads */
include/linux/fs.h +5 -0
@@ -446,6 +446,11 @@ struct address_space {spinlock_t i_mmap_lock;/* protect tree, count, list */ unsigned int truncate_count;/* Cover race condition with truncate */ unsigned long nrpages;/* number of total pages */ +#ifdef CONFIG_LARGE_BLOCKSIZE +loff_t offset_mask;/* Mask to get to offset bits */ +unsigned int order;/* Page order of the pages in here */ +unsigned int shift;/* Shift of index */ +#endif pgoff_t writeback_index;/* writeback starts here */ const struct address_space_operations *a_ops;/* methods */ unsigned long flags;/* error bits/gfp mask */
include/linux/pagemap.h +144 -6
199 lines not on this page · raw message
mm/filemap.c +12 -5
@@ -471,13 +471,13 @@ int add_to_page_cache_lru(struct page *p }#ifdef CONFIG_NUMA -struct page *__page_cache_alloc (gfp_t gfp) +struct page *__page_cache_alloc (gfp_t gfp,int order) {if (cpuset_do_page_mem_spread ()) {int n =cpuset_mem_spread_node (); -return alloc_pages_node (n, gfp,0 ); +return alloc_pages_node (n, gfp, order); } -return alloc_pages (gfp,0 ); +return alloc_pages (gfp, order); }EXPORT_SYMBOL (__page_cache_alloc);#endif
@@ -678,7 +678,7 @@ repeat:if (!page) {if (!cached_page) { cached_page = -__page_cache_alloc (gfp_mask); +__page_cache_alloc (gfp_mask,mapping_order (mapping));if (!cached_page)return NULL ; }
@@ -818,7 +818,8 @@ grab_cache_page_nowait(struct address_sppage_cache_release (page);return NULL ; } - page =__page_cache_alloc (mapping_gfp_mask (mapping) & ~__GFP_FS); + page =__page_cache_alloc (mapping_gfp_mask (mapping) & ~__GFP_FS, +mapping_order (mapping));if (page &&add_to_page_cache_lru (page, mapping, index,GFP_KERNEL )) {page_cache_release (page); page =NULL ;
@@ -1479,6 +1480,12 @@ int generic_file_mmap(struct file * file {struct address_space *mapping = file->f_mapping; +/* +* Forbid mmap access to higher order mappings. +*/ +if (mapping_order (mapping)) +return -ENOSYS ; +if (!mapping->a_ops->readpage)return -ENOEXEC ;file_accessed (file);
PATCH 32/41 Readahead changes to support large blocksize.
In reply to Christoph LameterFix up readhead for large I/O operations.
Only calculate the readahead until the 2M boundary then fall back to one page.
===================================================================
--- include/linux/mm.h | 2 +- mm/fadvise.c | 4 ++-- mm/filemap.c | 5 ++--- mm/madvise.c | 2 +- mm/readahead.c | 22 ++++++++++++++-------- 5 files changed, 20 insertions(+), 15 deletions(-)
include/linux/mm.h +1 -1
@@ -1142,7 +1142,7 @@ void page_cache_async_readahead(struct address_space *mapping,pgoff_t offset, unsigned long size); -unsigned long max_sane_readahead (unsigned long nr); +unsigned long max_sane_readahead (unsigned long nr,int order);/* Do stack extension */ extern int expand_stack (struct vm_area_struct *vma,unsigned long address);
mm/fadvise.c +2 -2
@@ -86,10 +86,10 @@ asmlinkage long sys_fadvise64_64(int fd, loff_t offset, loff_t len, int advice) nrpages = end_index - start_index +1 ;if (!nrpages) nrpages = ~0UL ; - + ret =force_page_cache_readahead (mapping, file, start_index, -max_sane_readahead (nrpages)); + nrpages);if (ret >0 ) ret =0 ;break ;
mm/filemap.c +2 -3
@@ -1237,8 +1237,7 @@ do_readahead(struct address_space *mapping, struct file *filp,if (!mapping || !mapping->a_ops || !mapping->a_ops->readpage)return -EINVAL ; -force_page_cache_readahead (mapping, filp, index, -max_sane_readahead (nr)); +force_page_cache_readahead (mapping, filp, index, nr);return 0 ; }
@@ -1373,7 +1372,7 @@ retry_find:count_vm_event (PGMAJFAULT ); } did_readaround =1 ; - ra_pages =max_sane_readahead (file->f_ra.ra_pages); + ra_pages = file->f_ra.ra_pages;if (ra_pages) {pgoff_t start =0 ;
mm/madvise.c +1 -1
@@ -124,7 +124,7 @@ static long madvise_willneed(struct vm_area_struct * vma, end = ((end - vma->vm_start) >>PAGE_SHIFT ) + vma->vm_pgoff;force_page_cache_readahead (file->f_mapping, - file, start,max_sane_readahead (end - start)); + file, start, end - start);return 0 ; }
mm/readahead.c +14 -8
@@ -44,7 +44,8 @@ EXPORT_SYMBOL_GPL(default_backing_dev_info);void file_ra_state_init (struct file_ra_state *ra,struct address_space *mapping) { - ra->ra_pages = mapping->backing_dev_info->ra_pages; + ra->ra_pages =DIV_ROUND_UP (mapping->backing_dev_info->ra_pages, +page_cache_size (mapping)); ra->prev_index =-1 ; }EXPORT_SYMBOL_GPL (file_ra_state_init);
@@ -84,7 +85,7 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,put_pages_list (pages);break ; } -task_io_account_read (PAGE_CACHE_SIZE ); +task_io_account_read (page_cache_size (mapping)); }pagevec_lru_add (&lru_pvec);return ret;
@@ -151,7 +152,7 @@ __do_page_cache_readahead(struct address_space *mapping, struct file *filp,if (isize ==0 ) goto out; - end_index = ((isize -1 ) >>PAGE_CACHE_SHIFT ); + end_index =page_cache_index (mapping, isize -1 );/* * Preallocate as many pages as we will need.
@@ -204,10 +205,12 @@ int force_page_cache_readahead(struct address_space *mapping, struct file *filp,if (unlikely (!mapping->a_ops->readpage && !mapping->a_ops->readpages))return -EINVAL ; + nr_to_read =max_sane_readahead (nr_to_read,mapping_order (mapping));while (nr_to_read) {int err; -unsigned long this_chunk = (2 *1024 *1024 ) /PAGE_CACHE_SIZE ; +unsigned long this_chunk =DIV_ROUND_UP (2 *1024 *1024 , +page_cache_size (mapping));if (this_chunk > nr_to_read) this_chunk = nr_to_read;
@@ -237,17 +240,20 @@ int do_page_cache_readahead(struct address_space *mapping, struct file *filp,if (bdi_read_congested (mapping->backing_dev_info))return -1 ; + nr_to_read =max_sane_readahead (nr_to_read,mapping_order (mapping));return __do_page_cache_readahead (mapping, filp, offset, nr_to_read,0 ); }/* -* Given a desired number of PAGE_CACHE_SIZE readahead pages, return a +* Given a desired number of page order readahead pages, return a * sensible upper limit. */ -unsigned long max_sane_readahead (unsigned long nr) +unsigned long max_sane_readahead (unsigned long nr,int order) { -return min (nr, (node_page_state (numa_node_id (),NR_INACTIVE ) - +node_page_state (numa_node_id (),NR_FREE_PAGES )) /2 ); +unsigned long base_pages =node_page_state (numa_node_id (),NR_INACTIVE ) + +node_page_state (numa_node_id (),NR_FREE_PAGES ); + +return min (nr, (base_pages /2 ) >> order); }/*
Signature · 3 lines
1.5.2.5 --
PATCH 33/41 Large blocksize support in ramfs
In reply to Christoph LameterThe simplest file system to use for large blocksize support is ramfs.
Note that ramfs does not use the lower layers (buffer I/O etc) so this case is useful for initial testing of changes to large buffer size support if one just wants to exercise the higher layers.
The patch adds the ability to specify a mount parameter to modify the order for the pages that are allocated by ramfs.
Here is an example of how to mount a volume with order 10 pages:
mount -tramfs -o10 none /media
Mounts a ramfs filesystem with 4MB sized pages. Then copy a file onto it.
cp linux-2.6.21-rc7.tar.gz /media
This will populate the ramfs volume. Note that we allocated 14 pages of 4M each instead of 13508.
Get rid of the large pages again
umount /media
--- fs/ramfs/inode.c | 12 +++++++++--- 1 files changed, 9 insertions(+), 3 deletions(-)
fs/ramfs/inode.c +9 -3
@@ -60,7 +60,8 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev) inode->i_blocks =0 ; inode->i_mapping->a_ops = &ramfs_aops; inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info; -mapping_set_gfp_mask (inode->i_mapping,GFP_HIGHUSER ); +mapping_setup (inode->i_mapping,GFP_HIGHUSER , + sb->s_blocksize_bits -PAGE_SHIFT ); inode->i_atime = inode->i_mtime = inode->i_ctime =CURRENT_TIME ;switch (mode &S_IFMT ) {default :
@@ -164,10 +165,15 @@ static int ramfs_fill_super(struct super_block * sb, void * data, int silent) {struct inode * inode;struct dentry * root; +int order =0 ; +char *options = data; + +if (options && *options) + order =simple_strtoul (options,NULL ,10 ); sb->s_maxbytes =MAX_LFS_FILESIZE ; - sb->s_blocksize =PAGE_CACHE_SIZE ; - sb->s_blocksize_bits =PAGE_CACHE_SHIFT ; + sb->s_blocksize =PAGE_CACHE_SIZE << order; + sb->s_blocksize_bits = order +PAGE_CACHE_SHIFT ; sb->s_magic =RAMFS_MAGIC ; sb->s_op = &ramfs_ops; sb->s_time_gran =1 ;
Signature · 3 lines
1.5.2.5 --
PATCH 34/41 Large blocksize support for XFS
In reply to Christoph LameterThe only change needed to enable Large Block I/O in XFS is to remove the check for a too large blocksize ;-)
--- fs/xfs/xfs_mount.c | 13 ------------- 1 files changed, 0 insertions(+), 13 deletions(-)
fs/xfs/xfs_mount.c +0 -13
@@ -326,19 +326,6 @@ xfs_mount_validate_sb(return XFS_ERROR (ENOSYS ); } -/* -* Until this is fixed only page-sized or smaller data blocks work. -*/ -if (unlikely (sbp->sb_blocksize >PAGE_SIZE )) { -xfs_fs_mount_cmn_err (flags, -"file system with blocksize %d bytes" , - sbp->sb_blocksize); -xfs_fs_mount_cmn_err (flags, -"only pagesize (%ld) or less will currently work." , -PAGE_SIZE ); -return XFS_ERROR (ENOSYS ); - } -return 0 ; }
Signature · 3 lines
1.5.2.5 --
PATCH 35/41 Reiserfs: Fix up mapping_set_gfp_mask()
In reply to Christoph Lametermapping_set_gfp_mask only works on order 0 page cache operations. Reiserfs can use 8k pages (order 1). Replace the mapping_set_gfp_mask with mapping_setup to make this work properly.
--- fs/reiserfs/xattr.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
fs/reiserfs/xattr.c +2 -1
@@ -405,9 +405,10 @@ static struct page *reiserfs_get_page(struct inode *dir, unsigned long n) {struct address_space *mapping = dir->i_mapping;struct page *page; +/* We can deadlock if we try to free dentries, and an unlink/rmdir has just occured - GFP_NOFS avoids this */ -mapping_set_gfp_mask (mapping,GFP_NOFS ); +mapping_setup (mapping,GFP_NOFS ,page_cache_shift (mapping)); page =read_mapping_page (mapping, n,NULL );if (!IS_ERR (page)) {kmap (page);
Signature · 3 lines
1.5.2.5 --
PATCH 36/41 64k block size support for Ext2/3/4
In reply to Christoph LameterThe next 4 patches support large block size (up to PAGESIZE, max 64KB) for ext2/3/4, originally from Takashi Sato.
http://marc.info/?l=linux-ext4&m=115768873518400&w=2
It's quite simple to support large block size in ext2/3/4, mostly just enlarge the block size limit. But it is NOT possible to have 64kB blocksize on ext2/3/4 without some changes to the directory handling code. The reason is that an empty 64kB directory block would have a rec_len == (__u16)2^16 == 0, and this would cause an error to be hit in the filesystem. The proposed solution is to put 2 empty records in such a directory, or to special-case an impossible value like rec_len = 0xffff to handle this.
The Patch-set consists of the following 4 patches.
[1/4] ext2/3/4: enlarge blocksize
- Allow blocksize up to pagesize
[2/4] ext2: fix rec_len overflow
- prevent rec_len from overflow with 64KB blocksize
[3/4] ext3: fix rec_len overflow
- prevent rec_len from overflow with 64KB blocksize
[4/4] ext4: fix rec_len overflow
- prevent rec_len from overflow with 64KB blocksize
Just rebase to 2.6.23-rc4 and against the ext4 patch queue. Compile tested only.
Next steps:
Need a e2fsprogs changes to able test this feature. As mkfs needs to be educated not assuming rec_len to be blocksize all the time. Will try it with Christoph Lameter's large block patch next.
--- fs/ext2/super.c | 2 +- fs/ext3/super.c | 5 ++++- fs/ext4/super.c | 5 +++++ include/linux/ext2_fs.h | 4 ++-- include/linux/ext3_fs.h | 4 ++-- include/linux/ext4_fs.h | 4 ++-- 6 files changed, 16 insertions(+), 8 deletions(-)
fs/ext2/super.c +1 -1
@@ -775,7 +775,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)brelse (bh);if (!sb_set_blocksize (sb, blocksize)) { -printk (KERN_ERR "EXT2-fs: blocksize too small for device.\n" ); +printk (KERN_ERR "EXT2-fs: bad blocksize %d.\n" , blocksize); goto failed_sbi; }
fs/ext3/super.c +4 -1
@@ -1549,7 +1549,10 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) }brelse (bh); -sb_set_blocksize (sb, blocksize); +if (!sb_set_blocksize (sb, blocksize)) { +printk (KERN_ERR "EXT3-fs: bad blocksize %d.\n" , blocksize); + goto out_fail; + } logic_sb_block = (sb_block *EXT3_MIN_BLOCK_SIZE ) / blocksize; offset = (sb_block *EXT3_MIN_BLOCK_SIZE ) % blocksize; bh =sb_bread (sb, logic_sb_block);
fs/ext4/super.c +5 -0
@@ -1496,6 +1496,11 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) goto out_fail; } +if (!sb_set_blocksize (sb, blocksize)) { +printk (KERN_ERR "EXT4-fs: bad blocksize %d.\n" , blocksize); + goto out_fail; + } +/* * The ext4 superblock will not be buffer aligned for other than 1kB * block sizes. We need to calculate the offset from buffer start.
include/linux/ext2_fs.h +2 -2
@@ -86,8 +86,8 @@ static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb)* Macro-instructions used to manage several block sizes */ #define EXT2_MIN_BLOCK_SIZE 1024 -#define EXT2_MAX_BLOCK_SIZE 4096 -#define EXT2_MIN_BLOCK_LOG_SIZE 10 +#define EXT2_MAX_BLOCK_SIZE 65536 +#define EXT2_MIN_BLOCK_LOG_SIZE 10#ifdef __KERNEL__# define EXT2_BLOCK_SIZE (s) ((s)->s_blocksize)#else
include/linux/ext3_fs.h +2 -2
@@ -76,8 +76,8 @@* Macro-instructions used to manage several block sizes */ #define EXT3_MIN_BLOCK_SIZE 1024 -#define EXT3_MAX_BLOCK_SIZE 4096 -#define EXT3_MIN_BLOCK_LOG_SIZE 10 +#define EXT3_MAX_BLOCK_SIZE 65536 +#define EXT3_MIN_BLOCK_LOG_SIZE 10#ifdef __KERNEL__# define EXT3_BLOCK_SIZE (s) ((s)->s_blocksize)#else
include/linux/ext4_fs.h +2 -2
@@ -77,8 +77,8 @@* Macro-instructions used to manage several block sizes */ #define EXT4_MIN_BLOCK_SIZE 1024 -#define EXT4_MAX_BLOCK_SIZE 4096 -#define EXT4_MIN_BLOCK_LOG_SIZE 10 +#define EXT4_MAX_BLOCK_SIZE 65536 +#define EXT4_MIN_BLOCK_LOG_SIZE 10#ifdef __KERNEL__# define EXT4_BLOCK_SIZE (s) ((s)->s_blocksize)#else
Signature · 3 lines
1.5.2.5 --
PATCH 37/41 ext2: fix rec_len overflow for 64KB block size
In reply to Christoph Lameter[2/4] ext2: fix rec_len overflow
- prevent rec_len from overflow with 64KB blocksize
--- fs/ext2/dir.c | 46 ++++++++++++++++++++++++++++++++++++---------- include/linux/ext2_fs.h | 13 +++++++++++++ 2 files changed, 49 insertions(+), 10 deletions(-)
fs/ext2/dir.c +36 -10
@@ -97,9 +97,9 @@ static void ext2_check_page(struct page *page) goto out; }for (offs =0 ; offs <= limit -EXT2_DIR_REC_LEN (1 ); offs += rec_len) { + offs =EXT2_DIR_ADJUST_TAIL_OFFS (offs, chunk_size); p = (ext2_dirent *)(kaddr + offs); rec_len =le16_to_cpu (p->rec_len); -if (rec_len <EXT2_DIR_REC_LEN (1 )) goto Eshort;if (rec_len &3 )
@@ -111,6 +111,7 @@ static void ext2_check_page(struct page *page)if (le32_to_cpu (p->inode) > max_inumber) goto Einumber; } + offs =EXT2_DIR_ADJUST_TAIL_OFFS (offs, chunk_size);if (offs != limit) goto Eend; out:
@@ -287,6 +288,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) de = (ext2_dirent *)(kaddr+offset); limit = kaddr +ext2_last_byte (inode, n) -EXT2_DIR_REC_LEN (1 );for ( ;(char *)de <= limit; de =ext2_next_entry (de)) { + de =EXT2_DIR_ADJUST_TAIL_ADDR (kaddr, de, sb->s_blocksize);if (de->rec_len ==0 ) {ext2_error (sb, __FUNCTION__,"zero-length directory entry" );
@@ -309,8 +311,10 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)return 0 ; } } + filp->f_pos =EXT2_DIR_ADJUST_TAIL_OFFS (filp->f_pos, sb->s_blocksize); filp->f_pos +=le16_to_cpu (de->rec_len); } + filp->f_pos =EXT2_DIR_ADJUST_TAIL_OFFS (filp->f_pos, sb->s_blocksize);ext2_put_page (page); }return 0 ;
@@ -348,13 +352,14 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, start =0 ; n = start; do { - char *kaddr; + char *kaddr, *page_start; page = ext2_get_page(dir, n); if (!IS_ERR(page)) { - kaddr = page_address(page); + kaddr = page_start = page_address(page); de = (ext2_dirent *) kaddr; kaddr += ext2_last_byte(dir, n) - reclen; while ((char *) de <= kaddr) { + de = EXT2_DIR_ADJUST_TAIL_ADDR(page_start, de, dir->i_sb->s_blocksize); if (de->rec_len == 0) { ext2_error(dir->i_sb, __FUNCTION__, "zero-length directory entry");
@@ -421,6 +426,7 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,unsigned to = from +le16_to_cpu (de->rec_len);int err; + to =EXT2_DIR_ADJUST_TAIL_OFFS (to, inode->i_sb->s_blocksize);lock_page (page); err = page->mapping->a_ops->prepare_write (NULL , page, from, to);BUG_ON (err);
@@ -452,6 +458,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode)char *kaddr;unsigned from, to;int err; +char *page_start =NULL ;/* * We take care of directory expansion in the same loop.
@@ -466,16 +473,28 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode)if (IS_ERR (page)) goto out;lock_page (page); - kaddr =page_address (page); + kaddr = page_start =page_address (page); dir_end = kaddr +ext2_last_byte (dir, n); de = (ext2_dirent *)kaddr; - kaddr +=page_cache_size (mapping) - reclen; +if (chunk_size <EXT2_DIR_MAX_REC_LEN ) { + kaddr +=page_cache_size (mapping) - reclen; + }else { + kaddr +=page_cache_size (mapping) - + (chunk_size -EXT2_DIR_MAX_REC_LEN ) - reclen; + }while ((char *)de <= kaddr) { + de =EXT2_DIR_ADJUST_TAIL_ADDR (page_start, de, chunk_size);if ((char *)de == dir_end) {/* We hit i_size */ name_len =0 ; - rec_len = chunk_size; - de->rec_len =cpu_to_le16 (chunk_size); +if (chunk_size <EXT2_DIR_MAX_REC_LEN ) { + rec_len = chunk_size; + de->rec_len =cpu_to_le16 (chunk_size); + }else { + rec_len =EXT2_DIR_MAX_REC_LEN ; + de->rec_len = +cpu_to_le16 (EXT2_DIR_MAX_REC_LEN ); + } de->inode =0 ; goto got_it; }
@@ -505,6 +524,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) got_it: from = (char *)de - (char *)page_address (page); to = from + rec_len; + to =EXT2_DIR_ADJUST_TAIL_OFFS (to, chunk_size); err = page->mapping->a_ops->prepare_write (NULL , page, from, to);if (err) goto out_unlock;
@@ -547,6 +567,7 @@ int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page )ext2_dirent * de = (ext2_dirent *) (kaddr + from);int err; + to =EXT2_DIR_ADJUST_TAIL_OFFS (to, inode->i_sb->s_blocksize);while ((char *)de < (char *)dir) {if (de->rec_len ==0 ) { ext2_error(inode->i_sb, __FUNCTION__,
@@ -604,7 +625,11 @@ int ext2_make_empty(struct inode *inode, struct inode *parent) de = (struct ext2_dir_entry_2 *)(kaddr +EXT2_DIR_REC_LEN (1 )); de->name_len =2 ; - de->rec_len =cpu_to_le16 (chunk_size -EXT2_DIR_REC_LEN (1 )); +if (chunk_size <EXT2_DIR_MAX_REC_LEN ) { + de->rec_len =cpu_to_le16 (chunk_size -EXT2_DIR_REC_LEN (1 )); + }else { + de->rec_len =cpu_to_le16 (EXT2_DIR_MAX_REC_LEN -EXT2_DIR_REC_LEN (1 )); + } de->inode =cpu_to_le32 (parent->i_ino);memcpy (de->name,"..\0" ,4 );ext2_set_de_type (de, inode);
@@ -624,18 +649,19 @@ int ext2_empty_dir (struct inode * inode)unsigned long i, npages =dir_pages (inode);for (i =0 ; i < npages; i++) { -char *kaddr; +char *kaddr, *page_start;ext2_dirent * de; page =ext2_get_page (inode, i);if (IS_ERR (page))continue ; - kaddr =page_address (page); + kaddr = page_start =page_address (page); de = (ext2_dirent *)kaddr; kaddr +=ext2_last_byte (inode, i) -EXT2_DIR_REC_LEN (1 );while ((char *)de <= kaddr) { + de =EXT2_DIR_ADJUST_TAIL_ADDR (page_start, de, inode->i_sb->s_blocksize);if (de->rec_len ==0 ) {ext2_error (inode->i_sb, __FUNCTION__,"zero-length directory entry" );
include/linux/ext2_fs.h +13 -0
@@ -557,5 +557,18 @@ enum {#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)#define EXT2_DIR_REC_LEN (name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) +#define EXT2_DIR_MAX_REC_LEN 65532 + +/* +* Align a tail offset(address) to the end of a directory block +*/ +#define EXT2_DIR_ADJUST_TAIL_OFFS (offs, bsize) \ + ((((offs) & ((bsize) -1)) == EXT2_DIR_MAX_REC_LEN) ? \ + ((offs) + (bsize) - EXT2_DIR_MAX_REC_LEN):(offs)) + +#define EXT2_DIR_ADJUST_TAIL_ADDR (page, de, bsize) \ + (((((char*)(de) - (page)) & ((bsize) - 1)) == EXT2_DIR_MAX_REC_LEN) ? \ + ((ext2_dirent*)((char*)(de) + (bsize) - EXT2_DIR_MAX_REC_LEN)):(de))#endif /* _LINUX_EXT2_FS_H */ +
Signature · 3 lines
1.5.2.5 --
PATCH 38/41 ext3: fix rec_len overflow with 64KB block size
In reply to Christoph Lameter[3/4] ext3: fix rec_len overflow
- prevent rec_len from overflow with 64KB blocksize
--- fs/ext3/dir.c | 13 +++++-- fs/ext3/namei.c | 88 +++++++++++++++++++++++++++++++++++++++-------- include/linux/ext3_fs.h | 9 +++++ 3 files changed, 91 insertions(+), 19 deletions(-)
fs/ext3/dir.c +9 -4
@@ -100,10 +100,11 @@ static int ext3_readdir(struct file * filp,unsigned long offset;int i, stored;struct ext3_dir_entry_2 *de; -struct super_block *sb;int err;struct inode *inode = filp->f_path.dentry->d_inode;int ret =0 ; +struct super_block *sb = inode->i_sb; +unsigned tail = sb->s_blocksize; sb = inode->i_sb;
@@ -168,8 +169,11 @@ revalidate:* readdir(2), then we might be pointing to an invalid * dirent right now. Scan from the start of the block * to make sure. */ -if (filp->f_version != inode->i_version) { -for (i =0 ; i < sb->s_blocksize && i < offset; ) { +if (tail >EXT3_DIR_MAX_REC_LEN ) { + tail =EXT3_DIR_MAX_REC_LEN ; + } +if (filp->f_version != inode->i_version) { +for (i =0 ; i < tail && i < offset; ) { de = (struct ext3_dir_entry_2 *) (bh->b_data + i);/* It's too expensive to do a full
@@ -190,7 +194,7 @@ revalidate: }while (!error && filp->f_pos < inode->i_size - && offset < sb->s_blocksize) { + && offset < tail) { de = (struct ext3_dir_entry_2 *) (bh->b_data + offset);if (!ext3_check_dir_entry ("ext3_readdir" , inode, de, bh, offset)) {
@@ -226,6 +230,7 @@ revalidate: } filp->f_pos +=le16_to_cpu (de->rec_len); } + filp->f_pos =EXT3_DIR_ADJUST_TAIL_OFFS (filp->f_pos, sb->s_blocksize); offset =0 ;brelse (bh); }
fs/ext3/namei.c +73 -15
196 lines not on this page · raw message
include/linux/ext3_fs.h +9 -0
@@ -660,6 +660,15 @@ struct ext3_dir_entry_2 {#define EXT3_DIR_ROUND (EXT3_DIR_PAD - 1)#define EXT3_DIR_REC_LEN (name_len) (((name_len) + 8 + EXT3_DIR_ROUND) & \ ~EXT3_DIR_ROUND) +#define EXT3_DIR_MAX_REC_LEN 65532 + +/* +* Align a tail offset to the end of a directory block +*/ +#define EXT3_DIR_ADJUST_TAIL_OFFS (offs, bsize) \ + ((((offs) & ((bsize) -1)) == EXT3_DIR_MAX_REC_LEN) ? \ + ((offs) + (bsize) - EXT3_DIR_MAX_REC_LEN):(offs)) +/* * Hash Tree Directory indexing * (c) Daniel Phillips, 2001
Signature · 3 lines
1.5.2.5 --
PATCH 39/41 ext4: fix rec_len overflow for 64KB block size
In reply to Christoph Lameter[4/4] ext4: fix rec_len overflow
- prevent rec_len from overflow with 64KB blocksize
--- fs/ext4/dir.c | 11 ++++-- fs/ext4/namei.c | 88 +++++++++++++++++++++++++++++++++++++++-------- include/linux/ext4_fs.h | 9 +++++ 3 files changed, 90 insertions(+), 18 deletions(-)
fs/ext4/dir.c +8 -3
@@ -100,10 +100,11 @@ static int ext4_readdir(struct file * filp,unsigned long offset;int i, stored;struct ext4_dir_entry_2 *de; -struct super_block *sb;int err;struct inode *inode = filp->f_path.dentry->d_inode; +struct super_block *sb = inode->i_sb;int ret =0 ; +unsigned tail = sb->s_blocksize; sb = inode->i_sb;
@@ -167,8 +168,11 @@ revalidate:* readdir(2), then we might be pointing to an invalid * dirent right now. Scan from the start of the block * to make sure. */ +if (tail >EXT4_DIR_MAX_REC_LEN ) { + tail =EXT4_DIR_MAX_REC_LEN ; + }if (filp->f_version != inode->i_version) { -for (i =0 ; i < sb->s_blocksize && i < offset; ) { +for (i =0 ; i < tail && i < offset; ) { de = (struct ext4_dir_entry_2 *) (bh->b_data + i);/* It's too expensive to do a full
@@ -189,7 +193,7 @@ revalidate: }while (!error && filp->f_pos < inode->i_size - && offset < sb->s_blocksize) { + && offset < tail) { de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);if (!ext4_check_dir_entry ("ext4_readdir" , inode, de, bh, offset)) {
@@ -226,6 +230,7 @@ revalidate: } filp->f_pos +=le16_to_cpu (de->rec_len); } + filp->f_pos =EXT4_DIR_ADJUST_TAIL_OFFS (filp->f_pos, sb->s_blocksize); offset =0 ;brelse (bh); }
fs/ext4/namei.c +73 -15
196 lines not on this page · raw message
include/linux/ext4_fs.h +9 -0
@@ -784,6 +784,15 @@ struct ext4_dir_entry_2 {#define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1)#define EXT4_DIR_REC_LEN (name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & \ ~EXT4_DIR_ROUND) +#define EXT4_DIR_MAX_REC_LEN 65532 + +/* +* Align a tail offset to the end of a directory block +*/ +#define EXT4_DIR_ADJUST_TAIL_OFFS (offs, bsize) \ + ((((offs) & ((bsize) -1)) == EXT4_DIR_MAX_REC_LEN) ? \ + ((offs) + (bsize) - EXT4_DIR_MAX_REC_LEN):(offs)) +/* * Hash Tree Directory indexing * (c) Daniel Phillips, 2001
Signature · 3 lines
1.5.2.5 --
PATCH 40/41 Do not use f_mapping in simple_prepare_write()
In reply to Christoph LameterSeems that simple_prepare_write() may occasionally be called for a page that does not have a mapping yet. Retrieve the page size from the page instead of the mapping.
--- fs/libfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/libfs.c +2 -2
@@ -341,10 +341,10 @@ int simple_prepare_write(struct file *fi unsigned from, unsigned to) {if (!PageUptodate (page)) { -if (to - from !=page_cache_size (file->f_mapping)) +if (to - from !=page_cache_page_size (page))zero_user_segments (page,0 , from, - to,page_cache_size (file->f_mapping)); + to,page_cache_page_size (page)); }return 0 ; }
PATCH 41/41 Mmap support using pte PAGE_SIZE mappings
In reply to Christoph LameterThis is realized by mmapping base page size (4k on x86) of the potentially larger page. Mmap semantics are not changed and therefore the large buffers can be handled by userspace as if files consisted of 4k pages (as it is now). The use of large buffersizes can therefore be fully transparent to user space.
Details:
- Modify the rmap functions (try_to_unmap, page_referenced and page_mkclean) to interate over all base pages of a large buffer to get all ptes that may point to a larger buffer.
- Change vm_fault logic in __do_fault() and filemap_fault() to convert from 4k pte logic into a pointer to the page of the large buffer and an index to the basepage in that large buffer.
- Fix up the memory policy address scan to skip tail pages of large buffers.
- Fix up page migration to allow the moving of large buffers.
Tested by formatting a 32k ext2 filesystem, booting off it and building a kernel.
--- include/linux/mm.h | 1 mm/filemap.c | 27 ++++++++---------- mm/fremap.c | 8 +++-- mm/memory.c | 79 ++++++++++++++++++++++++++++++++++++++--------------- mm/mempolicy.c | 27 ++++++++++++------ mm/migrate.c | 17 ++++++++--- mm/rmap.c | 63 ++++++++++++++++++++++++++++++++++-------- 7 files changed, 158 insertions(+), 64 deletions(-)
mm/filemap.c +12 -15
@@ -1320,9 +1320,12 @@ int filemap_fault(struct vm_area_structunsigned long size;int did_readaround =0 ;int ret =0 ; +pgoff_t pgoff = vmf->pgoff >>mapping_order (mapping); + vmf->page_index = + vmf->pgoff & ((1 <<mapping_order (mapping)) -1 ); size =page_cache_next (mapping,i_size_read (inode)); -if (vmf->pgoff >= size) +if (pgoff >= size) goto outside_data_content;/* If we don't want any read-ahead, don't bother */
@@ -1333,21 +1336,21 @@ int filemap_fault(struct vm_area_struct* Do we have something in the page cache already? */ retry_find: - page =find_lock_page (mapping, vmf->pgoff); + page =find_lock_page (mapping, pgoff);/* * For sequential accesses, we use the generic readahead logic. */ if (VM_SequentialReadHint (vma)) {if (!page) {page_cache_sync_readahead (mapping, ra, file, - vmf->pgoff,1 ); - page =find_lock_page (mapping, vmf->pgoff); + pgoff,1 ); + page =find_lock_page (mapping, pgoff);if (!page) goto no_cached_page; }if (PageReadahead (page)) {page_cache_async_readahead (mapping, ra, file, page, - vmf->pgoff,1 ); + pgoff,1 ); } }
@@ -1377,10 +1380,10 @@ retry_find:pgoff_t start =0 ;if (vmf->pgoff > ra_pages /2 ) - start = vmf->pgoff - ra_pages /2 ; + start = pgoff - ra_pages /2 ;do_page_cache_readahead (mapping, file, start, ra_pages); } - page =find_lock_page (mapping, vmf->pgoff); + page =find_lock_page (mapping, pgoff);if (!page) goto no_cached_page; }
@@ -1397,7 +1400,7 @@ retry_find:/* Must recheck i_size under page lock */ size =page_cache_next (mapping,i_size_read (inode)); -if (unlikely (vmf->pgoff >= size)) { +if (unlikely (pgoff >= size)) {unlock_page (page); goto outside_data_content; }
@@ -1424,7 +1427,7 @@ no_cached_page:* We're only likely to ever get here if MADV_RANDOM is in * effect. */ - error =page_cache_read (file, vmf->pgoff); + error =page_cache_read (file, pgoff);/* * The page we want has now been added to the page cache.
@@ -1479,12 +1482,6 @@ int generic_file_mmap(struct file * file {struct address_space *mapping = file->f_mapping; -/* -* Forbid mmap access to higher order mappings. -*/ -if (mapping_order (mapping)) -return -ENOSYS ; -if (!mapping->a_ops->readpage)return -ENOEXEC ;file_accessed (file);
mm/memory.c +58 -21
192 lines not on this page · raw message
include/linux/mm.h +1 -0
@@ -216,6 +216,7 @@ struct vm_fault {* is set (which is also implied by * VM_FAULT_ERROR). */ +int page_index;/* Index into compound page */ };/*
mm/rmap.c +51 -12
139 lines not on this page · raw message
mm/fremap.c +5 -3
@@ -32,10 +32,12 @@ static void zap_pte(struct mm_struct *mm pte =ptep_clear_flush (vma, addr, ptep); page =vm_normal_page (vma, addr, pte);if (page) { +struct page *head =page_cache_head (page); +if (pte_dirty (pte)) -set_page_dirty (page); -page_remove_rmap (page, vma); -page_cache_release (page); +set_page_dirty (head); +page_remove_rmap (head, vma); +page_cache_release (head);update_hiwater_rss (mm);dec_mm_counter (mm, file_rss); }
mm/mempolicy.c +19 -8
@@ -227,12 +227,16 @@ static int check_pte_range(struct vm_are do { struct page *page; int nid; + int pages = 1; if (!pte_present(*pte)) - continue; + goto next; page = vm_normal_page(vma, addr, *pte); - if (!page) - continue; + if (!page || PageTail(page)) + goto next; + + pages = page_cache_base_pages(page); + /* * The check for PageReserved here is important to avoid * handling zero pages and other pages that may have been
@@ -245,10 +249,10 @@ static int check_pte_range(struct vm_are* to put zero pages on the migration list. */ if (PageReserved (page)) -continue ; + goto next; nid =page_to_nid (page);if (node_isset (nid, *nodes) == !!(flags &MPOL_MF_INVERT )) -continue ; + goto next;if (flags &MPOL_MF_STATS )gather_stats (page, private,pte_dirty (*pte));
@@ -256,7 +260,11 @@ static int check_pte_range(struct vm_aremigrate_page_add (page, private, flags); else break; - }while (pte++, addr +=PAGE_SIZE , addr != end); + next: + pte += pages; + addr +=PAGE_SIZE * pages; + }while (addr != end); +pte_unmap_unlock (orig_pte, ptl);return addr != end; }
@@ -592,9 +600,12 @@ static void migrate_page_add(struct pageisolate_lru_page (page, pagelist); } -static struct page *new_node_page (struct page *page,unsigned long node,int **x) +static struct page *new_node_page (struct page *page, +unsigned long node,int **x) { -return alloc_pages_node (node,GFP_HIGHUSER_MOVABLE ,0 ); +return alloc_pages_node (node, +GFP_HIGHUSER_MOVABLE | __GFP_COMP, +page_cache_page_order (page)); }/*
mm/migrate.c +12 -5
@@ -196,15 +196,17 @@ static void remove_file_migration_ptes(sstruct address_space *mapping =page_mapping (new);struct prio_tree_iter iter;pgoff_t pgoff = new->index <<mapping_order (mapping); +int i;if (!mapping)return ;spin_lock (&mapping->i_mmap_lock); - vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) -remove_migration_pte (vma, old, new); - + vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { +for (i =0 ; i <page_cache_base_pages (old); i++) +remove_migration_pte (vma, old + i, new + i); + }spin_unlock (&mapping->i_mmap_lock); }
@@ -355,7 +357,10 @@ static int migrate_page_move_mapping(str*/ static void migrate_page_copy (struct page *newpage,struct page *page) { -copy_highpage (newpage, page); +int i; + +for (i =0 ; i <page_cache_base_pages (page); i++) +copy_highpage (newpage + i, page + i);if (PageError (page))SetPageError (newpage);
@@ -785,7 +790,8 @@ static struct page *new_page_node(struct *result = &pm->status;return alloc_pages_node (pm->node, -GFP_HIGHUSER_MOVABLE |GFP_THISNODE ,0 ); +GFP_HIGHUSER_MOVABLE |GFP_THISNODE | __GFP_COMP, +page_cache_page_order (p)); }/*
@@ -826,6 +832,7 @@ static int do_move_pages(struct mm_strucif (!page) goto set_status; + page =page_cache_head (page);if (PageReserved (page))/* Check for zero page */ goto put_and_set;