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 34f02923744..78eee32ee48 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -17,6 +17,7 @@  #include <linux/swap.h>  #include <linux/bio.h>  #include <linux/swapops.h> +#include <linux/buffer_head.h>  #include <linux/writeback.h>  #include <linux/frontswap.h>  #include <asm/pgtable.h> @@ -86,6 +87,98 @@ void end_swap_bio_read(struct bio *bio, int err)  	bio_put(bio);  } +int generic_swapfile_activate(struct swap_info_struct *sis, +				struct file *swap_file, +				sector_t *span) +{ +	struct address_space *mapping = swap_file->f_mapping; +	struct inode *inode = mapping->host; +	unsigned blocks_per_page; +	unsigned long page_no; +	unsigned blkbits; +	sector_t probe_block; +	sector_t last_block; +	sector_t lowest_block = -1; +	sector_t highest_block = 0; +	int nr_extents = 0; +	int ret; + +	blkbits = inode->i_blkbits; +	blocks_per_page = PAGE_SIZE >> blkbits; + +	/* +	 * Map all the blocks into the extent list.  This code doesn't try +	 * to be very smart. +	 */ +	probe_block = 0; +	page_no = 0; +	last_block = i_size_read(inode) >> blkbits; +	while ((probe_block + blocks_per_page) <= last_block && +			page_no < sis->max) { +		unsigned block_in_page; +		sector_t first_block; + +		first_block = bmap(inode, probe_block); +		if (first_block == 0) +			goto bad_bmap; + +		/* +		 * It must be PAGE_SIZE aligned on-disk +		 */ +		if (first_block & (blocks_per_page - 1)) { +			probe_block++; +			goto reprobe; +		} + +		for (block_in_page = 1; block_in_page < blocks_per_page; +					block_in_page++) { +			sector_t block; + +			block = bmap(inode, probe_block + block_in_page); +			if (block == 0) +				goto bad_bmap; +			if (block != first_block + block_in_page) { +				/* Discontiguity */ +				probe_block++; +				goto reprobe; +			} +		} + +		first_block >>= (PAGE_SHIFT - blkbits); +		if (page_no) {	/* exclude the header page */ +			if (first_block < lowest_block) +				lowest_block = first_block; +			if (first_block > highest_block) +				highest_block = first_block; +		} + +		/* +		 * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks +		 */ +		ret = add_swap_extent(sis, page_no, 1, first_block); +		if (ret < 0) +			goto out; +		nr_extents += ret; +		page_no++; +		probe_block += blocks_per_page; +reprobe: +		continue; +	} +	ret = nr_extents; +	*span = 1 + highest_block - lowest_block; +	if (page_no == 0) +		page_no = 1;	/* force Empty message */ +	sis->max = page_no; +	sis->pages = page_no - 1; +	sis->highest_bit = page_no - 1; +out: +	return ret; +bad_bmap: +	printk(KERN_ERR "swapon: swapfile has holes\n"); +	ret = -EINVAL; +	goto out; +} +  /*   * We may have stale swap cache pages in memory: notice   * them here and get rid of the unnecessary final write. @@ -94,6 +187,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)  {  	struct bio *bio;  	int ret = 0, rw = WRITE; +	struct swap_info_struct *sis = page_swap_info(page);  	if (try_to_free_swap(page)) {  		unlock_page(page); @@ -105,6 +199,33 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)  		end_page_writeback(page);  		goto out;  	} + +	if (sis->flags & SWP_FILE) { +		struct kiocb kiocb; +		struct file *swap_file = sis->swap_file; +		struct address_space *mapping = swap_file->f_mapping; +		struct iovec iov = { +			.iov_base = kmap(page), +			.iov_len  = PAGE_SIZE, +		}; + +		init_sync_kiocb(&kiocb, swap_file); +		kiocb.ki_pos = page_file_offset(page); +		kiocb.ki_left = PAGE_SIZE; +		kiocb.ki_nbytes = PAGE_SIZE; + +		unlock_page(page); +		ret = mapping->a_ops->direct_IO(KERNEL_WRITE, +						&kiocb, &iov, +						kiocb.ki_pos, 1); +		kunmap(page); +		if (ret == PAGE_SIZE) { +			count_vm_event(PSWPOUT); +			ret = 0; +		} +		return ret; +	} +  	bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write);  	if (bio == NULL) {  		set_page_dirty(page); @@ -126,6 +247,7 @@ int swap_readpage(struct page *page)  {  	struct bio *bio;  	int ret = 0; +	struct swap_info_struct *sis = page_swap_info(page);  	VM_BUG_ON(!PageLocked(page));  	VM_BUG_ON(PageUptodate(page)); @@ -134,6 +256,17 @@ int swap_readpage(struct page *page)  		unlock_page(page);  		goto out;  	} + +	if (sis->flags & SWP_FILE) { +		struct file *swap_file = sis->swap_file; +		struct address_space *mapping = swap_file->f_mapping; + +		ret = mapping->a_ops->readpage(swap_file, page); +		if (!ret) +			count_vm_event(PSWPIN); +		return ret; +	} +  	bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read);  	if (bio == NULL) {  		unlock_page(page); @@ -145,3 +278,15 @@ int swap_readpage(struct page *page)  out:  	return ret;  } + +int swap_set_page_dirty(struct page *page) +{ +	struct swap_info_struct *sis = page_swap_info(page); + +	if (sis->flags & SWP_FILE) { +		struct address_space *mapping = sis->swap_file->f_mapping; +		return mapping->a_ops->set_page_dirty(page); +	} else { +		return __set_page_dirty_no_writeback(page); +	} +}  |