aboutsummaryrefslogtreecommitdiffstats
path: root/mm/page_io.c
diff options
context:
space:
mode:
authorMel Gorman <mgorman@suse.de>2012-07-31 19:44:55 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-31 21:42:47 -0400
commit62c230bc1790923a1b35da03596a68a6c9b5b100 (patch)
tree46b300a00929087627e029b0f4d53e486a97ba7f /mm/page_io.c
parent18022c5d8627a7a9ba8097a0f238b513fae6f5b8 (diff)
mm: add support for a filesystem to activate swap files and use direct_IO for writing swap pages
Currently swapfiles are managed entirely by the core VM by using ->bmap to allocate space and write to the blocks directly. This effectively ensures that the underlying blocks are allocated and avoids the need for the swap subsystem to locate what physical blocks store offsets within a file. If the swap subsystem is to use the filesystem information to locate the blocks, it is critical that information such as block groups, block bitmaps and the block descriptor table that map the swap file were resident in memory. This patch adds address_space_operations that the VM can call when activating or deactivating swap backed by a file. int swap_activate(struct file *); int swap_deactivate(struct file *); The ->swap_activate() method is used to communicate to the file that the VM relies on it, and the address_space should take adequate measures such as reserving space in the underlying device, reserving memory for mempools and pinning information such as the block descriptor table in memory. The ->swap_deactivate() method is called on sys_swapoff() if ->swap_activate() returned success. After a successful swapfile ->swap_activate, the swapfile is marked SWP_FILE and swapper_space.a_ops will proxy to sis->swap_file->f_mappings->a_ops using ->direct_io to write swapcache pages and ->readpage to read. It is perfectly possible that direct_IO be used to read the swap pages but it is an unnecessary complication. Similarly, it is possible that ->writepage be used instead of direct_io to write the pages but filesystem developers have stated that calling writepage from the VM is undesirable for a variety of reasons and using direct_IO opens up the possibility of writing back batches of swap pages in the future. [a.p.zijlstra@chello.nl: Original patch] Signed-off-by: Mel Gorman <mgorman@suse.de> Acked-by: Rik van Riel <riel@redhat.com> Cc: Christoph Hellwig <hch@infradead.org> Cc: David S. Miller <davem@davemloft.net> Cc: Eric B Munson <emunson@mgebm.net> Cc: Eric Paris <eparis@redhat.com> Cc: James Morris <jmorris@namei.org> Cc: Mel Gorman <mgorman@suse.de> Cc: Mike Christie <michaelc@cs.wisc.edu> Cc: Neil Brown <neilb@suse.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> Cc: Trond Myklebust <Trond.Myklebust@netapp.com> Cc: Xiaotian Feng <dfeng@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/page_io.c')
-rw-r--r--mm/page_io.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/mm/page_io.c b/mm/page_io.c
index 34f02923744c..307a3e795290 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -17,6 +17,7 @@
17#include <linux/swap.h> 17#include <linux/swap.h>
18#include <linux/bio.h> 18#include <linux/bio.h>
19#include <linux/swapops.h> 19#include <linux/swapops.h>
20#include <linux/buffer_head.h>
20#include <linux/writeback.h> 21#include <linux/writeback.h>
21#include <linux/frontswap.h> 22#include <linux/frontswap.h>
22#include <asm/pgtable.h> 23#include <asm/pgtable.h>
@@ -94,6 +95,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
94{ 95{
95 struct bio *bio; 96 struct bio *bio;
96 int ret = 0, rw = WRITE; 97 int ret = 0, rw = WRITE;
98 struct swap_info_struct *sis = page_swap_info(page);
97 99
98 if (try_to_free_swap(page)) { 100 if (try_to_free_swap(page)) {
99 unlock_page(page); 101 unlock_page(page);
@@ -105,6 +107,32 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
105 end_page_writeback(page); 107 end_page_writeback(page);
106 goto out; 108 goto out;
107 } 109 }
110
111 if (sis->flags & SWP_FILE) {
112 struct kiocb kiocb;
113 struct file *swap_file = sis->swap_file;
114 struct address_space *mapping = swap_file->f_mapping;
115 struct iovec iov = {
116 .iov_base = page_address(page),
117 .iov_len = PAGE_SIZE,
118 };
119
120 init_sync_kiocb(&kiocb, swap_file);
121 kiocb.ki_pos = page_file_offset(page);
122 kiocb.ki_left = PAGE_SIZE;
123 kiocb.ki_nbytes = PAGE_SIZE;
124
125 unlock_page(page);
126 ret = mapping->a_ops->direct_IO(KERNEL_WRITE,
127 &kiocb, &iov,
128 kiocb.ki_pos, 1);
129 if (ret == PAGE_SIZE) {
130 count_vm_event(PSWPOUT);
131 ret = 0;
132 }
133 return ret;
134 }
135
108 bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write); 136 bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write);
109 if (bio == NULL) { 137 if (bio == NULL) {
110 set_page_dirty(page); 138 set_page_dirty(page);
@@ -126,6 +154,7 @@ int swap_readpage(struct page *page)
126{ 154{
127 struct bio *bio; 155 struct bio *bio;
128 int ret = 0; 156 int ret = 0;
157 struct swap_info_struct *sis = page_swap_info(page);
129 158
130 VM_BUG_ON(!PageLocked(page)); 159 VM_BUG_ON(!PageLocked(page));
131 VM_BUG_ON(PageUptodate(page)); 160 VM_BUG_ON(PageUptodate(page));
@@ -134,6 +163,17 @@ int swap_readpage(struct page *page)
134 unlock_page(page); 163 unlock_page(page);
135 goto out; 164 goto out;
136 } 165 }
166
167 if (sis->flags & SWP_FILE) {
168 struct file *swap_file = sis->swap_file;
169 struct address_space *mapping = swap_file->f_mapping;
170
171 ret = mapping->a_ops->readpage(swap_file, page);
172 if (!ret)
173 count_vm_event(PSWPIN);
174 return ret;
175 }
176
137 bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read); 177 bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read);
138 if (bio == NULL) { 178 if (bio == NULL) {
139 unlock_page(page); 179 unlock_page(page);
@@ -145,3 +185,15 @@ int swap_readpage(struct page *page)
145out: 185out:
146 return ret; 186 return ret;
147} 187}
188
189int swap_set_page_dirty(struct page *page)
190{
191 struct swap_info_struct *sis = page_swap_info(page);
192
193 if (sis->flags & SWP_FILE) {
194 struct address_space *mapping = sis->swap_file->f_mapping;
195 return mapping->a_ops->set_page_dirty(page);
196 } else {
197 return __set_page_dirty_no_writeback(page);
198 }
199}