From: Christoph Lameter Migrate a page with buffers without requiring writeback This introduces a new address space operation migratepage() that may be used by a filesystem to implement its own version of page migration. A version is provided that migrates buffers attached to pages. Some filesystems (ext2, ext3, xfs) are modified to utilize this feature. The swapper address space operation are modified so that a regular migrate_page() will occur for anonymous pages without writeback (migrate_pages forces every anonymous page to have a swap entry). Signed-off-by: Mike Kravetz Signed-off-by: Christoph Lameter Signed-off-by: Andrew Morton --- fs/buffer.c | 65 ++++++++++++++++++++++++++++++++++ fs/ext2/inode.c | 2 + fs/ext3/inode.c | 2 + fs/xfs/linux-2.6/xfs_aops.c | 1 fs/xfs/linux-2.6/xfs_buf.c | 1 include/linux/fs.h | 8 ++++ include/linux/swap.h | 4 ++ mm/rmap.c | 1 mm/swap_state.c | 1 mm/vmscan.c | 20 ++++++++++ 10 files changed, 105 insertions(+) diff -puN fs/buffer.c~direct-migration-v9-avoid-writeback--page_migrate-method fs/buffer.c --- 25/fs/buffer.c~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/fs/buffer.c Tue Jan 17 16:24:06 2006 @@ -3050,6 +3050,71 @@ asmlinkage long sys_bdflush(int func, lo } /* + * Migration function for pages with buffers. This function can only be used + * if the underlying filesystem guarantees that no other references to "page" + * exist. + */ +#ifdef CONFIG_MIGRATION +int buffer_migrate_page(struct page *newpage, struct page *page) +{ + struct address_space *mapping = page->mapping; + struct buffer_head *bh, *head; + + if (!mapping) + return -EAGAIN; + + if (!page_has_buffers(page)) + return migrate_page(newpage, page); + + head = page_buffers(page); + + if (migrate_page_remove_references(newpage, page, 3)) + return -EAGAIN; + + spin_lock(&mapping->private_lock); + + bh = head; + do { + get_bh(bh); + lock_buffer(bh); + bh = bh->b_this_page; + + } while (bh != head); + + ClearPagePrivate(page); + set_page_private(newpage, page_private(page)); + set_page_private(page, 0); + put_page(page); + get_page(newpage); + + bh = head; + do { + set_bh_page(bh, newpage, bh_offset(bh)); + bh = bh->b_this_page; + + } while (bh != head); + + SetPagePrivate(newpage); + spin_unlock(&mapping->private_lock); + + migrate_page_copy(newpage, page); + + spin_lock(&mapping->private_lock); + bh = head; + do { + unlock_buffer(bh); + put_bh(bh); + bh = bh->b_this_page; + + } while (bh != head); + spin_unlock(&mapping->private_lock); + + return 0; +} +EXPORT_SYMBOL(buffer_migrate_page); +#endif + +/* * Buffer-head allocation */ static kmem_cache_t *bh_cachep; diff -puN fs/ext2/inode.c~direct-migration-v9-avoid-writeback--page_migrate-method fs/ext2/inode.c --- 25/fs/ext2/inode.c~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/fs/ext2/inode.c Tue Jan 17 16:24:06 2006 @@ -706,6 +706,7 @@ struct address_space_operations ext2_aop .bmap = ext2_bmap, .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, + .migratepage = buffer_migrate_page, }; struct address_space_operations ext2_aops_xip = { @@ -723,6 +724,7 @@ struct address_space_operations ext2_nob .bmap = ext2_bmap, .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, + .migratepage = buffer_migrate_page, }; /* diff -puN fs/ext3/inode.c~direct-migration-v9-avoid-writeback--page_migrate-method fs/ext3/inode.c --- 25/fs/ext3/inode.c~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/fs/ext3/inode.c Tue Jan 17 16:24:06 2006 @@ -1559,6 +1559,7 @@ static struct address_space_operations e .invalidatepage = ext3_invalidatepage, .releasepage = ext3_releasepage, .direct_IO = ext3_direct_IO, + .migratepage = buffer_migrate_page, }; static struct address_space_operations ext3_writeback_aops = { @@ -1572,6 +1573,7 @@ static struct address_space_operations e .invalidatepage = ext3_invalidatepage, .releasepage = ext3_releasepage, .direct_IO = ext3_direct_IO, + .migratepage = buffer_migrate_page, }; static struct address_space_operations ext3_journalled_aops = { diff -puN fs/xfs/linux-2.6/xfs_aops.c~direct-migration-v9-avoid-writeback--page_migrate-method fs/xfs/linux-2.6/xfs_aops.c --- 25/fs/xfs/linux-2.6/xfs_aops.c~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/fs/xfs/linux-2.6/xfs_aops.c Tue Jan 17 16:24:06 2006 @@ -1439,4 +1439,5 @@ struct address_space_operations linvfs_a .commit_write = generic_commit_write, .bmap = linvfs_bmap, .direct_IO = linvfs_direct_IO, + .migratepage = buffer_migrate_page, }; diff -puN fs/xfs/linux-2.6/xfs_buf.c~direct-migration-v9-avoid-writeback--page_migrate-method fs/xfs/linux-2.6/xfs_buf.c --- 25/fs/xfs/linux-2.6/xfs_buf.c~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/fs/xfs/linux-2.6/xfs_buf.c Tue Jan 17 16:24:06 2006 @@ -1514,6 +1514,7 @@ xfs_mapping_buftarg( struct address_space *mapping; static struct address_space_operations mapping_aops = { .sync_page = block_sync_page, + .migratepage = fail_migrate_page, }; inode = new_inode(bdev->bd_inode->i_sb); diff -puN include/linux/fs.h~direct-migration-v9-avoid-writeback--page_migrate-method include/linux/fs.h --- 25/include/linux/fs.h~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/include/linux/fs.h Tue Jan 17 16:24:06 2006 @@ -365,6 +365,8 @@ struct address_space_operations { loff_t offset, unsigned long nr_segs); struct page* (*get_xip_page)(struct address_space *, sector_t, int); + /* migrate the contents of a page to the specified target */ + int (*migratepage) (struct page *, struct page *); }; struct backing_dev_info; @@ -1718,6 +1720,12 @@ extern void simple_release_fs(struct vfs extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t); +#ifdef CONFIG_MIGRATION +extern int buffer_migrate_page(struct page *, struct page *); +#else +#define buffer_migrate_page NULL +#endif + extern int inode_change_ok(struct inode *, struct iattr *); extern int __must_check inode_setattr(struct inode *, struct iattr *); diff -puN include/linux/swap.h~direct-migration-v9-avoid-writeback--page_migrate-method include/linux/swap.h --- 25/include/linux/swap.h~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/include/linux/swap.h Tue Jan 17 16:24:06 2006 @@ -194,11 +194,15 @@ extern int migrate_page(struct page *, s extern void migrate_page_copy(struct page *, struct page *); extern int migrate_pages(struct list_head *l, struct list_head *t, struct list_head *moved, struct list_head *failed); +extern int fail_migrate_page(struct page *, struct page *); #else static inline int isolate_lru_page(struct page *p) { return -ENOSYS; } static inline int putback_lru_pages(struct list_head *l) { return 0; } static inline int migrate_pages(struct list_head *l, struct list_head *t, struct list_head *moved, struct list_head *failed) { return -ENOSYS; } +/* Possible settings for the migrate_page() method in address_operations */ +#define migrate_page NULL +#define fail_migrate_page NULL #endif #ifdef CONFIG_MMU diff -puN mm/rmap.c~direct-migration-v9-avoid-writeback--page_migrate-method mm/rmap.c --- 25/mm/rmap.c~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/mm/rmap.c Tue Jan 17 16:24:06 2006 @@ -233,6 +233,7 @@ void remove_from_swap(struct page *page) delete_from_swap_cache(page); } +EXPORT_SYMBOL(remove_from_swap); #endif /* diff -puN mm/swap_state.c~direct-migration-v9-avoid-writeback--page_migrate-method mm/swap_state.c --- 25/mm/swap_state.c~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/mm/swap_state.c Tue Jan 17 16:24:06 2006 @@ -27,6 +27,7 @@ static struct address_space_operations s .writepage = swap_writepage, .sync_page = block_sync_page, .set_page_dirty = __set_page_dirty_nobuffers, + .migratepage = migrate_page, }; static struct backing_dev_info swap_backing_dev_info = { diff -puN mm/vmscan.c~direct-migration-v9-avoid-writeback--page_migrate-method mm/vmscan.c --- 25/mm/vmscan.c~direct-migration-v9-avoid-writeback--page_migrate-method Tue Jan 17 16:24:06 2006 +++ 25-akpm/mm/vmscan.c Tue Jan 17 16:24:06 2006 @@ -609,6 +609,15 @@ int putback_lru_pages(struct list_head * } /* + * Non migratable page + */ +int fail_migrate_page(struct page *newpage, struct page *page) +{ + return -EIO; +} +EXPORT_SYMBOL(fail_migrate_page); + +/* * swapout a single page * page is locked upon entry, unlocked on exit */ @@ -653,6 +662,7 @@ unlock_retry: retry: return -EAGAIN; } +EXPORT_SYMBOL(swap_page); /* * Page migration was first developed in the context of the memory hotplug @@ -743,6 +753,7 @@ static int migrate_page_remove_reference return 0; } +EXPORT_SYMBOL(migrate_page_remove_references); /* * Copy the page to its new location @@ -782,6 +793,7 @@ void migrate_page_copy(struct page *newp if (PageWriteback(newpage)) end_page_writeback(newpage); } +EXPORT_SYMBOL(migrate_page_copy); /* * Common logic to directly migrate a single page suitable for @@ -809,6 +821,7 @@ int migrate_page(struct page *newpage, s remove_from_swap(newpage); return 0; } +EXPORT_SYMBOL(migrate_page); /* * migrate_pages @@ -908,6 +921,11 @@ redo: if (!mapping) goto unlock_both; + if (mapping->a_ops->migratepage) { + rc = mapping->a_ops->migratepage(newpage, page); + goto unlock_both; + } + /* * Trigger writeout if page is dirty */ @@ -977,6 +995,7 @@ next: return nr_failed + retry; } +EXPORT_SYMBOL(migrate_pages); /* * Isolate one page from the LRU lists and put it on the @@ -1006,6 +1025,7 @@ int isolate_lru_page(struct page *page) return ret; } +EXPORT_SYMBOL(isolate_lru_page); #endif /* _