aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Lameter <clameter@sgi.com>2006-02-01 06:05:41 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-02-01 11:53:17 -0500
commite965f9630c651fa4249039fd4b80c9392d07a856 (patch)
tree1353dd536d0ee549c30e462086624c21788ee9d2
parent7e2ab150d1b3b286a4c864c60a549b2601777b63 (diff)
[PATCH] Direct Migration V9: Avoid writeback / page_migrate() method
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 <kravetz@us.ibm.com> Signed-off-by: Christoph Lameter <clameter@sgi.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/buffer.c60
-rw-r--r--fs/ext2/inode.c2
-rw-r--r--fs/ext3/inode.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.c1
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.c1
-rw-r--r--include/linux/fs.h8
-rw-r--r--include/linux/swap.h5
-rw-r--r--mm/rmap.c1
-rw-r--r--mm/swap_state.c1
-rw-r--r--mm/vmscan.c20
10 files changed, 100 insertions, 1 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 3dc712f29d2d..8bcbac87a28c 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -3050,6 +3050,66 @@ asmlinkage long sys_bdflush(int func, long data)
3050} 3050}
3051 3051
3052/* 3052/*
3053 * Migration function for pages with buffers. This function can only be used
3054 * if the underlying filesystem guarantees that no other references to "page"
3055 * exist.
3056 */
3057#ifdef CONFIG_MIGRATION
3058int buffer_migrate_page(struct page *newpage, struct page *page)
3059{
3060 struct address_space *mapping = page->mapping;
3061 struct buffer_head *bh, *head;
3062
3063 if (!mapping)
3064 return -EAGAIN;
3065
3066 if (!page_has_buffers(page))
3067 return migrate_page(newpage, page);
3068
3069 head = page_buffers(page);
3070
3071 if (migrate_page_remove_references(newpage, page, 3))
3072 return -EAGAIN;
3073
3074 bh = head;
3075 do {
3076 get_bh(bh);
3077 lock_buffer(bh);
3078 bh = bh->b_this_page;
3079
3080 } while (bh != head);
3081
3082 ClearPagePrivate(page);
3083 set_page_private(newpage, page_private(page));
3084 set_page_private(page, 0);
3085 put_page(page);
3086 get_page(newpage);
3087
3088 bh = head;
3089 do {
3090 set_bh_page(bh, newpage, bh_offset(bh));
3091 bh = bh->b_this_page;
3092
3093 } while (bh != head);
3094
3095 SetPagePrivate(newpage);
3096
3097 migrate_page_copy(newpage, page);
3098
3099 bh = head;
3100 do {
3101 unlock_buffer(bh);
3102 put_bh(bh);
3103 bh = bh->b_this_page;
3104
3105 } while (bh != head);
3106
3107 return 0;
3108}
3109EXPORT_SYMBOL(buffer_migrate_page);
3110#endif
3111
3112/*
3053 * Buffer-head allocation 3113 * Buffer-head allocation
3054 */ 3114 */
3055static kmem_cache_t *bh_cachep; 3115static kmem_cache_t *bh_cachep;
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index e7d3f0522d01..a717837f272e 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -706,6 +706,7 @@ struct address_space_operations ext2_aops = {
706 .bmap = ext2_bmap, 706 .bmap = ext2_bmap,
707 .direct_IO = ext2_direct_IO, 707 .direct_IO = ext2_direct_IO,
708 .writepages = ext2_writepages, 708 .writepages = ext2_writepages,
709 .migratepage = buffer_migrate_page,
709}; 710};
710 711
711struct address_space_operations ext2_aops_xip = { 712struct address_space_operations ext2_aops_xip = {
@@ -723,6 +724,7 @@ struct address_space_operations ext2_nobh_aops = {
723 .bmap = ext2_bmap, 724 .bmap = ext2_bmap,
724 .direct_IO = ext2_direct_IO, 725 .direct_IO = ext2_direct_IO,
725 .writepages = ext2_writepages, 726 .writepages = ext2_writepages,
727 .migratepage = buffer_migrate_page,
726}; 728};
727 729
728/* 730/*
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 8824e84f8a56..3fc4238e9703 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -1559,6 +1559,7 @@ static struct address_space_operations ext3_ordered_aops = {
1559 .invalidatepage = ext3_invalidatepage, 1559 .invalidatepage = ext3_invalidatepage,
1560 .releasepage = ext3_releasepage, 1560 .releasepage = ext3_releasepage,
1561 .direct_IO = ext3_direct_IO, 1561 .direct_IO = ext3_direct_IO,
1562 .migratepage = buffer_migrate_page,
1562}; 1563};
1563 1564
1564static struct address_space_operations ext3_writeback_aops = { 1565static struct address_space_operations ext3_writeback_aops = {
@@ -1572,6 +1573,7 @@ static struct address_space_operations ext3_writeback_aops = {
1572 .invalidatepage = ext3_invalidatepage, 1573 .invalidatepage = ext3_invalidatepage,
1573 .releasepage = ext3_releasepage, 1574 .releasepage = ext3_releasepage,
1574 .direct_IO = ext3_direct_IO, 1575 .direct_IO = ext3_direct_IO,
1576 .migratepage = buffer_migrate_page,
1575}; 1577};
1576 1578
1577static struct address_space_operations ext3_journalled_aops = { 1579static struct address_space_operations ext3_journalled_aops = {
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c
index 120626789406..9892268e3005 100644
--- a/fs/xfs/linux-2.6/xfs_aops.c
+++ b/fs/xfs/linux-2.6/xfs_aops.c
@@ -1462,4 +1462,5 @@ struct address_space_operations linvfs_aops = {
1462 .commit_write = generic_commit_write, 1462 .commit_write = generic_commit_write,
1463 .bmap = linvfs_bmap, 1463 .bmap = linvfs_bmap,
1464 .direct_IO = linvfs_direct_IO, 1464 .direct_IO = linvfs_direct_IO,
1465 .migratepage = buffer_migrate_page,
1465}; 1466};
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c
index a36a8e3b703f..bfb4f2917bb6 100644
--- a/fs/xfs/linux-2.6/xfs_buf.c
+++ b/fs/xfs/linux-2.6/xfs_buf.c
@@ -1521,6 +1521,7 @@ xfs_mapping_buftarg(
1521 struct address_space *mapping; 1521 struct address_space *mapping;
1522 static struct address_space_operations mapping_aops = { 1522 static struct address_space_operations mapping_aops = {
1523 .sync_page = block_sync_page, 1523 .sync_page = block_sync_page,
1524 .migratepage = fail_migrate_page,
1524 }; 1525 };
1525 1526
1526 inode = new_inode(bdev->bd_inode->i_sb); 1527 inode = new_inode(bdev->bd_inode->i_sb);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 84bb449b9b01..e059da947007 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -363,6 +363,8 @@ struct address_space_operations {
363 loff_t offset, unsigned long nr_segs); 363 loff_t offset, unsigned long nr_segs);
364 struct page* (*get_xip_page)(struct address_space *, sector_t, 364 struct page* (*get_xip_page)(struct address_space *, sector_t,
365 int); 365 int);
366 /* migrate the contents of a page to the specified target */
367 int (*migratepage) (struct page *, struct page *);
366}; 368};
367 369
368struct backing_dev_info; 370struct backing_dev_info;
@@ -1719,6 +1721,12 @@ extern void simple_release_fs(struct vfsmount **mount, int *count);
1719 1721
1720extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t); 1722extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t);
1721 1723
1724#ifdef CONFIG_MIGRATION
1725extern int buffer_migrate_page(struct page *, struct page *);
1726#else
1727#define buffer_migrate_page NULL
1728#endif
1729
1722extern int inode_change_ok(struct inode *, struct iattr *); 1730extern int inode_change_ok(struct inode *, struct iattr *);
1723extern int __must_check inode_setattr(struct inode *, struct iattr *); 1731extern int __must_check inode_setattr(struct inode *, struct iattr *);
1724 1732
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 229b6d04b4b6..f3e17d5963c3 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -193,13 +193,18 @@ extern int isolate_lru_page(struct page *p);
193extern int putback_lru_pages(struct list_head *l); 193extern int putback_lru_pages(struct list_head *l);
194extern int migrate_page(struct page *, struct page *); 194extern int migrate_page(struct page *, struct page *);
195extern void migrate_page_copy(struct page *, struct page *); 195extern void migrate_page_copy(struct page *, struct page *);
196extern int migrate_page_remove_references(struct page *, struct page *, int);
196extern int migrate_pages(struct list_head *l, struct list_head *t, 197extern int migrate_pages(struct list_head *l, struct list_head *t,
197 struct list_head *moved, struct list_head *failed); 198 struct list_head *moved, struct list_head *failed);
199extern int fail_migrate_page(struct page *, struct page *);
198#else 200#else
199static inline int isolate_lru_page(struct page *p) { return -ENOSYS; } 201static inline int isolate_lru_page(struct page *p) { return -ENOSYS; }
200static inline int putback_lru_pages(struct list_head *l) { return 0; } 202static inline int putback_lru_pages(struct list_head *l) { return 0; }
201static inline int migrate_pages(struct list_head *l, struct list_head *t, 203static inline int migrate_pages(struct list_head *l, struct list_head *t,
202 struct list_head *moved, struct list_head *failed) { return -ENOSYS; } 204 struct list_head *moved, struct list_head *failed) { return -ENOSYS; }
205/* Possible settings for the migrate_page() method in address_operations */
206#define migrate_page NULL
207#define fail_migrate_page NULL
203#endif 208#endif
204 209
205#ifdef CONFIG_MMU 210#ifdef CONFIG_MMU
diff --git a/mm/rmap.c b/mm/rmap.c
index f4b91d7aa5cf..df2c41c2a9a2 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -233,6 +233,7 @@ void remove_from_swap(struct page *page)
233 233
234 delete_from_swap_cache(page); 234 delete_from_swap_cache(page);
235} 235}
236EXPORT_SYMBOL(remove_from_swap);
236#endif 237#endif
237 238
238/* 239/*
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 7b09ac503fec..db8a3d3e1636 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -27,6 +27,7 @@ static struct address_space_operations swap_aops = {
27 .writepage = swap_writepage, 27 .writepage = swap_writepage,
28 .sync_page = block_sync_page, 28 .sync_page = block_sync_page,
29 .set_page_dirty = __set_page_dirty_nobuffers, 29 .set_page_dirty = __set_page_dirty_nobuffers,
30 .migratepage = migrate_page,
30}; 31};
31 32
32static struct backing_dev_info swap_backing_dev_info = { 33static struct backing_dev_info swap_backing_dev_info = {
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 5e98b86feb74..5a610804cd06 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -615,6 +615,15 @@ int putback_lru_pages(struct list_head *l)
615} 615}
616 616
617/* 617/*
618 * Non migratable page
619 */
620int fail_migrate_page(struct page *newpage, struct page *page)
621{
622 return -EIO;
623}
624EXPORT_SYMBOL(fail_migrate_page);
625
626/*
618 * swapout a single page 627 * swapout a single page
619 * page is locked upon entry, unlocked on exit 628 * page is locked upon entry, unlocked on exit
620 */ 629 */
@@ -659,6 +668,7 @@ unlock_retry:
659retry: 668retry:
660 return -EAGAIN; 669 return -EAGAIN;
661} 670}
671EXPORT_SYMBOL(swap_page);
662 672
663/* 673/*
664 * Page migration was first developed in the context of the memory hotplug 674 * Page migration was first developed in the context of the memory hotplug
@@ -674,7 +684,7 @@ retry:
674 * Remove references for a page and establish the new page with the correct 684 * Remove references for a page and establish the new page with the correct
675 * basic settings to be able to stop accesses to the page. 685 * basic settings to be able to stop accesses to the page.
676 */ 686 */
677static int migrate_page_remove_references(struct page *newpage, 687int migrate_page_remove_references(struct page *newpage,
678 struct page *page, int nr_refs) 688 struct page *page, int nr_refs)
679{ 689{
680 struct address_space *mapping = page_mapping(page); 690 struct address_space *mapping = page_mapping(page);
@@ -749,6 +759,7 @@ static int migrate_page_remove_references(struct page *newpage,
749 759
750 return 0; 760 return 0;
751} 761}
762EXPORT_SYMBOL(migrate_page_remove_references);
752 763
753/* 764/*
754 * Copy the page to its new location 765 * Copy the page to its new location
@@ -788,6 +799,7 @@ void migrate_page_copy(struct page *newpage, struct page *page)
788 if (PageWriteback(newpage)) 799 if (PageWriteback(newpage))
789 end_page_writeback(newpage); 800 end_page_writeback(newpage);
790} 801}
802EXPORT_SYMBOL(migrate_page_copy);
791 803
792/* 804/*
793 * Common logic to directly migrate a single page suitable for 805 * Common logic to directly migrate a single page suitable for
@@ -815,6 +827,7 @@ int migrate_page(struct page *newpage, struct page *page)
815 remove_from_swap(newpage); 827 remove_from_swap(newpage);
816 return 0; 828 return 0;
817} 829}
830EXPORT_SYMBOL(migrate_page);
818 831
819/* 832/*
820 * migrate_pages 833 * migrate_pages
@@ -914,6 +927,11 @@ redo:
914 if (!mapping) 927 if (!mapping)
915 goto unlock_both; 928 goto unlock_both;
916 929
930 if (mapping->a_ops->migratepage) {
931 rc = mapping->a_ops->migratepage(newpage, page);
932 goto unlock_both;
933 }
934
917 /* 935 /*
918 * Trigger writeout if page is dirty 936 * Trigger writeout if page is dirty
919 */ 937 */