diff options
-rw-r--r-- | include/linux/fs.h | 6 | ||||
-rw-r--r-- | include/linux/swap.h | 5 | ||||
-rw-r--r-- | mm/page_io.c | 92 | ||||
-rw-r--r-- | mm/swapfile.c | 91 |
4 files changed, 106 insertions, 88 deletions
diff --git a/include/linux/fs.h b/include/linux/fs.h index 38356ab827c9..c8667f8b5358 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -429,6 +429,7 @@ struct kstatfs; | |||
429 | struct vm_area_struct; | 429 | struct vm_area_struct; |
430 | struct vfsmount; | 430 | struct vfsmount; |
431 | struct cred; | 431 | struct cred; |
432 | struct swap_info_struct; | ||
432 | 433 | ||
433 | extern void __init inode_init(void); | 434 | extern void __init inode_init(void); |
434 | extern void __init inode_init_early(void); | 435 | extern void __init inode_init_early(void); |
@@ -640,8 +641,9 @@ struct address_space_operations { | |||
640 | int (*error_remove_page)(struct address_space *, struct page *); | 641 | int (*error_remove_page)(struct address_space *, struct page *); |
641 | 642 | ||
642 | /* swapfile support */ | 643 | /* swapfile support */ |
643 | int (*swap_activate)(struct file *file); | 644 | int (*swap_activate)(struct swap_info_struct *sis, struct file *file, |
644 | int (*swap_deactivate)(struct file *file); | 645 | sector_t *span); |
646 | void (*swap_deactivate)(struct file *file); | ||
645 | }; | 647 | }; |
646 | 648 | ||
647 | extern const struct address_space_operations empty_aops; | 649 | extern const struct address_space_operations empty_aops; |
diff --git a/include/linux/swap.h b/include/linux/swap.h index ab230b1ebf61..388e70601413 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
@@ -324,6 +324,11 @@ extern int swap_writepage(struct page *page, struct writeback_control *wbc); | |||
324 | extern int swap_set_page_dirty(struct page *page); | 324 | extern int swap_set_page_dirty(struct page *page); |
325 | extern void end_swap_bio_read(struct bio *bio, int err); | 325 | extern void end_swap_bio_read(struct bio *bio, int err); |
326 | 326 | ||
327 | int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, | ||
328 | unsigned long nr_pages, sector_t start_block); | ||
329 | int generic_swapfile_activate(struct swap_info_struct *, struct file *, | ||
330 | sector_t *); | ||
331 | |||
327 | /* linux/mm/swap_state.c */ | 332 | /* linux/mm/swap_state.c */ |
328 | extern struct address_space swapper_space; | 333 | extern struct address_space swapper_space; |
329 | #define total_swapcache_pages swapper_space.nrpages | 334 | #define total_swapcache_pages swapper_space.nrpages |
diff --git a/mm/page_io.c b/mm/page_io.c index 307a3e795290..4a379629e31f 100644 --- a/mm/page_io.c +++ b/mm/page_io.c | |||
@@ -87,6 +87,98 @@ void end_swap_bio_read(struct bio *bio, int err) | |||
87 | bio_put(bio); | 87 | bio_put(bio); |
88 | } | 88 | } |
89 | 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 | |||
90 | /* | 182 | /* |
91 | * We may have stale swap cache pages in memory: notice | 183 | * We may have stale swap cache pages in memory: notice |
92 | * them here and get rid of the unnecessary final write. | 184 | * them here and get rid of the unnecessary final write. |
diff --git a/mm/swapfile.c b/mm/swapfile.c index 6ffc87602f4a..7307fc928d7b 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c | |||
@@ -1345,7 +1345,7 @@ static void destroy_swap_extents(struct swap_info_struct *sis) | |||
1345 | * | 1345 | * |
1346 | * This function rather assumes that it is called in ascending page order. | 1346 | * This function rather assumes that it is called in ascending page order. |
1347 | */ | 1347 | */ |
1348 | static int | 1348 | int |
1349 | add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, | 1349 | add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, |
1350 | unsigned long nr_pages, sector_t start_block) | 1350 | unsigned long nr_pages, sector_t start_block) |
1351 | { | 1351 | { |
@@ -1421,106 +1421,25 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) | |||
1421 | struct file *swap_file = sis->swap_file; | 1421 | struct file *swap_file = sis->swap_file; |
1422 | struct address_space *mapping = swap_file->f_mapping; | 1422 | struct address_space *mapping = swap_file->f_mapping; |
1423 | struct inode *inode = mapping->host; | 1423 | struct inode *inode = mapping->host; |
1424 | unsigned blocks_per_page; | ||
1425 | unsigned long page_no; | ||
1426 | unsigned blkbits; | ||
1427 | sector_t probe_block; | ||
1428 | sector_t last_block; | ||
1429 | sector_t lowest_block = -1; | ||
1430 | sector_t highest_block = 0; | ||
1431 | int nr_extents = 0; | ||
1432 | int ret; | 1424 | int ret; |
1433 | 1425 | ||
1434 | if (S_ISBLK(inode->i_mode)) { | 1426 | if (S_ISBLK(inode->i_mode)) { |
1435 | ret = add_swap_extent(sis, 0, sis->max, 0); | 1427 | ret = add_swap_extent(sis, 0, sis->max, 0); |
1436 | *span = sis->pages; | 1428 | *span = sis->pages; |
1437 | goto out; | 1429 | return ret; |
1438 | } | 1430 | } |
1439 | 1431 | ||
1440 | if (mapping->a_ops->swap_activate) { | 1432 | if (mapping->a_ops->swap_activate) { |
1441 | ret = mapping->a_ops->swap_activate(swap_file); | 1433 | ret = mapping->a_ops->swap_activate(sis, swap_file, span); |
1442 | if (!ret) { | 1434 | if (!ret) { |
1443 | sis->flags |= SWP_FILE; | 1435 | sis->flags |= SWP_FILE; |
1444 | ret = add_swap_extent(sis, 0, sis->max, 0); | 1436 | ret = add_swap_extent(sis, 0, sis->max, 0); |
1445 | *span = sis->pages; | 1437 | *span = sis->pages; |
1446 | } | 1438 | } |
1447 | goto out; | 1439 | return ret; |
1448 | } | 1440 | } |
1449 | 1441 | ||
1450 | blkbits = inode->i_blkbits; | 1442 | return generic_swapfile_activate(sis, swap_file, span); |
1451 | blocks_per_page = PAGE_SIZE >> blkbits; | ||
1452 | |||
1453 | /* | ||
1454 | * Map all the blocks into the extent list. This code doesn't try | ||
1455 | * to be very smart. | ||
1456 | */ | ||
1457 | probe_block = 0; | ||
1458 | page_no = 0; | ||
1459 | last_block = i_size_read(inode) >> blkbits; | ||
1460 | while ((probe_block + blocks_per_page) <= last_block && | ||
1461 | page_no < sis->max) { | ||
1462 | unsigned block_in_page; | ||
1463 | sector_t first_block; | ||
1464 | |||
1465 | first_block = bmap(inode, probe_block); | ||
1466 | if (first_block == 0) | ||
1467 | goto bad_bmap; | ||
1468 | |||
1469 | /* | ||
1470 | * It must be PAGE_SIZE aligned on-disk | ||
1471 | */ | ||
1472 | if (first_block & (blocks_per_page - 1)) { | ||
1473 | probe_block++; | ||
1474 | goto reprobe; | ||
1475 | } | ||
1476 | |||
1477 | for (block_in_page = 1; block_in_page < blocks_per_page; | ||
1478 | block_in_page++) { | ||
1479 | sector_t block; | ||
1480 | |||
1481 | block = bmap(inode, probe_block + block_in_page); | ||
1482 | if (block == 0) | ||
1483 | goto bad_bmap; | ||
1484 | if (block != first_block + block_in_page) { | ||
1485 | /* Discontiguity */ | ||
1486 | probe_block++; | ||
1487 | goto reprobe; | ||
1488 | } | ||
1489 | } | ||
1490 | |||
1491 | first_block >>= (PAGE_SHIFT - blkbits); | ||
1492 | if (page_no) { /* exclude the header page */ | ||
1493 | if (first_block < lowest_block) | ||
1494 | lowest_block = first_block; | ||
1495 | if (first_block > highest_block) | ||
1496 | highest_block = first_block; | ||
1497 | } | ||
1498 | |||
1499 | /* | ||
1500 | * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks | ||
1501 | */ | ||
1502 | ret = add_swap_extent(sis, page_no, 1, first_block); | ||
1503 | if (ret < 0) | ||
1504 | goto out; | ||
1505 | nr_extents += ret; | ||
1506 | page_no++; | ||
1507 | probe_block += blocks_per_page; | ||
1508 | reprobe: | ||
1509 | continue; | ||
1510 | } | ||
1511 | ret = nr_extents; | ||
1512 | *span = 1 + highest_block - lowest_block; | ||
1513 | if (page_no == 0) | ||
1514 | page_no = 1; /* force Empty message */ | ||
1515 | sis->max = page_no; | ||
1516 | sis->pages = page_no - 1; | ||
1517 | sis->highest_bit = page_no - 1; | ||
1518 | out: | ||
1519 | return ret; | ||
1520 | bad_bmap: | ||
1521 | printk(KERN_ERR "swapon: swapfile has holes\n"); | ||
1522 | ret = -EINVAL; | ||
1523 | goto out; | ||
1524 | } | 1443 | } |
1525 | 1444 | ||
1526 | static void enable_swap_info(struct swap_info_struct *p, int prio, | 1445 | static void enable_swap_info(struct swap_info_struct *p, int prio, |