diff options
Diffstat (limited to 'mm/page_io.c')
-rw-r--r-- | mm/page_io.c | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/mm/page_io.c b/mm/page_io.c index 34f02923744c..78eee32ee486 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> |
@@ -86,6 +87,98 @@ void end_swap_bio_read(struct bio *bio, int err) | |||
86 | bio_put(bio); | 87 | bio_put(bio); |
87 | } | 88 | } |
88 | 89 | ||
90 | int generic_swapfile_activate(struct swap_info_struct *sis, | ||
91 | struct file *swap_file, | ||
92 | sector_t *span) | ||
93 | { | ||
94 | struct address_space *mapping = swap_file->f_mapping; | ||
95 | struct inode *inode = mapping->host; | ||
96 | unsigned blocks_per_page; | ||
97 | unsigned long page_no; | ||
98 | unsigned blkbits; | ||
99 | sector_t probe_block; | ||
100 | sector_t last_block; | ||
101 | sector_t lowest_block = -1; | ||
102 | sector_t highest_block = 0; | ||
103 | int nr_extents = 0; | ||
104 | int ret; | ||
105 | |||
106 | blkbits = inode->i_blkbits; | ||
107 | blocks_per_page = PAGE_SIZE >> blkbits; | ||
108 | |||
109 | /* | ||
110 | * Map all the blocks into the extent list. This code doesn't try | ||
111 | * to be very smart. | ||
112 | */ | ||
113 | probe_block = 0; | ||
114 | page_no = 0; | ||
115 | last_block = i_size_read(inode) >> blkbits; | ||
116 | while ((probe_block + blocks_per_page) <= last_block && | ||
117 | page_no < sis->max) { | ||
118 | unsigned block_in_page; | ||
119 | sector_t first_block; | ||
120 | |||
121 | first_block = bmap(inode, probe_block); | ||
122 | if (first_block == 0) | ||
123 | goto bad_bmap; | ||
124 | |||
125 | /* | ||
126 | * It must be PAGE_SIZE aligned on-disk | ||
127 | */ | ||
128 | if (first_block & (blocks_per_page - 1)) { | ||
129 | probe_block++; | ||
130 | goto reprobe; | ||
131 | } | ||
132 | |||
133 | for (block_in_page = 1; block_in_page < blocks_per_page; | ||
134 | block_in_page++) { | ||
135 | sector_t block; | ||
136 | |||
137 | block = bmap(inode, probe_block + block_in_page); | ||
138 | if (block == 0) | ||
139 | goto bad_bmap; | ||
140 | if (block != first_block + block_in_page) { | ||
141 | /* Discontiguity */ | ||
142 | probe_block++; | ||
143 | goto reprobe; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | first_block >>= (PAGE_SHIFT - blkbits); | ||
148 | if (page_no) { /* exclude the header page */ | ||
149 | if (first_block < lowest_block) | ||
150 | lowest_block = first_block; | ||
151 | if (first_block > highest_block) | ||
152 | highest_block = first_block; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks | ||
157 | */ | ||
158 | ret = add_swap_extent(sis, page_no, 1, first_block); | ||
159 | if (ret < 0) | ||
160 | goto out; | ||
161 | nr_extents += ret; | ||
162 | page_no++; | ||
163 | probe_block += blocks_per_page; | ||
164 | reprobe: | ||
165 | continue; | ||
166 | } | ||
167 | ret = nr_extents; | ||
168 | *span = 1 + highest_block - lowest_block; | ||
169 | if (page_no == 0) | ||
170 | page_no = 1; /* force Empty message */ | ||
171 | sis->max = page_no; | ||
172 | sis->pages = page_no - 1; | ||
173 | sis->highest_bit = page_no - 1; | ||
174 | out: | ||
175 | return ret; | ||
176 | bad_bmap: | ||
177 | printk(KERN_ERR "swapon: swapfile has holes\n"); | ||
178 | ret = -EINVAL; | ||
179 | goto out; | ||
180 | } | ||
181 | |||
89 | /* | 182 | /* |
90 | * We may have stale swap cache pages in memory: notice | 183 | * We may have stale swap cache pages in memory: notice |
91 | * them here and get rid of the unnecessary final write. | 184 | * them here and get rid of the unnecessary final write. |
@@ -94,6 +187,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) | |||
94 | { | 187 | { |
95 | struct bio *bio; | 188 | struct bio *bio; |
96 | int ret = 0, rw = WRITE; | 189 | int ret = 0, rw = WRITE; |
190 | struct swap_info_struct *sis = page_swap_info(page); | ||
97 | 191 | ||
98 | if (try_to_free_swap(page)) { | 192 | if (try_to_free_swap(page)) { |
99 | unlock_page(page); | 193 | unlock_page(page); |
@@ -105,6 +199,33 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) | |||
105 | end_page_writeback(page); | 199 | end_page_writeback(page); |
106 | goto out; | 200 | goto out; |
107 | } | 201 | } |
202 | |||
203 | if (sis->flags & SWP_FILE) { | ||
204 | struct kiocb kiocb; | ||
205 | struct file *swap_file = sis->swap_file; | ||
206 | struct address_space *mapping = swap_file->f_mapping; | ||
207 | struct iovec iov = { | ||
208 | .iov_base = kmap(page), | ||
209 | .iov_len = PAGE_SIZE, | ||
210 | }; | ||
211 | |||
212 | init_sync_kiocb(&kiocb, swap_file); | ||
213 | kiocb.ki_pos = page_file_offset(page); | ||
214 | kiocb.ki_left = PAGE_SIZE; | ||
215 | kiocb.ki_nbytes = PAGE_SIZE; | ||
216 | |||
217 | unlock_page(page); | ||
218 | ret = mapping->a_ops->direct_IO(KERNEL_WRITE, | ||
219 | &kiocb, &iov, | ||
220 | kiocb.ki_pos, 1); | ||
221 | kunmap(page); | ||
222 | if (ret == PAGE_SIZE) { | ||
223 | count_vm_event(PSWPOUT); | ||
224 | ret = 0; | ||
225 | } | ||
226 | return ret; | ||
227 | } | ||
228 | |||
108 | bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write); | 229 | bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write); |
109 | if (bio == NULL) { | 230 | if (bio == NULL) { |
110 | set_page_dirty(page); | 231 | set_page_dirty(page); |
@@ -126,6 +247,7 @@ int swap_readpage(struct page *page) | |||
126 | { | 247 | { |
127 | struct bio *bio; | 248 | struct bio *bio; |
128 | int ret = 0; | 249 | int ret = 0; |
250 | struct swap_info_struct *sis = page_swap_info(page); | ||
129 | 251 | ||
130 | VM_BUG_ON(!PageLocked(page)); | 252 | VM_BUG_ON(!PageLocked(page)); |
131 | VM_BUG_ON(PageUptodate(page)); | 253 | VM_BUG_ON(PageUptodate(page)); |
@@ -134,6 +256,17 @@ int swap_readpage(struct page *page) | |||
134 | unlock_page(page); | 256 | unlock_page(page); |
135 | goto out; | 257 | goto out; |
136 | } | 258 | } |
259 | |||
260 | if (sis->flags & SWP_FILE) { | ||
261 | struct file *swap_file = sis->swap_file; | ||
262 | struct address_space *mapping = swap_file->f_mapping; | ||
263 | |||
264 | ret = mapping->a_ops->readpage(swap_file, page); | ||
265 | if (!ret) | ||
266 | count_vm_event(PSWPIN); | ||
267 | return ret; | ||
268 | } | ||
269 | |||
137 | bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read); | 270 | bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read); |
138 | if (bio == NULL) { | 271 | if (bio == NULL) { |
139 | unlock_page(page); | 272 | unlock_page(page); |
@@ -145,3 +278,15 @@ int swap_readpage(struct page *page) | |||
145 | out: | 278 | out: |
146 | return ret; | 279 | return ret; |
147 | } | 280 | } |
281 | |||
282 | int swap_set_page_dirty(struct page *page) | ||
283 | { | ||
284 | struct swap_info_struct *sis = page_swap_info(page); | ||
285 | |||
286 | if (sis->flags & SWP_FILE) { | ||
287 | struct address_space *mapping = sis->swap_file->f_mapping; | ||
288 | return mapping->a_ops->set_page_dirty(page); | ||
289 | } else { | ||
290 | return __set_page_dirty_no_writeback(page); | ||
291 | } | ||
292 | } | ||