diff options
-rw-r--r-- | Documentation/filesystems/Locking | 13 | ||||
-rw-r--r-- | Documentation/filesystems/vfs.txt | 12 | ||||
-rw-r--r-- | include/linux/fs.h | 4 | ||||
-rw-r--r-- | include/linux/swap.h | 2 | ||||
-rw-r--r-- | mm/page_io.c | 52 | ||||
-rw-r--r-- | mm/swap_state.c | 2 | ||||
-rw-r--r-- | mm/swapfile.c | 23 |
7 files changed, 105 insertions, 3 deletions
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index e0cce2a5f820..2db1900d7538 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking | |||
@@ -206,6 +206,8 @@ prototypes: | |||
206 | int (*launder_page)(struct page *); | 206 | int (*launder_page)(struct page *); |
207 | int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long); | 207 | int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long); |
208 | int (*error_remove_page)(struct address_space *, struct page *); | 208 | int (*error_remove_page)(struct address_space *, struct page *); |
209 | int (*swap_activate)(struct file *); | ||
210 | int (*swap_deactivate)(struct file *); | ||
209 | 211 | ||
210 | locking rules: | 212 | locking rules: |
211 | All except set_page_dirty and freepage may block | 213 | All except set_page_dirty and freepage may block |
@@ -229,6 +231,8 @@ migratepage: yes (both) | |||
229 | launder_page: yes | 231 | launder_page: yes |
230 | is_partially_uptodate: yes | 232 | is_partially_uptodate: yes |
231 | error_remove_page: yes | 233 | error_remove_page: yes |
234 | swap_activate: no | ||
235 | swap_deactivate: no | ||
232 | 236 | ||
233 | ->write_begin(), ->write_end(), ->sync_page() and ->readpage() | 237 | ->write_begin(), ->write_end(), ->sync_page() and ->readpage() |
234 | may be called from the request handler (/dev/loop). | 238 | may be called from the request handler (/dev/loop). |
@@ -330,6 +334,15 @@ cleaned, or an error value if not. Note that in order to prevent the page | |||
330 | getting mapped back in and redirtied, it needs to be kept locked | 334 | getting mapped back in and redirtied, it needs to be kept locked |
331 | across the entire operation. | 335 | across the entire operation. |
332 | 336 | ||
337 | ->swap_activate will be called with a non-zero argument on | ||
338 | files backing (non block device backed) swapfiles. A return value | ||
339 | of zero indicates success, in which case this file can be used for | ||
340 | backing swapspace. The swapspace operations will be proxied to the | ||
341 | address space operations. | ||
342 | |||
343 | ->swap_deactivate() will be called in the sys_swapoff() | ||
344 | path after ->swap_activate() returned success. | ||
345 | |||
333 | ----------------------- file_lock_operations ------------------------------ | 346 | ----------------------- file_lock_operations ------------------------------ |
334 | prototypes: | 347 | prototypes: |
335 | void (*fl_copy_lock)(struct file_lock *, struct file_lock *); | 348 | void (*fl_copy_lock)(struct file_lock *, struct file_lock *); |
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index aa754e01464e..065aa2dc0835 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt | |||
@@ -592,6 +592,8 @@ struct address_space_operations { | |||
592 | int (*migratepage) (struct page *, struct page *); | 592 | int (*migratepage) (struct page *, struct page *); |
593 | int (*launder_page) (struct page *); | 593 | int (*launder_page) (struct page *); |
594 | int (*error_remove_page) (struct mapping *mapping, struct page *page); | 594 | int (*error_remove_page) (struct mapping *mapping, struct page *page); |
595 | int (*swap_activate)(struct file *); | ||
596 | int (*swap_deactivate)(struct file *); | ||
595 | }; | 597 | }; |
596 | 598 | ||
597 | writepage: called by the VM to write a dirty page to backing store. | 599 | writepage: called by the VM to write a dirty page to backing store. |
@@ -760,6 +762,16 @@ struct address_space_operations { | |||
760 | Setting this implies you deal with pages going away under you, | 762 | Setting this implies you deal with pages going away under you, |
761 | unless you have them locked or reference counts increased. | 763 | unless you have them locked or reference counts increased. |
762 | 764 | ||
765 | swap_activate: Called when swapon is used on a file to allocate | ||
766 | space if necessary and pin the block lookup information in | ||
767 | memory. A return value of zero indicates success, | ||
768 | in which case this file can be used to back swapspace. The | ||
769 | swapspace operations will be proxied to this address space's | ||
770 | ->swap_{out,in} methods. | ||
771 | |||
772 | swap_deactivate: Called during swapoff on files where swap_activate | ||
773 | was successful. | ||
774 | |||
763 | 775 | ||
764 | The File Object | 776 | The File Object |
765 | =============== | 777 | =============== |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 9d77309da153..38356ab827c9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -638,6 +638,10 @@ struct address_space_operations { | |||
638 | int (*is_partially_uptodate) (struct page *, read_descriptor_t *, | 638 | int (*is_partially_uptodate) (struct page *, read_descriptor_t *, |
639 | unsigned long); | 639 | unsigned long); |
640 | int (*error_remove_page)(struct address_space *, struct page *); | 640 | int (*error_remove_page)(struct address_space *, struct page *); |
641 | |||
642 | /* swapfile support */ | ||
643 | int (*swap_activate)(struct file *file); | ||
644 | int (*swap_deactivate)(struct file *file); | ||
641 | }; | 645 | }; |
642 | 646 | ||
643 | extern const struct address_space_operations empty_aops; | 647 | extern const struct address_space_operations empty_aops; |
diff --git a/include/linux/swap.h b/include/linux/swap.h index e62425ded2ed..ab230b1ebf61 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
@@ -151,6 +151,7 @@ enum { | |||
151 | SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ | 151 | SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ |
152 | SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */ | 152 | SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */ |
153 | SWP_BLKDEV = (1 << 6), /* its a block device */ | 153 | SWP_BLKDEV = (1 << 6), /* its a block device */ |
154 | SWP_FILE = (1 << 7), /* set after swap_activate success */ | ||
154 | /* add others here before... */ | 155 | /* add others here before... */ |
155 | SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ | 156 | SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ |
156 | }; | 157 | }; |
@@ -320,6 +321,7 @@ static inline void mem_cgroup_uncharge_swap(swp_entry_t ent) | |||
320 | /* linux/mm/page_io.c */ | 321 | /* linux/mm/page_io.c */ |
321 | extern int swap_readpage(struct page *); | 322 | extern int swap_readpage(struct page *); |
322 | extern int swap_writepage(struct page *page, struct writeback_control *wbc); | 323 | extern int swap_writepage(struct page *page, struct writeback_control *wbc); |
324 | extern int swap_set_page_dirty(struct page *page); | ||
323 | extern void end_swap_bio_read(struct bio *bio, int err); | 325 | extern void end_swap_bio_read(struct bio *bio, int err); |
324 | 326 | ||
325 | /* linux/mm/swap_state.c */ | 327 | /* linux/mm/swap_state.c */ |
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) | |||
145 | out: | 185 | out: |
146 | return ret; | 186 | return ret; |
147 | } | 187 | } |
188 | |||
189 | int 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 | } | ||
diff --git a/mm/swap_state.c b/mm/swap_state.c index c85b5590cccd..0cb36fb1f61c 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c | |||
@@ -27,7 +27,7 @@ | |||
27 | */ | 27 | */ |
28 | static const struct address_space_operations swap_aops = { | 28 | static const struct address_space_operations swap_aops = { |
29 | .writepage = swap_writepage, | 29 | .writepage = swap_writepage, |
30 | .set_page_dirty = __set_page_dirty_no_writeback, | 30 | .set_page_dirty = swap_set_page_dirty, |
31 | .migratepage = migrate_page, | 31 | .migratepage = migrate_page, |
32 | }; | 32 | }; |
33 | 33 | ||
diff --git a/mm/swapfile.c b/mm/swapfile.c index f89af5ba2eb2..6ffc87602f4a 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c | |||
@@ -1329,6 +1329,14 @@ static void destroy_swap_extents(struct swap_info_struct *sis) | |||
1329 | list_del(&se->list); | 1329 | list_del(&se->list); |
1330 | kfree(se); | 1330 | kfree(se); |
1331 | } | 1331 | } |
1332 | |||
1333 | if (sis->flags & SWP_FILE) { | ||
1334 | struct file *swap_file = sis->swap_file; | ||
1335 | struct address_space *mapping = swap_file->f_mapping; | ||
1336 | |||
1337 | sis->flags &= ~SWP_FILE; | ||
1338 | mapping->a_ops->swap_deactivate(swap_file); | ||
1339 | } | ||
1332 | } | 1340 | } |
1333 | 1341 | ||
1334 | /* | 1342 | /* |
@@ -1410,7 +1418,9 @@ add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, | |||
1410 | */ | 1418 | */ |
1411 | static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) | 1419 | static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) |
1412 | { | 1420 | { |
1413 | struct inode *inode; | 1421 | struct file *swap_file = sis->swap_file; |
1422 | struct address_space *mapping = swap_file->f_mapping; | ||
1423 | struct inode *inode = mapping->host; | ||
1414 | unsigned blocks_per_page; | 1424 | unsigned blocks_per_page; |
1415 | unsigned long page_no; | 1425 | unsigned long page_no; |
1416 | unsigned blkbits; | 1426 | unsigned blkbits; |
@@ -1421,13 +1431,22 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) | |||
1421 | int nr_extents = 0; | 1431 | int nr_extents = 0; |
1422 | int ret; | 1432 | int ret; |
1423 | 1433 | ||
1424 | inode = sis->swap_file->f_mapping->host; | ||
1425 | if (S_ISBLK(inode->i_mode)) { | 1434 | if (S_ISBLK(inode->i_mode)) { |
1426 | ret = add_swap_extent(sis, 0, sis->max, 0); | 1435 | ret = add_swap_extent(sis, 0, sis->max, 0); |
1427 | *span = sis->pages; | 1436 | *span = sis->pages; |
1428 | goto out; | 1437 | goto out; |
1429 | } | 1438 | } |
1430 | 1439 | ||
1440 | if (mapping->a_ops->swap_activate) { | ||
1441 | ret = mapping->a_ops->swap_activate(swap_file); | ||
1442 | if (!ret) { | ||
1443 | sis->flags |= SWP_FILE; | ||
1444 | ret = add_swap_extent(sis, 0, sis->max, 0); | ||
1445 | *span = sis->pages; | ||
1446 | } | ||
1447 | goto out; | ||
1448 | } | ||
1449 | |||
1431 | blkbits = inode->i_blkbits; | 1450 | blkbits = inode->i_blkbits; |
1432 | blocks_per_page = PAGE_SIZE >> blkbits; | 1451 | blocks_per_page = PAGE_SIZE >> blkbits; |
1433 | 1452 | ||