Migrate a page with buffers without requiring writeback This introduces a new address space operation migrate_page() 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 are modified to utilize this feature. The swapper address space operation are modified so that a regular migrate_pages() will occur for anonymous pages without writeback. Signed-off-by: Mike Kravetz Signed-off-by: Christoph Lameter Index: linux-2.6.14-rc5-mm1/include/linux/fs.h =================================================================== --- linux-2.6.14-rc5-mm1.orig/include/linux/fs.h 2005-10-31 14:11:15.000000000 -0800 +++ linux-2.6.14-rc5-mm1/include/linux/fs.h 2005-11-02 14:15:31.000000000 -0800 @@ -327,6 +327,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 (*migrate_page) (struct page *, struct page *); }; struct backing_dev_info; Index: linux-2.6.14-rc5-mm1/mm/swap_state.c =================================================================== --- linux-2.6.14-rc5-mm1.orig/mm/swap_state.c 2005-10-31 14:11:17.000000000 -0800 +++ linux-2.6.14-rc5-mm1/mm/swap_state.c 2005-11-02 14:15:31.000000000 -0800 @@ -26,6 +26,7 @@ static struct address_space_operations s .writepage = swap_writepage, .sync_page = block_sync_page, .set_page_dirty = __set_page_dirty_nobuffers, + .migrate_page = migrate_page, }; static struct backing_dev_info swap_backing_dev_info = { Index: linux-2.6.14-rc5-mm1/fs/xfs/linux-2.6/xfs_aops.c =================================================================== --- linux-2.6.14-rc5-mm1.orig/fs/xfs/linux-2.6/xfs_aops.c 2005-10-19 23:23:05.000000000 -0700 +++ linux-2.6.14-rc5-mm1/fs/xfs/linux-2.6/xfs_aops.c 2005-11-02 14:15:31.000000000 -0800 @@ -1356,4 +1356,5 @@ struct address_space_operations linvfs_a .commit_write = generic_commit_write, .bmap = linvfs_bmap, .direct_IO = linvfs_direct_IO, + .migrate_page = buffer_migrate_page, }; Index: linux-2.6.14-rc5-mm1/fs/buffer.c =================================================================== --- linux-2.6.14-rc5-mm1.orig/fs/buffer.c 2005-10-31 14:11:00.000000000 -0800 +++ linux-2.6.14-rc5-mm1/fs/buffer.c 2005-11-02 14:15:31.000000000 -0800 @@ -3024,6 +3024,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" + * exists. + */ +int buffer_migrate_page(struct page *newpage, struct page *page) +{ + int rc; + struct address_space *mapping = page->mapping; + struct buffer_head *bh, *head = page_buffers(page); + + /* + * Remove the mapping so the page can be migrated. + */ + if (!mapping) + return -ENOENT; + + if (!PagePrivate(page)) + return migrate_page(newpage, page); + + rc = migrate_page_remove_references(newpage, page, 3); + + if (rc) + return rc; + + 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; +} + +/* * Buffer-head allocation */ static kmem_cache_t *bh_cachep; Index: linux-2.6.14-rc5-mm1/fs/ext3/inode.c =================================================================== --- linux-2.6.14-rc5-mm1.orig/fs/ext3/inode.c 2005-10-31 14:11:03.000000000 -0800 +++ linux-2.6.14-rc5-mm1/fs/ext3/inode.c 2005-11-02 14:15:31.000000000 -0800 @@ -1557,6 +1557,7 @@ static struct address_space_operations e .invalidatepage = ext3_invalidatepage, .releasepage = ext3_releasepage, .direct_IO = ext3_direct_IO, + .migrate_page = buffer_migrate_page, }; static struct address_space_operations ext3_writeback_aops = { @@ -1570,6 +1571,7 @@ static struct address_space_operations e .invalidatepage = ext3_invalidatepage, .releasepage = ext3_releasepage, .direct_IO = ext3_direct_IO, + .migrate_page = buffer_migrate_page, }; static struct address_space_operations ext3_journalled_aops = { Index: linux-2.6.14-rc5-mm1/fs/ext2/inode.c =================================================================== --- linux-2.6.14-rc5-mm1.orig/fs/ext2/inode.c 2005-10-31 14:11:03.000000000 -0800 +++ linux-2.6.14-rc5-mm1/fs/ext2/inode.c 2005-11-02 14:15:31.000000000 -0800 @@ -706,6 +706,7 @@ struct address_space_operations ext2_aop .bmap = ext2_bmap, .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, + .migrate_page = 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, + .migrate_page = buffer_migrate_page, }; /* Index: linux-2.6.14-rc5-mm1/fs/xfs/linux-2.6/xfs_buf.c =================================================================== --- linux-2.6.14-rc5-mm1.orig/fs/xfs/linux-2.6/xfs_buf.c 2005-10-31 14:10:52.000000000 -0800 +++ linux-2.6.14-rc5-mm1/fs/xfs/linux-2.6/xfs_buf.c 2005-11-02 14:15:31.000000000 -0800 @@ -1633,6 +1633,7 @@ xfs_mapping_buftarg( struct address_space *mapping; static struct address_space_operations mapping_aops = { .sync_page = block_sync_page, + .migrate_page = fail_migrate_page, }; inode = new_inode(bdev->bd_inode->i_sb); Index: linux-2.6.14-rc5-mm1/include/linux/buffer_head.h =================================================================== --- linux-2.6.14-rc5-mm1.orig/include/linux/buffer_head.h 2005-10-31 14:10:59.000000000 -0800 +++ linux-2.6.14-rc5-mm1/include/linux/buffer_head.h 2005-11-02 14:15:31.000000000 -0800 @@ -210,6 +210,7 @@ int nobh_truncate_page(struct address_sp int nobh_writepage(struct page *page, get_block_t *get_block, struct writeback_control *wbc); +int buffer_migrate_page(struct page *, struct page *); /* * inline definitions Index: linux-2.6.14-rc5-mm1/mm/vmscan.c =================================================================== --- linux-2.6.14-rc5-mm1.orig/mm/vmscan.c 2005-11-02 14:11:17.000000000 -0800 +++ linux-2.6.14-rc5-mm1/mm/vmscan.c 2005-11-02 14:15:31.000000000 -0800 @@ -725,6 +725,14 @@ int migrate_page_remove_references(struc } /* + * Non migratable page + */ +int fail_migrate_page(struct page *newpage, struct page *page) +{ + return -EIO; +} + +/* * Copy the page to its new location */ void migrate_page_copy(struct page *newpage, struct page *page) @@ -886,6 +894,11 @@ redo: */ mapping = page_mapping(page); + if (mapping->a_ops->migrate_page) { + rc = mapping->a_ops->migrate_page(newpage, page); + goto unlock_both; + } + /* * If the page is dirty then try_to_release_page * will fail. swap_page() will then trigger writeout. Index: linux-2.6.14-rc5-mm1/include/linux/swap.h =================================================================== --- linux-2.6.14-rc5-mm1.orig/include/linux/swap.h 2005-11-02 14:11:17.000000000 -0800 +++ linux-2.6.14-rc5-mm1/include/linux/swap.h 2005-11-02 14:15:31.000000000 -0800 @@ -185,6 +185,7 @@ extern int migrate_pages(struct list_hea extern int migrate_page(struct page *, struct page *); extern int migrate_page_remove_references(struct page *, struct page *, int); extern void migrate_page_copy(struct page *, struct page *); +extern int fail_migrate_page(struct page *, struct page *); #endif #ifdef CONFIG_MMU